Browse Source

implemented storing edit as new version, impelemented loading of arbitrary versions of wiki page

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
module/wiki
Stephan Richter 2 months ago
parent
commit
aad9a7d9b8
  1. 56
      frontend/src/routes/wiki/View.svelte
  2. 1
      translations/src/main/resources/de.json
  3. 16
      web/src/main/resources/web/css/default.css
  4. 12
      wiki/src/main/java/de/srsoftware/umbrella/wiki/SqliteDb.java
  5. 2
      wiki/src/main/java/de/srsoftware/umbrella/wiki/WikiDb.java
  6. 24
      wiki/src/main/java/de/srsoftware/umbrella/wiki/WikiModule.java

56
frontend/src/routes/wiki/View.svelte

@ -1,25 +1,43 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { api } from '../../urls.svelte'; import { useTinyRouter } from 'svelte-tiny-router';
import { t } from '../../translations.svelte'; import { api } from '../../urls.svelte';
import { t } from '../../translations.svelte';
import Editor from '../../Components/MarkdownEditor.svelte'; import Editor from '../../Components/MarkdownEditor.svelte';
let error = $state(null); let error = $state(null);
let { id, version } = $props(); let { id, version } = $props();
let page = $state(null); let page = $state(null);
let router = useTinyRouter();
async function loadPage(){ async function loadContent(res){
const url = api(`wiki/page/${id}`);
const res = await fetch(url,{credentials:'include'});
if (res.ok){ if (res.ok){
page = null;
page = await res.json(); page = await res.json();
page.versions.sort((a,b)=>b-a);
version = page.version;
error = null; error = null;
} else { } else {
error = await res.text(); error = await res.text();
} }
} }
async function loadPage(){
let path = `wiki/page/${id}`;
if (version) path += `/version/${version}`;
const url = api(path);
const res = await fetch(url,{credentials:'include'});
loadContent(res);
}
function onclick(e){
e.preventDefault();
let href = e.target.getAttribute('href');
if (href) router.navigate(href);
return false;
}
async function patch(data){ async function patch(data){
const url = api(`wiki/page/${id}`); const url = api(`wiki/page/${id}`);
const res = await fetch(url,{ const res = await fetch(url,{
@ -27,31 +45,27 @@
method:'PATCH', method:'PATCH',
body:JSON.stringify(data) body:JSON.stringify(data)
}); });
if (res.ok){ loadContent(res);
error = null;
page = await res.json();
return true;
} else {
error = await res.text();
return false;
}
} }
onMount(loadPage); $effect(loadPage);
</script> </script>
<h2>{id}</h2>
{#if error} {#if error}
<span class="error">{error}</span> <span class="error">{error}</span>
{/if} {/if}
{#if page} {#if page}
<span class="version">{t('version')}</span> <span class="version">{t('version')}</span>
{#each Array.from({ length: page.version }, (_, i) => page.version - i) as v} {#each page.versions as v}
<span class="version"> <span class="version">
{#if v == page.version}[{/if} <a href={`/wiki/${id}/version/${v}`} {onclick} class={page.version == v?'selected':''}>{v}</a>
<a href={`/wiki/${id}/version/${v}`}>{v}</a>
{#if v == page.version}]{/if}
&nbsp; &nbsp;
</span> </span>
{/each} {/each}
{/if}
<h2>{id}</h2>
{#if page}
{#if page.version != page.versions[0]}
<span class="warn">{t('not_recent_version')}</span>
{/if}
<Editor editable={true} value={page.content} onSet={s => patch({content:s})}></Editor> <Editor editable={true} value={page.content} onSet={s => patch({content:s})}></Editor>
{/if} {/if}

1
translations/src/main/resources/de.json

@ -156,6 +156,7 @@
"no_company": "keine Firma", "no_company": "keine Firma",
"note": "Notiz", "note": "Notiz",
"notes": "Notizen", "notes": "Notizen",
"not_recent_version": "Die ist nicht die neuste Version dieser Seite!",
"number": "Nummer", "number": "Nummer",
"oidc_Login" : "Anmeldung mit OIDC", "oidc_Login" : "Anmeldung mit OIDC",

16
web/src/main/resources/web/css/default.css

@ -69,6 +69,13 @@ nav {
border-radius: 6px; border-radius: 6px;
} }
.warn {
background: yellow;
color: black;
padding: 5px;
border-radius: 6px;
}
fieldset[tabindex="0"]{ fieldset[tabindex="0"]{
max-height: 55px; max-height: 55px;
overflow: hidden; overflow: hidden;
@ -369,4 +376,13 @@ table{
position: absolute; position: absolute;
right: 0; right: 0;
bottom: 0; bottom: 0;
}
.version > a{
padding: 5px;
}
.version a.selected{
border: 1px solid orange;
border-radius: 5px;
} }

12
wiki/src/main/java/de/srsoftware/umbrella/wiki/SqliteDb.java

@ -2,8 +2,6 @@ package de.srsoftware.umbrella.wiki;
import de.srsoftware.tools.jdbc.Condition; import de.srsoftware.tools.jdbc.Condition;
import de.srsoftware.umbrella.core.BaseDb; import de.srsoftware.umbrella.core.BaseDb;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Hash;
import de.srsoftware.umbrella.core.model.Permission; import de.srsoftware.umbrella.core.model.Permission;
import de.srsoftware.umbrella.core.model.WikiPage; import de.srsoftware.umbrella.core.model.WikiPage;
@ -80,9 +78,15 @@ public class SqliteDb extends BaseDb implements WikiDb {
} }
@Override @Override
public WikiPage load(String id) { public WikiPage load(String id, Integer version) {
try { try {
var rs = select(ALL).from(TABLE_PAGES).where(ID,Condition.equal(id)).sort(VERSION+" DESC").limit(1).exec(db); var query = select(ALL).from(TABLE_PAGES).where(ID,Condition.equal(id));
if (version == null) {
query.sort(VERSION+" DESC").limit(1);
} else {
query.where(VERSION,Condition.equal(version));
}
var rs = query.exec(db);
WikiPage page = null; WikiPage page = null;
if (rs.next()) page = WikiPage.of(rs); if (rs.next()) page = WikiPage.of(rs);
rs.close(); rs.close();

2
wiki/src/main/java/de/srsoftware/umbrella/wiki/WikiDb.java

@ -12,7 +12,7 @@ public interface WikiDb {
List<String> listUserPages(long userId); List<String> listUserPages(long userId);
WikiPage load(String id); WikiPage load(String id, Integer version);
Map<Long, Permission> loadMembers(WikiPage page); Map<Long, Permission> loadMembers(WikiPage page);

24
wiki/src/main/java/de/srsoftware/umbrella/wiki/WikiModule.java

@ -17,11 +17,9 @@ import java.io.IOException;
import java.util.Optional; import java.util.Optional;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect; import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.Constants.VERSION;
import static de.srsoftware.umbrella.core.Paths.PAGE; import static de.srsoftware.umbrella.core.Paths.PAGE;
import static de.srsoftware.umbrella.core.Paths.VIEW; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.core.Util.mapValues;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
import static de.srsoftware.umbrella.core.model.Permission.EDIT; import static de.srsoftware.umbrella.core.model.Permission.EDIT;
import static de.srsoftware.umbrella.wiki.Constants.*; import static de.srsoftware.umbrella.wiki.Constants.*;
@ -73,7 +71,17 @@ public class WikiModule extends BaseHandler implements WikiService {
private boolean getPage(Path path, UmbrellaUser user, HttpExchange ex) throws IOException { private boolean getPage(Path path, UmbrellaUser user, HttpExchange ex) throws IOException {
var id = path.pop(); var id = path.pop();
var page = loadPage(id); Integer version = null;
if (!path.empty()){
var head = path.pop();
if (!VERSION.equals(head)) return doGet(path,ex);
try {
version = Integer.parseInt(path.pop());
} catch (NumberFormatException e) {
throw invalidFieldException(VERSION,"int number");
}
}
var page = loadPage(id,version);
var permission = page.members().get(user.id()); var permission = page.members().get(user.id());
if (permission == null) throw forbidden("You are not allowed to access \"{0}\"!",id); if (permission == null) throw forbidden("You are not allowed to access \"{0}\"!",id);
return sendContent(ex, page); return sendContent(ex, page);
@ -97,14 +105,14 @@ public class WikiModule extends BaseHandler implements WikiService {
return page; return page;
} }
private WikiPage loadPage(String id){ private WikiPage loadPage(String id, Integer version){
if (id == null) throw missingFieldException(PAGE_ID); if (id == null) throw missingFieldException(PAGE_ID);
return loadMembers(wikiDb.load(id)); return loadMembers(wikiDb.load(id, version));
} }
private boolean patchPage(Path path, UmbrellaUser user, HttpExchange ex) throws IOException { private boolean patchPage(Path path, UmbrellaUser user, HttpExchange ex) throws IOException {
var id = path.pop(); var id = path.pop();
var page = loadPage(id); var page = loadPage(id, null);
var member = page.members().get(user.id()); var member = page.members().get(user.id());
if (member == null || member.permission() != EDIT) throw forbidden("You are not allowed to edit {0}!",id); if (member == null || member.permission() != EDIT) throw forbidden("You are not allowed to edit {0}!",id);
var json = json(ex); var json = json(ex);

Loading…
Cancel
Save