From bf7de3cb6c0cbfc544d57a4a664073ae4a1ca416 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Mon, 6 Oct 2025 23:14:39 +0200 Subject: [PATCH] preparing contact index page Signed-off-by: Stephan Richter --- .../umbrella/backend/Application.java | 2 + .../umbrella/contact/Constants.java | 3 + .../umbrella/contact/ContactDb.java | 4 +- .../umbrella/contact/ContactModule.java | 40 +++++++++++- .../srsoftware/umbrella/contact/SqliteDb.java | 62 +++++++++++++++---- .../srsoftware/umbrella/core/Constants.java | 1 + .../umbrella/core/ModuleRegistry.java | 6 ++ .../umbrella/core/api/ContactService.java | 4 +- .../umbrella/core/model/Contact.java | 5 +- frontend/src/App.svelte | 2 + frontend/src/Components/Menu.svelte | 1 + frontend/src/routes/contact/Index.svelte | 24 +++++++ translations/src/main/resources/de.json | 3 +- translations/src/main/resources/en.json | 1 + 14 files changed, 135 insertions(+), 23 deletions(-) create mode 100644 frontend/src/routes/contact/Index.svelte diff --git a/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java b/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java index 24e8422..a510189 100644 --- a/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java +++ b/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java @@ -11,6 +11,7 @@ import de.srsoftware.configuration.JsonConfig; import de.srsoftware.tools.ColorLogger; import de.srsoftware.umbrella.bookmarks.BookmarkApi; import de.srsoftware.umbrella.company.CompanyModule; +import de.srsoftware.umbrella.contact.ContactModule; import de.srsoftware.umbrella.core.Util; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.documents.DocumentApi; @@ -68,6 +69,7 @@ public class Application { new BookmarkApi(config).bindPath("/api/bookmark").on(server); new CompanyModule(config).bindPath("/api/company").on(server); new CompanyLegacy(config).bindPath("/legacy/company").on(server); + new ContactModule(config).bindPath("/api/contact").on(server); new DocumentApi(config).bindPath("/api/document").on(server); new ItemApi(config).bindPath("/api/items").on(server); new UserLegacy(config).bindPath("/legacy/user").on(server); diff --git a/contact/src/main/java/de/srsoftware/umbrella/contact/Constants.java b/contact/src/main/java/de/srsoftware/umbrella/contact/Constants.java index 9dcb66d..8e3ba3c 100644 --- a/contact/src/main/java/de/srsoftware/umbrella/contact/Constants.java +++ b/contact/src/main/java/de/srsoftware/umbrella/contact/Constants.java @@ -4,7 +4,10 @@ package de.srsoftware.umbrella.contact; public class Constants { private Constants(){} + + public static final String ASSIGNED = "assigned"; public static final String CONFIG_DATABASE = "umbrella.modules.contact.database"; + public static final String CONTACT_ID = "contact_id"; public static final String TABLE_CONTACTS_USERS = "contacts_users"; public static final String TABLE_CONTACTS = "contacts"; } diff --git a/contact/src/main/java/de/srsoftware/umbrella/contact/ContactDb.java b/contact/src/main/java/de/srsoftware/umbrella/contact/ContactDb.java index 23133c6..4d3921e 100644 --- a/contact/src/main/java/de/srsoftware/umbrella/contact/ContactDb.java +++ b/contact/src/main/java/de/srsoftware/umbrella/contact/ContactDb.java @@ -3,8 +3,8 @@ package de.srsoftware.umbrella.contact; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Contact; -import java.util.Collection; +import java.util.Map; public interface ContactDb { - Collection listContactsOf(long id) throws UmbrellaException; + Map listContactsOf(long id) throws UmbrellaException; } diff --git a/contact/src/main/java/de/srsoftware/umbrella/contact/ContactModule.java b/contact/src/main/java/de/srsoftware/umbrella/contact/ContactModule.java index 3675464..68162c0 100644 --- a/contact/src/main/java/de/srsoftware/umbrella/contact/ContactModule.java +++ b/contact/src/main/java/de/srsoftware/umbrella/contact/ContactModule.java @@ -3,25 +3,59 @@ package de.srsoftware.umbrella.contact; import static de.srsoftware.umbrella.contact.Constants.CONFIG_DATABASE; import static de.srsoftware.umbrella.core.ConnectionProvider.connect; +import static de.srsoftware.umbrella.core.ModuleRegistry.userService; +import static de.srsoftware.umbrella.core.Paths.LIST; +import static de.srsoftware.umbrella.core.Util.mapValues; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; +import com.sun.net.httpserver.HttpExchange; import de.srsoftware.configuration.Configuration; +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.api.ContactService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Contact; +import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; -import java.util.Collection; +import java.io.IOException; +import java.util.Map; +import java.util.Optional; -public class ContactModule implements ContactService { +public class ContactModule extends BaseHandler implements ContactService { private final ContactDb contactDb; public ContactModule(Configuration config) throws UmbrellaException { + super(); var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); contactDb = new SqliteDb(connect(dbFile)); + ModuleRegistry.add(this); } @Override - public Collection listContactsOf(UmbrellaUser user) throws UmbrellaException { + public boolean doGet(Path path, HttpExchange ex) throws IOException { + addCors(ex); + try { + Optional token = SessionToken.from(ex).map(Token::of); + var user = userService().loadUser(token); + if (user.isEmpty()) return unauthorized(ex); + var head = path.pop(); + return switch (head) { + case LIST -> getContacts(user.get(),ex); + case null, default -> super.doGet(path, ex); + }; + } catch (UmbrellaException e) { + return send(ex,e); + } + } + + private boolean getContacts(UmbrellaUser user, HttpExchange ex) throws IOException { + return sendContent(ex,mapValues(listContactsOf(user))); + } + + @Override + public Map listContactsOf(UmbrellaUser user) throws UmbrellaException { return contactDb.listContactsOf(user.id()); } } diff --git a/contact/src/main/java/de/srsoftware/umbrella/contact/SqliteDb.java b/contact/src/main/java/de/srsoftware/umbrella/contact/SqliteDb.java index 06ecbe1..96ee13e 100644 --- a/contact/src/main/java/de/srsoftware/umbrella/contact/SqliteDb.java +++ b/contact/src/main/java/de/srsoftware/umbrella/contact/SqliteDb.java @@ -4,34 +4,72 @@ package de.srsoftware.umbrella.contact; import static de.srsoftware.tools.jdbc.Condition.equal; import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; import static de.srsoftware.tools.jdbc.Query.select; -import static de.srsoftware.umbrella.contact.Constants.TABLE_CONTACTS; -import static de.srsoftware.umbrella.contact.Constants.TABLE_CONTACTS_USERS; +import static de.srsoftware.umbrella.contact.Constants.*; import static de.srsoftware.umbrella.core.Constants.*; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException; +import static java.text.MessageFormat.format; +import de.srsoftware.umbrella.core.BaseDb; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Contact; import java.sql.Connection; import java.sql.SQLException; -import java.util.Collection; -import java.util.HashSet; +import java.util.HashMap; +import java.util.Map; -public class SqliteDb implements ContactDb{ - private final Connection conn; +public class SqliteDb extends BaseDb implements ContactDb{ public SqliteDb(Connection connection) { - conn = connection; + super(connection); } @Override - public Collection listContactsOf(long userId) throws UmbrellaException{ + protected int createTables() { + int currentVersion = createSettingsTable(); + switch (currentVersion){ + case 0: + createContactTable(); + createContactsUsersTable(); + } + + return setCurrentVersion(1); + } + + private void createContactTable() { + var sql = "CREATE TABLE IF NOT EXISTS {0} ({1} INTEGER PRIMARY KEY, {2} TEXT)"; + sql = format(sql, TABLE_CONTACTS, ID, DATA); + try { + db.prepareStatement(sql).execute(); + } catch (SQLException e) { + throw databaseException(ERROR_FAILED_CREATE_TABLE,TABLE_CONTACTS); + } + } + + private void createContactsUsersTable() { + var sql = "CREATE TABLE IF NOT EXISTS {0} ({1} INT NOT NULL, {2} INT NOT NULL, {3} BOOLEAN DEFAULT 0, PRIMARY KEY({1}, {2}))"; + sql = format(sql, TABLE_CONTACTS_USERS, CONTACT_ID, USER_ID, ASSIGNED); try { - var rs = select(ALL).from(TABLE_CONTACTS).leftJoin(ID,TABLE_CONTACTS_USERS,USER_ID).where(USER_ID,equal(userId)).exec(conn); - var contacts = new HashSet(); - while (rs.next()) contacts.add(Contact.of(rs)); + db.prepareStatement(sql).execute(); + } catch (SQLException e) { + throw databaseException(ERROR_FAILED_CREATE_TABLE,TABLE_CONTACTS_USERS); + } + } + + + @Override + public Map listContactsOf(long userId) throws UmbrellaException{ + try { + var rs = select(ALL).from(TABLE_CONTACTS).leftJoin(ID,TABLE_CONTACTS_USERS,CONTACT_ID).where(USER_ID,equal(userId)).exec(db); + var contacts = new HashMap(); + while (rs.next()) { + var contact = Contact.of(rs); + contacts.put(contact.id(),contact); + } rs.close(); return contacts; } catch (SQLException e) { - throw UmbrellaException.databaseException("Failed to load contacts og user {0}",userId); + throw databaseException("Failed to load contacts of user {0}",userId); } } + } 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 ebfa9f0..7ab37d8 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/Constants.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/Constants.java @@ -185,6 +185,7 @@ public class Constants { public static final String UTF8 = UTF_8.displayName(); public static final String VALUE = "value"; + public static final String VCARD = "vcard"; public static final String VERSION = "version"; public static final String VERSIONS = "versions"; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java b/core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java index a77046f..6b8b614 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java @@ -7,6 +7,7 @@ import de.srsoftware.umbrella.core.api.*; public class ModuleRegistry { private BookmarkService bookmarkService; private CompanyService companyService; + private ContactService contactService; private DocumentService documentService; private FileService fileService; private ItemService itemService; @@ -29,6 +30,7 @@ public class ModuleRegistry { switch (service) { case BookmarkService bs: singleton.bookmarkService = bs; break; case CompanyService cs: singleton.companyService = cs; break; + case ContactService cs: singleton.contactService = cs; break; case DocumentService ds: singleton.documentService = ds; break; case FileService fs: singleton.fileService = fs; break; case ItemService is: singleton.itemService = is; break; @@ -55,6 +57,10 @@ public class ModuleRegistry { return singleton.companyService; } + public static ContactService contactService() { + return singleton.contactService; + } + public static DocumentService documentService(){ return singleton.documentService; } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/ContactService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/ContactService.java index 87a6d2e..12fd921 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/ContactService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/ContactService.java @@ -4,8 +4,8 @@ package de.srsoftware.umbrella.core.api; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Contact; import de.srsoftware.umbrella.core.model.UmbrellaUser; -import java.util.Collection; +import java.util.Map; public interface ContactService { - Collection listContactsOf(UmbrellaUser user) throws UmbrellaException; + Map listContactsOf(UmbrellaUser user) throws UmbrellaException; } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Contact.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Contact.java index f56f9c8..a62e951 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Contact.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Contact.java @@ -1,8 +1,7 @@ /* © SRSoftware 2025 */ package de.srsoftware.umbrella.core.model; -import static de.srsoftware.umbrella.core.Constants.DATA; -import static de.srsoftware.umbrella.core.Constants.ID; +import static de.srsoftware.umbrella.core.Constants.*; import de.srsoftware.tools.Mappable; import java.sql.ResultSet; @@ -16,6 +15,6 @@ public record Contact(long id, String vcard) implements Mappable { @Override public Map toMap() { - return Map.of(); + return Map.of(ID,id,VCARD,vcard); } } diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 79894e5..30c765b 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -11,6 +11,7 @@ import Bookmarks from "./routes/bookmark/Index.svelte"; import Callback from "./routes/user/OidcCallback.svelte"; import Companies from "./routes/company/Index.svelte"; + import ContactList from "./routes/contact/Index.svelte"; import DocList from "./routes/document/List.svelte"; import EditService from "./routes/user/EditService.svelte"; import EditUser from "./routes/user/EditUser.svelte"; @@ -81,6 +82,7 @@ + diff --git a/frontend/src/Components/Menu.svelte b/frontend/src/Components/Menu.svelte index d036878..f4817db 100644 --- a/frontend/src/Components/Menu.svelte +++ b/frontend/src/Components/Menu.svelte @@ -62,6 +62,7 @@ onMount(fetchModules); {t('files')} {t('timetracking')} {t('wiki')} + {t('contacts')} {#if user.id == 2} {t('tutorial')} {/if} diff --git a/frontend/src/routes/contact/Index.svelte b/frontend/src/routes/contact/Index.svelte new file mode 100644 index 0000000..9dcd1ad --- /dev/null +++ b/frontend/src/routes/contact/Index.svelte @@ -0,0 +1,24 @@ + + +

{t('contacts')}

\ No newline at end of file diff --git a/translations/src/main/resources/de.json b/translations/src/main/resources/de.json index c081c79..5679d6d 100644 --- a/translations/src/main/resources/de.json +++ b/translations/src/main/resources/de.json @@ -31,7 +31,8 @@ "company_optional": "Firma (optional)", "confirmation": "Bestätigung", "complete": "abschließen", - "contact": "Kontakte", + "contact": "Kontakt", + "contacts": "Kontakte", "contained_tax": "enthaltene Steuer", "content": "Inhalt", "context": "Kontext", diff --git a/translations/src/main/resources/en.json b/translations/src/main/resources/en.json index 7fdbc5b..9feb1ee 100644 --- a/translations/src/main/resources/en.json +++ b/translations/src/main/resources/en.json @@ -32,6 +32,7 @@ "confirmation": "confirmation", "complete": "complete", "contact": "contact", + "contacts": "contacts", "contained_tax": "contained tax", "content": "content", "context": "context",