implemented /bookmark/<ID>/view

This commit is contained in:
2025-08-03 15:28:49 +02:00
parent b041e4e9be
commit 783eaf3303
8 changed files with 89 additions and 21 deletions

View File

@@ -19,11 +19,10 @@ import de.srsoftware.umbrella.core.api.UserService;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.Token;
import de.srsoftware.umbrella.core.model.UmbrellaUser; import de.srsoftware.umbrella.core.model.UmbrellaUser;
import org.json.JSONArray;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.json.JSONArray;
public class BookmarkApi extends BaseHandler { public class BookmarkApi extends BaseHandler {
private final BookmarkDb db; private final BookmarkDb db;
@@ -47,7 +46,11 @@ public class BookmarkApi extends BaseHandler {
var head = path.pop(); var head = path.pop();
return switch (head) { return switch (head) {
case LIST -> getUserBookmarks(user.get(),ex); case LIST -> getUserBookmarks(user.get(),ex);
case null, default -> super.doPost(path,ex); case null -> super.doPost(path,ex);
default -> {
var id = Long.parseLong(head);
yield getBookmark(user.get(),id,ex);
}
}; };
} catch (NumberFormatException e){ } catch (NumberFormatException e){
return sendContent(ex,HTTP_BAD_REQUEST,"Invalid project id"); return sendContent(ex,HTTP_BAD_REQUEST,"Invalid project id");
@@ -56,6 +59,12 @@ public class BookmarkApi extends BaseHandler {
} }
} }
private boolean getBookmark(UmbrellaUser user, long id, HttpExchange ex) throws IOException {
var bookmark = db.load(id,user.id());
tags.getTags(BOOKMARK, id, user).forEach(bookmark.tags()::add);
return sendContent(ex,bookmark);
}
@Override @Override
public boolean doPost(Path path, HttpExchange ex) throws IOException { public boolean doPost(Path path, HttpExchange ex) throws IOException {
addCors(ex); addCors(ex);

View File

@@ -5,7 +5,9 @@ import de.srsoftware.umbrella.core.model.Bookmark;
import java.util.Map; import java.util.Map;
public interface BookmarkDb { public interface BookmarkDb {
Bookmark save(String url, String comment, long userId); Map<Long, Bookmark> list(long userId);
Map<Long, Bookmark> list(long id); Bookmark load(long id, long userId);
Bookmark save(String url, String comment, long userId);
} }

View File

@@ -1,22 +1,20 @@
/* © SRSoftware 2025 */ /* © SRSoftware 2025 */
package de.srsoftware.umbrella.bookmarks; package de.srsoftware.umbrella.bookmarks;
import static de.srsoftware.tools.jdbc.Condition.equal;
import static de.srsoftware.tools.jdbc.Query.*; import static de.srsoftware.tools.jdbc.Query.*;
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
import static de.srsoftware.umbrella.bookmarks.Constants.*; import static de.srsoftware.umbrella.bookmarks.Constants.*;
import static de.srsoftware.umbrella.core.Constants.*; import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.Constants.ERROR_FAILED_CREATE_TABLE; import static de.srsoftware.umbrella.core.Constants.ERROR_FAILED_CREATE_TABLE;
import static de.srsoftware.umbrella.core.Util.sha1;
import static java.lang.System.Logger.Level.ERROR; import static java.lang.System.Logger.Level.ERROR;
import static java.text.MessageFormat.format; import static java.text.MessageFormat.format;
import static java.time.ZoneOffset.UTC; import static java.time.ZoneOffset.UTC;
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.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Bookmark; import de.srsoftware.umbrella.core.model.Bookmark;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashMap; import java.util.HashMap;
@@ -73,7 +71,7 @@ CREATE TABLE IF NOT EXISTS {0} (
public Map<Long, Bookmark> list(long userId) { public Map<Long, Bookmark> list(long userId) {
try { try {
var map = new HashMap<Long,Bookmark>(); var map = new HashMap<Long,Bookmark>();
var rs = select(ALL).from(TABLE_URL_COMMENTS).leftJoin(URL_ID,TABLE_URLS,ID).where(USER_ID, Condition.equal(userId)).exec(db); var rs = select(ALL).from(TABLE_URL_COMMENTS).leftJoin(URL_ID,TABLE_URLS,ID).where(USER_ID, equal(userId)).exec(db);
while (rs.next()){ while (rs.next()){
var bookmark = Bookmark.of(rs); var bookmark = Bookmark.of(rs);
map.put(bookmark.id(),bookmark); map.put(bookmark.id(),bookmark);
@@ -85,11 +83,25 @@ CREATE TABLE IF NOT EXISTS {0} (
} }
} }
@Override
public Bookmark load(long id, long userId) {
try {
Bookmark result = null;
var rs = select(ALL).from(TABLE_URLS).leftJoin(ID,TABLE_URL_COMMENTS,URL_ID).where(ID,equal(id)).where(USER_ID,equal(userId)).exec(db);
if (rs.next()) result = Bookmark.of(rs);
rs.close();
if (result != null) return result;
throw UmbrellaException.notFound("No bookmark with id {0}",id);
} catch (SQLException e) {
throw new UmbrellaException("Failed to load bookmark");
}
}
@Override @Override
public Bookmark save(String url, String comment, long userId) { public Bookmark save(String url, String comment, long userId) {
try { try {
var timestamp = LocalDateTime.now(); var timestamp = LocalDateTime.now();
var rs = select(ID).from(TABLE_URLS).where(URL,Condition.equal(url)).exec(db); var rs = select(ID).from(TABLE_URLS).where(URL, equal(url)).exec(db);
var id = 0L; var id = 0L;
if (rs.next()) id = rs.getLong(ID); if (rs.next()) id = rs.getLong(ID);
rs.close(); rs.close();

View File

@@ -2,6 +2,7 @@
package de.srsoftware.umbrella.core.model; package de.srsoftware.umbrella.core.model;
import static de.srsoftware.umbrella.core.Constants.*; import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.Util.markdown;
import static java.time.ZoneOffset.UTC; import static java.time.ZoneOffset.UTC;
import de.srsoftware.tools.Mappable; import de.srsoftware.tools.Mappable;
@@ -27,7 +28,7 @@ public record Bookmark(long id, String url, String comment, LocalDateTime timest
return Map.of( return Map.of(
ID,id, ID,id,
URL, url, URL, url,
COMMENT, comment, COMMENT, Map.of(SOURCE,comment,RENDERED,markdown(comment)),
TAGS, tags, TAGS, tags,
TIMESTAMP, timestamp.withNano(0) TIMESTAMP, timestamp.withNano(0)
); );

View File

@@ -7,6 +7,7 @@
import AddDoc from "./routes/document/Add.svelte"; import AddDoc from "./routes/document/Add.svelte";
import AddTask from "./routes/task/Add.svelte"; import AddTask from "./routes/task/Add.svelte";
import Bookmark from "./routes/bookmark/View.svelte";
import Bookmarks from "./routes/bookmark/Index.svelte"; import Bookmarks from "./routes/bookmark/Index.svelte";
import Callback from "./routes/user/OidcCallback.svelte"; import Callback from "./routes/user/OidcCallback.svelte";
import DocList from "./routes/document/List.svelte"; import DocList from "./routes/document/List.svelte";
@@ -52,6 +53,7 @@
<Menu /> <Menu />
<Route path="/" component={User} /> <Route path="/" component={User} />
<Route path="/bookmark" component={Bookmarks} /> <Route path="/bookmark" component={Bookmarks} />
<Route path="/bookmark/:id/view" component={Bookmark} />
<Route path="/document" component={DocList} /> <Route path="/document" component={DocList} />
<Route path="/document/add" component={AddDoc} /> <Route path="/document/add" component={AddDoc} />
<Route path="/document/:id/send" component={SendDoc} /> <Route path="/document/:id/send" component={SendDoc} />

View File

@@ -6,6 +6,7 @@
import Editor from '../../Components/MarkdownEditor.svelte'; import Editor from '../../Components/MarkdownEditor.svelte';
import Tags from '../tags/TagList.svelte'; import Tags from '../tags/TagList.svelte';
import Template from './Template.svelte';
let bookmarks = $state(null); let bookmarks = $state(null);
let new_bookmark = $state({ let new_bookmark = $state({
@@ -75,16 +76,7 @@
<button {onclick}>{t('save')}</button> <button {onclick}>{t('save')}</button>
{#if bookmarks} {#if bookmarks}
{#each bookmarks as bookmark} {#each bookmarks as bookmark}
<fieldset class="bookmark"> <Template {bookmark} />
<legend>
<a href={bookmark.url} target="_blank" class="url">{bookmark.url}</a>
</legend>
<legend class="date">
{bookmark.timestamp.replace('T',' ')}
</legend>
{bookmark.comment}
<Tags module="bookmark" id={bookmark.id} />
</fieldset>
{/each} {/each}
{/if} {/if}
</fieldset> </fieldset>

View File

@@ -0,0 +1,18 @@
<script>
import Tags from '../tags/TagList.svelte';
let { bookmark } = $props();
</script>
{#if bookmark}
<fieldset class="bookmark">
<legend>
<a href={bookmark.url} target="_blank" class="url">{bookmark.url}</a>
</legend>
<legend class="date">
{bookmark.timestamp.replace('T',' ')}
</legend>
{@html bookmark.comment.rendered}
<Tags module="bookmark" id={bookmark.id} />
</fieldset>
{/if}

View File

@@ -0,0 +1,32 @@
<script>
import { onMount } from 'svelte';
import Bookmark from './Template.svelte';
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
import Editor from '../../Components/MarkdownEditor.svelte';
import Template from './Template.svelte';
let bookmark = $state(null);
let error = $state(null);
let { id } = $props();
async function load(){
const url = api(`bookmark/${id}`);
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
bookmark = await resp.json();
} else {
error = await resp.text();
}
}
onMount(load);
</script>
{#if error}
<span class="error">{error}</span>
{/if}
<Template {bookmark} />