implemented adding new pages
This commit is contained in:
@@ -34,6 +34,7 @@ public class BookmarkApi extends BaseHandler implements BookmarkService {
|
||||
super();
|
||||
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE));
|
||||
db = new SqliteDb(connect(dbFile));
|
||||
ModuleRegistry.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,6 +5,7 @@ public class Paths {
|
||||
private Paths(){};
|
||||
|
||||
public static final String ADD = "add";
|
||||
public static final String AVAILABLE = "available";
|
||||
public static final String CSS = "css";
|
||||
public static final String COMMON_TEMPLATES = "common_templates";
|
||||
public static final String JSON = "json";
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.json.JSONObject;
|
||||
|
||||
public class WikiPage implements Mappable {
|
||||
|
||||
private long id;
|
||||
private final long id;
|
||||
private String title;
|
||||
private int version;
|
||||
private final List<Integer> versions = new ArrayList<>();
|
||||
@@ -50,11 +50,6 @@ public class WikiPage implements Mappable {
|
||||
return id;
|
||||
}
|
||||
|
||||
public WikiPage id(long newVal){
|
||||
id = newVal;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isDirty(String field) {
|
||||
return dirtyFields.contains(field);
|
||||
}
|
||||
@@ -109,6 +104,13 @@ public class WikiPage implements Mappable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public WikiPage setNew(){
|
||||
dirtyFields.add(TITLE);
|
||||
dirtyFields.add(CONTENT);
|
||||
dirtyFields.add(MEMBERS);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String title(){
|
||||
return title;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import Login from "./Components/Login.svelte";
|
||||
import Messages from "./routes/message/Messages.svelte";
|
||||
import Menu from "./Components/Menu.svelte";
|
||||
import NewPage from "./routes/wiki/AddPage.svelte";
|
||||
import Notes from "./routes/notes/Index.svelte";
|
||||
import ProjectList from "./routes/project/List.svelte";
|
||||
import ProjectAdd from "./routes/project/Create.svelte";
|
||||
@@ -90,6 +91,7 @@
|
||||
<Route path="/user/oidc/add" component={EditService} />
|
||||
<Route path="/user/oidc/edit/:serviceName" component={EditService} />
|
||||
<Route path="/wiki" component={WikiIndex} />
|
||||
<Route path="/wiki/add" component={NewPage} />
|
||||
<Route path="/wiki/:key/view" component={WikiPage} />
|
||||
<Route path="/wiki/:key/version/:version" component={WikiPage} />
|
||||
<Route>
|
||||
|
||||
@@ -39,10 +39,7 @@
|
||||
});
|
||||
if (resp.ok){
|
||||
const patched = await resp.json();
|
||||
for (let key of Object.keys(patched)){
|
||||
console.log('patching '+key+'…');
|
||||
company[key] = patched[key];
|
||||
}
|
||||
for (let key of Object.keys(patched)) company[key] = patched[key];
|
||||
return true;
|
||||
}
|
||||
error = await resp.text();
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
|
||||
function onselect(company){
|
||||
project.company_id = company.id;
|
||||
console.log(project);
|
||||
}
|
||||
|
||||
function toggleSettings(ev){
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
function dropMember(member){
|
||||
delete task.members[member.user.id];
|
||||
console.log({drop:member.user.id});
|
||||
/// TODO: ?
|
||||
}
|
||||
|
||||
async function load(){
|
||||
|
||||
66
frontend/src/routes/wiki/AddPage.svelte
Normal file
66
frontend/src/routes/wiki/AddPage.svelte
Normal file
@@ -0,0 +1,66 @@
|
||||
<script>
|
||||
import { useTinyRouter } from 'svelte-tiny-router';
|
||||
import { api } from '../../urls.svelte';
|
||||
import { t } from '../../translations.svelte';
|
||||
|
||||
import Markdown from '../../Components/MarkdownEditor.svelte';
|
||||
|
||||
let content = $state({source:null,rendered:null});
|
||||
let error = $state(null);
|
||||
let router = useTinyRouter();
|
||||
let timer = null;
|
||||
let title = $state(null);
|
||||
|
||||
async function onsubmit(ev){
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const url = api(`wiki/${title}`);
|
||||
const res = await fetch(url,{
|
||||
credentials:'include',
|
||||
method:'POST',
|
||||
body:content.source
|
||||
});
|
||||
if (res.ok){
|
||||
router.navigate(`/wiki/${title}/view`);
|
||||
} else {
|
||||
error = await res.text();
|
||||
}
|
||||
}
|
||||
|
||||
function checkTitle(){
|
||||
if (title) {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(requestTitle,1000);
|
||||
}
|
||||
}
|
||||
|
||||
async function requestTitle(){
|
||||
timer = null;
|
||||
var url = api(`wiki/available/${title}`);
|
||||
var res = await fetch(url,{credentials:'include'});
|
||||
const body = await res.text();
|
||||
if (res.ok){
|
||||
error = body == 'true' ? null : t('title_not_available',{title:title})
|
||||
} else {
|
||||
error = body;
|
||||
if (!error) error = t('failed_to_check_availability');
|
||||
}
|
||||
}
|
||||
|
||||
$effect(checkTitle)
|
||||
</script>
|
||||
|
||||
<h1>{t('create_new_object',{object:t('page')})}</h1>
|
||||
{#if error}
|
||||
<span class="error">{error}</span>
|
||||
{/if}
|
||||
<form {onsubmit}>
|
||||
<label>
|
||||
{t('Name')}
|
||||
<input type="text" bind:value={title} />
|
||||
</label>
|
||||
<label>
|
||||
{t('content')}
|
||||
<Markdown bind:value={content} simple={true} />
|
||||
</label>
|
||||
</form>
|
||||
@@ -33,6 +33,8 @@
|
||||
<span class="error">{error}</span>
|
||||
{/if}
|
||||
<h1>{t('wiki')}</h1>
|
||||
<button onclick={() => router.navigate('/wiki/add')}><span class="symbol"></span> {t('create_new_object',{object:t('page')})}</button>
|
||||
|
||||
{#if pages}
|
||||
{#each pages as page}
|
||||
{#if page.charAt(0).toUpperCase() != lastLetter}
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
"oidc_Login" : "Anmeldung mit OIDC",
|
||||
"old_password": "altes Passwort",
|
||||
|
||||
"page": "Seite",
|
||||
"parent_task": "übergeordnete Aufgabe",
|
||||
"password" : "Passwort",
|
||||
"permission": {
|
||||
@@ -233,6 +234,7 @@
|
||||
"subtasks": "Unteraufgaben",
|
||||
"succeeding_document": "Nachfolge-Dokument",
|
||||
"sum_of_records": "Summe der ausgewählten Einträge",
|
||||
|
||||
"tag_uses": "Verwendung des Tags „{tag}“",
|
||||
"tags": "Tags",
|
||||
"task": "Aufgabe",
|
||||
@@ -244,6 +246,7 @@
|
||||
"theme": "Design",
|
||||
"times": "Zeiten",
|
||||
"timetracking": "Zeiterfassung",
|
||||
"title_not_available": "„{title}“ ist als Seitenname nicht mehr verfügbar!",
|
||||
"title_or_desc": "Titel/Beschreibung",
|
||||
"tutorial": "Tutorial",
|
||||
"type": "Dokumententyp",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* © SRSoftware 2025 */
|
||||
package de.srsoftware.umbrella.wiki;
|
||||
|
||||
import static de.srsoftware.tools.jdbc.Condition.equal;
|
||||
import static de.srsoftware.tools.jdbc.Query.*;
|
||||
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
|
||||
import static de.srsoftware.umbrella.core.Constants.*;
|
||||
@@ -142,10 +143,37 @@ public class SqliteDb extends BaseDb implements WikiDb {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextId() {
|
||||
try {
|
||||
var id = 0L;
|
||||
var rs = select("MAX(ID)").from(TABLE_PAGES).exec(db);
|
||||
if (rs.next()) id = rs.getLong(1);
|
||||
rs.close();
|
||||
return id+1;
|
||||
} catch (SQLException e) {
|
||||
throw databaseException("Failed to query next free page id!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(String title) {
|
||||
if (title==null||title.isBlank())return false;
|
||||
try {
|
||||
var count = 1;
|
||||
var rs = select("COUNT(ID)").from(TABLE_PAGES).where(TITLE,equal(title)).exec(db);
|
||||
if (rs.next()) count = rs.getInt(1);
|
||||
rs.close();
|
||||
return count < 1;
|
||||
} catch (SQLException e) {
|
||||
throw databaseException("Failed to query availability of {0}!",title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> listUserPages(long userId) {
|
||||
try {
|
||||
var rs = select(TITLE,"MAX(version) AS version").from(TABLE_PAGES).leftJoin(ID,TABLE_PAGES_USERS,PAGE_ID).where(USER_ID, Condition.equal(userId)).groupBy(TITLE).sort("TITLE COLLATE NOCASE ASC").exec(db);
|
||||
var rs = select(TITLE,"MAX(version) AS version").from(TABLE_PAGES).leftJoin(ID,TABLE_PAGES_USERS,PAGE_ID).where(USER_ID, equal(userId)).groupBy(TITLE).sort("TITLE COLLATE NOCASE ASC").exec(db);
|
||||
var set = new ArrayList<String>();
|
||||
while (rs.next()) set.add(rs.getString(TITLE));
|
||||
rs.close();
|
||||
@@ -160,11 +188,11 @@ public class SqliteDb extends BaseDb implements WikiDb {
|
||||
WikiPage page = null;
|
||||
try { // Try to load by id
|
||||
long id = Long.parseLong(title);
|
||||
var query = select(ALL).from(TABLE_PAGES).where(ID,Condition.equal(id));
|
||||
var query = select(ALL).from(TABLE_PAGES).where(ID, equal(id));
|
||||
if (version == null) {
|
||||
query.sort(VERSION+" DESC").limit(1);
|
||||
} else {
|
||||
query.where(VERSION,Condition.equal(version));
|
||||
query.where(VERSION, equal(version));
|
||||
}
|
||||
var rs = query.exec(db);
|
||||
if (rs.next()) page = WikiPage.of(rs);
|
||||
@@ -175,11 +203,11 @@ public class SqliteDb extends BaseDb implements WikiDb {
|
||||
throw databaseException("Failed to load wiki page \"{0}\" from database!",title);
|
||||
}
|
||||
if (page == null) try { // page was not loaded by ID
|
||||
var query = select(ALL).from(TABLE_PAGES).where(TITLE,Condition.equal(title));
|
||||
var query = select(ALL).from(TABLE_PAGES).where(TITLE, equal(title));
|
||||
if (version == null) {
|
||||
query.sort(VERSION+" DESC").limit(1);
|
||||
} else {
|
||||
query.where(VERSION,Condition.equal(version));
|
||||
query.where(VERSION, equal(version));
|
||||
}
|
||||
var rs = query.exec(db);
|
||||
if (rs.next()) page = WikiPage.of(rs);
|
||||
@@ -189,7 +217,7 @@ public class SqliteDb extends BaseDb implements WikiDb {
|
||||
}
|
||||
if (page == null) throw notFound("Failed to load wiki page \"{0}\" from database!",title);
|
||||
try {
|
||||
var rs = select(VERSION).from(TABLE_PAGES).where(ID,Condition.equal(page.id())).sort(VERSION).exec(db);
|
||||
var rs = select(VERSION).from(TABLE_PAGES).where(ID, equal(page.id())).sort(VERSION).exec(db);
|
||||
var versions = page.versions();
|
||||
while (rs.next()) versions.add(rs.getInt(VERSION));
|
||||
rs.close();
|
||||
@@ -291,7 +319,7 @@ public class SqliteDb extends BaseDb implements WikiDb {
|
||||
public Map<Long, Permission> loadMembers(WikiPage page) {
|
||||
try {
|
||||
var map = new HashMap<Long, Permission>();
|
||||
var rs = select(ALL).from(TABLE_PAGES_USERS).where(PAGE_ID,Condition.equal(page.id())).exec(db);
|
||||
var rs = select(ALL).from(TABLE_PAGES_USERS).where(PAGE_ID, equal(page.id())).exec(db);
|
||||
while (rs.next()){
|
||||
var permission = wikiPermission(rs.getInt(PERMISSIONS));
|
||||
if (permission != null) map.put(rs.getLong(USER_ID),permission);
|
||||
@@ -309,7 +337,7 @@ public class SqliteDb extends BaseDb implements WikiDb {
|
||||
if (page.isDirty(CONTENT) || page.isDirty(ID) || page.isDirty(TITLE)) insertInto(TABLE_PAGES,ID,VERSION,TITLE,CONTENT)
|
||||
.values(page.id(),page.version(),page.title(),page.content()).execute(db).close();
|
||||
if (page.isDirty(MEMBERS)){
|
||||
Query.delete().from(TABLE_PAGES_USERS).where(PAGE_ID,Condition.equal(page.title())).where(USER_ID,Condition.notIn(page.members().keySet().toArray())).execute(db);
|
||||
Query.delete().from(TABLE_PAGES_USERS).where(PAGE_ID, equal(page.title())).where(USER_ID,Condition.notIn(page.members().keySet().toArray())).execute(db);
|
||||
var query = replaceInto(TABLE_PAGES_USERS,PAGE_ID,USER_ID,PERMISSIONS);
|
||||
for (var member : page.members().entrySet()) query.values(page.id(),member.getKey(),wikiPermissionCode(member.getValue().permission()));
|
||||
query.execute(db).close();
|
||||
|
||||
@@ -9,6 +9,10 @@ import java.util.Map;
|
||||
|
||||
public interface WikiDb {
|
||||
|
||||
long getNextId();
|
||||
|
||||
boolean isAvailable(String title);
|
||||
|
||||
List<String> listUserPages(long userId);
|
||||
|
||||
WikiPage load(String id, Integer version);
|
||||
|
||||
@@ -4,6 +4,7 @@ package de.srsoftware.umbrella.wiki;
|
||||
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
|
||||
import static de.srsoftware.umbrella.core.Constants.VERSION;
|
||||
import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
|
||||
import static de.srsoftware.umbrella.core.Paths.AVAILABLE;
|
||||
import static de.srsoftware.umbrella.core.Paths.PAGE;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
|
||||
import static de.srsoftware.umbrella.core.model.Permission.EDIT;
|
||||
@@ -43,6 +44,7 @@ public class WikiModule extends BaseHandler implements WikiService {
|
||||
var head = path.pop();
|
||||
return switch (head) {
|
||||
case null -> getUserPages(user.get(),ex);
|
||||
case AVAILABLE -> getAvailability(path,ex);
|
||||
case PAGE -> getPage(path, user.get(), ex);
|
||||
default -> super.doGet(path,ex);
|
||||
};
|
||||
@@ -69,6 +71,25 @@ public class WikiModule extends BaseHandler implements WikiService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPost(Path path, HttpExchange ex) throws IOException {
|
||||
addCors(ex);
|
||||
try {
|
||||
Optional<Token> token = SessionToken.from(ex).map(Token::of);
|
||||
var user = userService().loadUser(token);
|
||||
if (user.isEmpty()) return unauthorized(ex);
|
||||
var title = path.pop();
|
||||
if (!path.empty()) return super.doPost(path,ex);
|
||||
return postNewPage(title,user.get(),ex);
|
||||
} catch (UmbrellaException e){
|
||||
return send(ex,e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getAvailability(Path path, HttpExchange ex) throws IOException {
|
||||
return sendContent(ex,wikiDb.isAvailable(path.pop()));
|
||||
}
|
||||
|
||||
private boolean getPage(Path path, UmbrellaUser user, HttpExchange ex) throws IOException {
|
||||
var id = path.pop();
|
||||
Integer version = null;
|
||||
@@ -118,4 +139,11 @@ public class WikiModule extends BaseHandler implements WikiService {
|
||||
var json = json(ex);
|
||||
return sendContent(ex,wikiDb.save(page.patch(json, userService())));
|
||||
}
|
||||
|
||||
private boolean postNewPage(String title, UmbrellaUser user, HttpExchange ex) throws IOException {
|
||||
var content = body(ex);
|
||||
var page = new WikiPage(wikiDb.getNextId(),title,1,content);
|
||||
page.members().put(user.id(),new Member(user,EDIT));
|
||||
return sendContent(ex,wikiDb.save(page.setNew()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user