fix(acl): dölj redigeringsknappen för användare utan edit-rättighet
Some checks failed
build-and-push / build (push) Failing after 1m3s
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>
This commit is contained in:
@@ -1,14 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
||||||
import { EditorView, basicSetup } from 'codemirror'
|
import { EditorView, basicSetup } from 'codemirror'
|
||||||
import { EditorState } from '@codemirror/state'
|
import { EditorState, Compartment } from '@codemirror/state'
|
||||||
import { markdown } from '@codemirror/lang-markdown'
|
import { markdown } from '@codemirror/lang-markdown'
|
||||||
import { oneDark } from '@codemirror/theme-one-dark'
|
import { oneDark } from '@codemirror/theme-one-dark'
|
||||||
|
|
||||||
const model = defineModel<string>({ required: true })
|
const model = defineModel<string>({ required: true })
|
||||||
|
const props = withDefaults(defineProps<{ readonly?: boolean }>(), { readonly: false })
|
||||||
const container = ref<HTMLElement | null>(null)
|
const container = ref<HTMLElement | null>(null)
|
||||||
let view: EditorView | null = null
|
let view: EditorView | null = null
|
||||||
|
|
||||||
|
const readonlyCompartment = new Compartment()
|
||||||
|
const readonlyExtensions = (ro: boolean) => [EditorState.readOnly.of(ro), EditorView.editable.of(!ro)]
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!container.value) return
|
if (!container.value) return
|
||||||
|
|
||||||
@@ -20,6 +24,7 @@ onMounted(() => {
|
|||||||
basicSetup,
|
basicSetup,
|
||||||
markdown(),
|
markdown(),
|
||||||
oneDark,
|
oneDark,
|
||||||
|
readonlyCompartment.of(readonlyExtensions(props.readonly)),
|
||||||
EditorView.updateListener.of((update) => {
|
EditorView.updateListener.of((update) => {
|
||||||
if (update.docChanged) {
|
if (update.docChanged) {
|
||||||
model.value = update.state.doc.toString()
|
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.
|
// Sync external model changes into CodeMirror.
|
||||||
watch(model, (adoc) => {
|
watch(model, (adoc) => {
|
||||||
if (!view) return
|
if (!view) return
|
||||||
|
|||||||
@@ -14,9 +14,15 @@ import ImageEditDialog from './ImageEditDialog.vue'
|
|||||||
import LinkDialog from './LinkDialog.vue'
|
import LinkDialog from './LinkDialog.vue'
|
||||||
|
|
||||||
const model = defineModel<string>({ required: true })
|
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)
|
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 showImagePicker = ref(false)
|
||||||
const showImageEditor = ref(false)
|
const showImageEditor = ref(false)
|
||||||
const showLinkDialog = ref({ open: false, url: '', text: '' })
|
const showLinkDialog = ref({ open: false, url: '', text: '' })
|
||||||
@@ -473,9 +479,9 @@ function handleEditorClick(e: MouseEvent) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Banner when not editing -->
|
<!-- Banner when not editing (hidden entirely without edit permission) -->
|
||||||
<div
|
<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"
|
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>
|
<span class="text-sm text-indigo-700 dark:text-indigo-300 ml-2">Läsläge</span>
|
||||||
|
|||||||
@@ -357,11 +357,13 @@ function formatDate(dateStr: string) {
|
|||||||
<VisualEditor
|
<VisualEditor
|
||||||
v-else-if="versionEditorMode === 'visual'"
|
v-else-if="versionEditorMode === 'visual'"
|
||||||
:slug="slug"
|
:slug="slug"
|
||||||
|
:editable="false"
|
||||||
:model-value="versionContent"
|
:model-value="versionContent"
|
||||||
@update:model-value="() => {}"
|
@update:model-value="() => {}"
|
||||||
/>
|
/>
|
||||||
<SourceEditor
|
<SourceEditor
|
||||||
v-else
|
v-else
|
||||||
|
readonly
|
||||||
:model-value="versionContent"
|
:model-value="versionContent"
|
||||||
@update:model-value="() => {}"
|
@update:model-value="() => {}"
|
||||||
/>
|
/>
|
||||||
@@ -370,8 +372,8 @@ function formatDate(dateStr: string) {
|
|||||||
<!-- Normal editor -->
|
<!-- Normal editor -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div v-if="loading" class="p-6 text-slate-400 animate-pulse">Loading…</div>
|
<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" />
|
<VisualEditor v-else-if="editorMode === 'visual'" v-model="content" :slug="slug" :editable="canEditDoc" />
|
||||||
<SourceEditor v-else v-model="content" />
|
<SourceEditor v-else v-model="content" :readonly="!canEditDoc" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user