diff --git a/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java b/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java index f31033e..311d58f 100644 --- a/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java +++ b/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java @@ -5,6 +5,7 @@ import static de.srsoftware.umbrella.bookmarks.Constants.*; import static de.srsoftware.umbrella.core.ConnectionProvider.connect; import static de.srsoftware.umbrella.core.Constants.*; import static de.srsoftware.umbrella.core.Paths.LIST; +import static de.srsoftware.umbrella.core.Paths.SEARCH; import static de.srsoftware.umbrella.core.Util.mapValues; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; @@ -21,6 +22,7 @@ import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Optional; import org.json.JSONArray; @@ -71,6 +73,7 @@ public class BookmarkApi extends BaseHandler implements BookmarkService { if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { + case SEARCH -> postSearch(user.get(),ex); case null -> postBookmark(user.get(),ex); default -> super.doPost(path,ex); }; @@ -118,4 +121,13 @@ public class BookmarkApi extends BaseHandler implements BookmarkService { } return sendContent(ex,bookmark); } + + private boolean postSearch(UmbrellaUser user, HttpExchange ex) throws IOException { + var json = json(ex); + if (!(json.has(KEY) && json.get(KEY) instanceof String key)) throw missingFieldException(KEY); + var keys = Arrays.asList(key.split(" ")); + var fulltext = json.has(FULLTEXT) && json.get(FULLTEXT) instanceof Boolean val && val; + var bookmarks = db.find(user.id(),keys); + return sendContent(ex,mapValues(bookmarks)); + } } diff --git a/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkDb.java b/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkDb.java index 1a0f29d..b6d331a 100644 --- a/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkDb.java +++ b/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkDb.java @@ -7,7 +7,9 @@ import java.util.Collection; import java.util.Map; public interface BookmarkDb { - Map list(long userId, long offset, long limit); + Map find(long userId, Collection key); + + Map list(long userId, Long offset, Long limit); Bookmark load(long id, long userId); diff --git a/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/SqliteDb.java b/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/SqliteDb.java index acc7fd1..dbf6a1a 100644 --- a/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/SqliteDb.java +++ b/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/SqliteDb.java @@ -2,6 +2,7 @@ package de.srsoftware.umbrella.bookmarks; import static de.srsoftware.tools.jdbc.Condition.equal; +import static de.srsoftware.tools.jdbc.Condition.like; import static de.srsoftware.tools.jdbc.Query.*; import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; import static de.srsoftware.umbrella.bookmarks.Constants.*; @@ -69,7 +70,26 @@ CREATE TABLE IF NOT EXISTS {0} ( } @Override - public Map list(long userId, long offset, long limit) { + public Map find(long userId, Collection keys) { + try { + var map = new HashMap(); + var query = select(ALL).from(TABLE_URL_COMMENTS).leftJoin(URL_ID,TABLE_URLS,ID) + .where(USER_ID, equal(userId)); + for (var key : keys) query.where(COMMENT,like("%"+key+"%")); + var rs = query.sort(format("{0} DESC",TIMESTAMP)).exec(db); + while (rs.next()){ + var bookmark = Bookmark.of(rs); + map.put(bookmark.urlId(),bookmark); + } + rs.close();; + return map; + } catch (SQLException e) { + throw new UmbrellaException("Failed to load bookmark list"); + } + } + + @Override + public Map list(long userId, Long offset, Long limit) { try { var map = new HashMap(); var rs = select(ALL).from(TABLE_URL_COMMENTS).leftJoin(URL_ID,TABLE_URLS,ID).where(USER_ID, equal(userId)).sort(format("{0} DESC",TIMESTAMP)).skip(offset).limit(limit).exec(db); diff --git a/core/src/main/java/de/srsoftware/umbrella/core/Constants.java b/core/src/main/java/de/srsoftware/umbrella/core/Constants.java index 740a0f0..d8f1f3c 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/Constants.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/Constants.java @@ -98,6 +98,7 @@ public class Constants { public static final String FIELD_TYPE_PREFIX = "type_prefix"; public static final String FIELD_TYPE_SUFFIX = "type_suffix"; public static final String FIELD_UNIT = "unit"; + public static final String FULLTEXT = "fulltext"; public static final String GET = "GET"; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Project.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Project.java index 2fc8397..7b11340 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Project.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Project.java @@ -31,7 +31,7 @@ public class Project implements Mappable { this.companyId = companyId; this.showClosed = showClosed; this.members = members; - this.allowedStates = allowedStates; + this.allowedStates = new ArrayList<>(allowedStates); } public Collection allowedStates(){ diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Status.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Status.java index 2e49391..c2677ba 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Status.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Status.java @@ -17,7 +17,7 @@ public record Status(String name, int code) implements Mappable { public static final Status STARTED = new Status("STARTED",40); // was 20 public static final Status COMPLETE = new Status("COMPLETE",60); public static final Status CANCELLED = new Status("CANCELLED", 100); - public static final List PREDEFINED = new ArrayList<>(List.of(OPEN, STARTED, PENDING, COMPLETE, CANCELLED)); + public static final List PREDEFINED = List.of(OPEN, STARTED, PENDING, COMPLETE, CANCELLED); public static Status of(int code){ return switch (code){ diff --git a/frontend/src/Components/Menu.svelte b/frontend/src/Components/Menu.svelte index a9b9b42..2aec68e 100644 --- a/frontend/src/Components/Menu.svelte +++ b/frontend/src/Components/Menu.svelte @@ -5,6 +5,7 @@ import { useTinyRouter } from 'svelte-tiny-router'; import { logout, user } from '../user.svelte.js'; import { t } from '../translations.svelte.js'; +let key = $state(null); const router = useTinyRouter(); const modules = $state([]); @@ -27,6 +28,12 @@ function go(path){ return false; } +async function search(e){ + e.preventDefault(); + router.navigate(`/search?key=${key}`); + return false; +} + onMount(fetchModules); @@ -37,6 +44,10 @@ onMount(fetchModules);