From bda42eb15ae3ff72eb6538d46e5f14be58119039 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Wed, 15 Oct 2025 15:33:01 +0200 Subject: [PATCH] implemented tag cloud Signed-off-by: Stephan Richter --- frontend/src/App.svelte | 2 + frontend/src/Components/Menu.svelte | 1 + frontend/src/routes/tags/Index.svelte | 40 +++++++++++++++++++ .../umbrella/markdown/MarkdownApi.java | 3 -- .../srsoftware/umbrella/tags/Constants.java | 1 + .../de/srsoftware/umbrella/tags/SqliteDb.java | 19 +++++++++ .../de/srsoftware/umbrella/tags/TagDB.java | 3 ++ .../srsoftware/umbrella/tags/TagModule.java | 18 +++++---- translations/src/main/resources/de.json | 1 + web/src/main/resources/web/css/default.css | 9 +++++ 10 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 frontend/src/routes/tags/Index.svelte diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 30c765b..75c0ce9 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -28,6 +28,7 @@ import ResetPw from "./routes/user/ResetPw.svelte"; import Search from "./routes/search/Search.svelte"; import SendDoc from "./routes/document/Send.svelte"; + import TagList from "./routes/tags/Index.svelte"; import TagUses from "./routes/tags/TagUses.svelte"; import TaskList from "./routes/task/Index.svelte"; import Times from "./routes/time/Index.svelte"; @@ -96,6 +97,7 @@ + diff --git a/frontend/src/Components/Menu.svelte b/frontend/src/Components/Menu.svelte index f4817db..86694a3 100644 --- a/frontend/src/Components/Menu.svelte +++ b/frontend/src/Components/Menu.svelte @@ -56,6 +56,7 @@ onMount(fetchModules); {t('companies')} {t('projects')} {t('tasks')} + {t('tags')} {t('documents')} {t('bookmarks')} {t('notes')} diff --git a/frontend/src/routes/tags/Index.svelte b/frontend/src/routes/tags/Index.svelte new file mode 100644 index 0000000..43cb9fb --- /dev/null +++ b/frontend/src/routes/tags/Index.svelte @@ -0,0 +1,40 @@ + + +

{t('tags')}

+ +
+{#if tags} +{#each tags as entry} +{entry.tag} +{/each} +{/if} +
\ No newline at end of file diff --git a/markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java b/markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java index 4e11a54..c71b9c3 100644 --- a/markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java +++ b/markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java @@ -6,15 +6,12 @@ import static de.srsoftware.umbrella.core.ModuleRegistry.userService; import com.sun.net.httpserver.HttpExchange; import de.srsoftware.tools.Path; -import de.srsoftware.tools.SessionToken; import de.srsoftware.umbrella.core.BaseHandler; import de.srsoftware.umbrella.core.ModuleRegistry; import de.srsoftware.umbrella.core.Util; import de.srsoftware.umbrella.core.api.MarkdownService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; -import de.srsoftware.umbrella.core.model.Token; import java.io.IOException; -import java.util.Optional; public class MarkdownApi extends BaseHandler implements MarkdownService { diff --git a/tags/src/main/java/de/srsoftware/umbrella/tags/Constants.java b/tags/src/main/java/de/srsoftware/umbrella/tags/Constants.java index 26a0eff..adeee1e 100644 --- a/tags/src/main/java/de/srsoftware/umbrella/tags/Constants.java +++ b/tags/src/main/java/de/srsoftware/umbrella/tags/Constants.java @@ -6,6 +6,7 @@ public class Constants { public static final String COMMENT_HASH = "comment_hash"; public static final String CONFIG_DATABASE = "umbrella.modules.tags.database"; + public static final String COUNT = "count"; public static final String TABLE_COMMENTS = "comments"; public static final String TABLE_TAGS = "tags"; public static final String TABLE_TAGS_NEW = "tags_new"; diff --git a/tags/src/main/java/de/srsoftware/umbrella/tags/SqliteDb.java b/tags/src/main/java/de/srsoftware/umbrella/tags/SqliteDb.java index 119b1da..3f9d80e 100644 --- a/tags/src/main/java/de/srsoftware/umbrella/tags/SqliteDb.java +++ b/tags/src/main/java/de/srsoftware/umbrella/tags/SqliteDb.java @@ -17,6 +17,7 @@ import static java.lang.System.Logger.Level.*; import static java.text.MessageFormat.format; import static java.time.ZoneOffset.UTC; +import de.srsoftware.tools.Tuple; import de.srsoftware.tools.jdbc.Query; import de.srsoftware.umbrella.bookmarks.BookmarkDb; import de.srsoftware.umbrella.core.BaseDb; @@ -269,6 +270,24 @@ CREATE TABLE IF NOT EXISTS {0} ( } } + @Override + public Collection> list(long userId) { + try { + var rs = select("tag, COUNT(tag) as count").from(TABLE_TAGS).where(USER_ID,equal(userId)).groupBy(TAG).sort(TAG).exec(db); + var list = new ArrayList>(); + while (rs.next()) { + var tag = rs.getString(TAG); + if (tag.isBlank()) continue;; + var count = rs.getLong("count"); + list.add(Tuple.of(tag,count)); + } + rs.close(); + return list; + } catch (SQLException e) { + throw databaseException("Failed to load tags for user {0}",userId); + } + } + @Override public Map> list(long userId, String module, Collection entityIds) { try { diff --git a/tags/src/main/java/de/srsoftware/umbrella/tags/TagDB.java b/tags/src/main/java/de/srsoftware/umbrella/tags/TagDB.java index 57effd3..8056d0e 100644 --- a/tags/src/main/java/de/srsoftware/umbrella/tags/TagDB.java +++ b/tags/src/main/java/de/srsoftware/umbrella/tags/TagDB.java @@ -2,6 +2,7 @@ package de.srsoftware.umbrella.tags; +import de.srsoftware.tools.Tuple; import java.util.Collection; import java.util.List; import java.util.Map; @@ -14,6 +15,8 @@ public interface TagDB { Map> getUses(String tag, long id); + Collection> list(long userId); + Set list(long userId, String module, long entityId); /** diff --git a/tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java b/tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java index 07277bf..7dc48fa 100644 --- a/tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java +++ b/tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java @@ -7,8 +7,7 @@ import static de.srsoftware.umbrella.core.ModuleRegistry.userService; import static de.srsoftware.umbrella.core.ResponseCode.HTTP_UNPROCESSABLE; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.unprocessable; -import static de.srsoftware.umbrella.tags.Constants.CONFIG_DATABASE; -import static de.srsoftware.umbrella.tags.Constants.TAG; +import static de.srsoftware.umbrella.tags.Constants.*; import com.sun.net.httpserver.HttpExchange; import de.srsoftware.configuration.Configuration; @@ -67,7 +66,7 @@ public class TagModule extends BaseHandler implements TagService { var user = userService().refreshSession(ex); if (user.isEmpty()) return unauthorized(ex); var module = path.pop(); - if (module == null) throw unprocessable("Module missing in path."); + if (module == null) return getUserTags(ex, user.get()); var head = path.pop(); if (USES.equals(module)) return getTagUses(ex,head,user.get()); long entityId = Long.parseLong(head); @@ -79,10 +78,6 @@ public class TagModule extends BaseHandler implements TagService { } } - private boolean getTagUses(HttpExchange ex, String tag, UmbrellaUser user) throws IOException { - return sendContent(ex,tagDb.getUses(tag,user.id())); - } - @Override public boolean doPost(Path path, HttpExchange ex) throws IOException { addCors(ex); @@ -117,6 +112,10 @@ public class TagModule extends BaseHandler implements TagService { } } + private boolean getTagUses(HttpExchange ex, String tag, UmbrellaUser user) throws IOException { + return sendContent(ex,tagDb.getUses(tag,user.id())); + } + public Collection getTags(String module, long entityId, UmbrellaUser user) throws UmbrellaException{ return tagDb.list(user.id(),module,entityId); } @@ -126,6 +125,11 @@ public class TagModule extends BaseHandler implements TagService { return tagDb.list(user.id(),module,entityIds); } + private boolean getUserTags(HttpExchange ex, UmbrellaUser user) throws IOException { + var tuples = tagDb.list(user.id()).stream().filter(t -> t.a != null && t.b != null); + return sendContent(ex,tuples.map(t -> Map.of(TAG,t.a,COUNT,t.b))); + } + @Override public void save(String module, long entityId, Collection userIds, Collection tags) { tagDb.save(userIds,module,entityId,tags); diff --git a/translations/src/main/resources/de.json b/translations/src/main/resources/de.json index d8c732a..72451a4 100644 --- a/translations/src/main/resources/de.json +++ b/translations/src/main/resources/de.json @@ -38,6 +38,7 @@ "contained_tax": "enthaltene Steuer", "content": "Inhalt", "context": "Kontext", + "count_of_occurrences": "{count} Verwendungen", "country": "Land", "COURT": "Amtsgericht", "CUSTOMER-NUMBER": "Kundennummer", diff --git a/web/src/main/resources/web/css/default.css b/web/src/main/resources/web/css/default.css index 144cea0..ff2653a 100644 --- a/web/src/main/resources/web/css/default.css +++ b/web/src/main/resources/web/css/default.css @@ -423,4 +423,13 @@ fieldset.vcard{ .vcard td button.symbol{ float: right; +} + +.cloud .tag{ + border: 1px solid; + border-radius: 4px; + padding: 2px; + margin: 0 6px; + white-space: nowrap; + display: inline flow-root; } \ No newline at end of file