Browse Source

implemented /bookmark/<ID>/view

featue/module_registry
Stephan Richter 3 months ago
parent
commit
783eaf3303
  1. 15
      bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java
  2. 6
      bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkDb.java
  3. 22
      bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/SqliteDb.java
  4. 3
      core/src/main/java/de/srsoftware/umbrella/core/model/Bookmark.java
  5. 2
      frontend/src/App.svelte
  6. 12
      frontend/src/routes/bookmark/Index.svelte
  7. 18
      frontend/src/routes/bookmark/Template.svelte
  8. 32
      frontend/src/routes/bookmark/View.svelte

15
bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java

@ -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);

6
bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkDb.java

@ -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);
Bookmark load(long id, long userId);
Map<Long, Bookmark> list(long id); Bookmark save(String url, String comment, long userId);
} }

22
bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/SqliteDb.java

@ -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();

3
core/src/main/java/de/srsoftware/umbrella/core/model/Bookmark.java

@ -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)
); );

2
frontend/src/App.svelte

@ -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} />

12
frontend/src/routes/bookmark/Index.svelte

@ -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>

18
frontend/src/routes/bookmark/Template.svelte

@ -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}

32
frontend/src/routes/bookmark/View.svelte

@ -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} />
Loading…
Cancel
Save