Browse Source

preparing contact index page

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
module/contact
Stephan Richter 4 weeks ago
parent
commit
bf7de3cb6c
  1. 2
      backend/src/main/java/de/srsoftware/umbrella/backend/Application.java
  2. 3
      contact/src/main/java/de/srsoftware/umbrella/contact/Constants.java
  3. 4
      contact/src/main/java/de/srsoftware/umbrella/contact/ContactDb.java
  4. 40
      contact/src/main/java/de/srsoftware/umbrella/contact/ContactModule.java
  5. 62
      contact/src/main/java/de/srsoftware/umbrella/contact/SqliteDb.java
  6. 1
      core/src/main/java/de/srsoftware/umbrella/core/Constants.java
  7. 6
      core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java
  8. 4
      core/src/main/java/de/srsoftware/umbrella/core/api/ContactService.java
  9. 5
      core/src/main/java/de/srsoftware/umbrella/core/model/Contact.java
  10. 2
      frontend/src/App.svelte
  11. 1
      frontend/src/Components/Menu.svelte
  12. 24
      frontend/src/routes/contact/Index.svelte
  13. 3
      translations/src/main/resources/de.json
  14. 1
      translations/src/main/resources/en.json

2
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.tools.ColorLogger;
import de.srsoftware.umbrella.bookmarks.BookmarkApi; import de.srsoftware.umbrella.bookmarks.BookmarkApi;
import de.srsoftware.umbrella.company.CompanyModule; import de.srsoftware.umbrella.company.CompanyModule;
import de.srsoftware.umbrella.contact.ContactModule;
import de.srsoftware.umbrella.core.Util; import de.srsoftware.umbrella.core.Util;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.documents.DocumentApi; import de.srsoftware.umbrella.documents.DocumentApi;
@ -68,6 +69,7 @@ public class Application {
new BookmarkApi(config).bindPath("/api/bookmark").on(server); new BookmarkApi(config).bindPath("/api/bookmark").on(server);
new CompanyModule(config).bindPath("/api/company").on(server); new CompanyModule(config).bindPath("/api/company").on(server);
new CompanyLegacy(config).bindPath("/legacy/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 DocumentApi(config).bindPath("/api/document").on(server);
new ItemApi(config).bindPath("/api/items").on(server); new ItemApi(config).bindPath("/api/items").on(server);
new UserLegacy(config).bindPath("/legacy/user").on(server); new UserLegacy(config).bindPath("/legacy/user").on(server);

3
contact/src/main/java/de/srsoftware/umbrella/contact/Constants.java

@ -4,7 +4,10 @@ package de.srsoftware.umbrella.contact;
public class Constants { public class Constants {
private Constants(){} private Constants(){}
public static final String ASSIGNED = "assigned";
public static final String CONFIG_DATABASE = "umbrella.modules.contact.database"; 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_USERS = "contacts_users";
public static final String TABLE_CONTACTS = "contacts"; public static final String TABLE_CONTACTS = "contacts";
} }

4
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.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Contact; import de.srsoftware.umbrella.core.model.Contact;
import java.util.Collection; import java.util.Map;
public interface ContactDb { public interface ContactDb {
Collection<Contact> listContactsOf(long id) throws UmbrellaException; Map<Long,Contact> listContactsOf(long id) throws UmbrellaException;
} }

40
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.contact.Constants.CONFIG_DATABASE;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect; 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 static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.configuration.Configuration; 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.api.ContactService;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Contact; import de.srsoftware.umbrella.core.model.Contact;
import de.srsoftware.umbrella.core.model.Token;
import de.srsoftware.umbrella.core.model.UmbrellaUser; 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; private final ContactDb contactDb;
public ContactModule(Configuration config) throws UmbrellaException { public ContactModule(Configuration config) throws UmbrellaException {
super();
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE));
contactDb = new SqliteDb(connect(dbFile)); contactDb = new SqliteDb(connect(dbFile));
ModuleRegistry.add(this);
} }
@Override @Override
public Collection<Contact> listContactsOf(UmbrellaUser user) throws UmbrellaException { public boolean doGet(Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
Optional<Token> 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<Long,Contact> listContactsOf(UmbrellaUser user) throws UmbrellaException {
return contactDb.listContactsOf(user.id()); return contactDb.listContactsOf(user.id());
} }
} }

62
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.Condition.equal;
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
import static de.srsoftware.tools.jdbc.Query.select; import static de.srsoftware.tools.jdbc.Query.select;
import static de.srsoftware.umbrella.contact.Constants.TABLE_CONTACTS; import static de.srsoftware.umbrella.contact.Constants.*;
import static de.srsoftware.umbrella.contact.Constants.TABLE_CONTACTS_USERS;
import static de.srsoftware.umbrella.core.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.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Contact; import de.srsoftware.umbrella.core.model.Contact;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection; import java.util.HashMap;
import java.util.HashSet; import java.util.Map;
public class SqliteDb implements ContactDb{ public class SqliteDb extends BaseDb implements ContactDb{
private final Connection conn;
public SqliteDb(Connection connection) { public SqliteDb(Connection connection) {
conn = connection; super(connection);
} }
@Override @Override
public Collection<Contact> 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 { try {
var rs = select(ALL).from(TABLE_CONTACTS).leftJoin(ID,TABLE_CONTACTS_USERS,USER_ID).where(USER_ID,equal(userId)).exec(conn); db.prepareStatement(sql).execute();
var contacts = new HashSet<Contact>(); } catch (SQLException e) {
while (rs.next()) contacts.add(Contact.of(rs)); throw databaseException(ERROR_FAILED_CREATE_TABLE,TABLE_CONTACTS_USERS);
}
}
@Override
public Map<Long,Contact> 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<Long,Contact>();
while (rs.next()) {
var contact = Contact.of(rs);
contacts.put(contact.id(),contact);
}
rs.close(); rs.close();
return contacts; return contacts;
} catch (SQLException e) { } catch (SQLException e) {
throw UmbrellaException.databaseException("Failed to load contacts og user {0}",userId); throw databaseException("Failed to load contacts of user {0}",userId);
} }
} }
} }

1
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 UTF8 = UTF_8.displayName();
public static final String VALUE = "value"; public static final String VALUE = "value";
public static final String VCARD = "vcard";
public static final String VERSION = "version"; public static final String VERSION = "version";
public static final String VERSIONS = "versions"; public static final String VERSIONS = "versions";

6
core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java

@ -7,6 +7,7 @@ import de.srsoftware.umbrella.core.api.*;
public class ModuleRegistry { public class ModuleRegistry {
private BookmarkService bookmarkService; private BookmarkService bookmarkService;
private CompanyService companyService; private CompanyService companyService;
private ContactService contactService;
private DocumentService documentService; private DocumentService documentService;
private FileService fileService; private FileService fileService;
private ItemService itemService; private ItemService itemService;
@ -29,6 +30,7 @@ public class ModuleRegistry {
switch (service) { switch (service) {
case BookmarkService bs: singleton.bookmarkService = bs; break; case BookmarkService bs: singleton.bookmarkService = bs; break;
case CompanyService cs: singleton.companyService = cs; break; case CompanyService cs: singleton.companyService = cs; break;
case ContactService cs: singleton.contactService = cs; break;
case DocumentService ds: singleton.documentService = ds; break; case DocumentService ds: singleton.documentService = ds; break;
case FileService fs: singleton.fileService = fs; break; case FileService fs: singleton.fileService = fs; break;
case ItemService is: singleton.itemService = is; break; case ItemService is: singleton.itemService = is; break;
@ -55,6 +57,10 @@ public class ModuleRegistry {
return singleton.companyService; return singleton.companyService;
} }
public static ContactService contactService() {
return singleton.contactService;
}
public static DocumentService documentService(){ public static DocumentService documentService(){
return singleton.documentService; return singleton.documentService;
} }

4
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.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Contact; import de.srsoftware.umbrella.core.model.Contact;
import de.srsoftware.umbrella.core.model.UmbrellaUser; import de.srsoftware.umbrella.core.model.UmbrellaUser;
import java.util.Collection; import java.util.Map;
public interface ContactService { public interface ContactService {
Collection<Contact> listContactsOf(UmbrellaUser user) throws UmbrellaException; Map<Long, Contact> listContactsOf(UmbrellaUser user) throws UmbrellaException;
} }

5
core/src/main/java/de/srsoftware/umbrella/core/model/Contact.java

@ -1,8 +1,7 @@
/* © SRSoftware 2025 */ /* © SRSoftware 2025 */
package de.srsoftware.umbrella.core.model; package de.srsoftware.umbrella.core.model;
import static de.srsoftware.umbrella.core.Constants.DATA; import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.Constants.ID;
import de.srsoftware.tools.Mappable; import de.srsoftware.tools.Mappable;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -16,6 +15,6 @@ public record Contact(long id, String vcard) implements Mappable {
@Override @Override
public Map<String, Object> toMap() { public Map<String, Object> toMap() {
return Map.of(); return Map.of(ID,id,VCARD,vcard);
} }
} }

2
frontend/src/App.svelte

@ -11,6 +11,7 @@
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 Companies from "./routes/company/Index.svelte"; import Companies from "./routes/company/Index.svelte";
import ContactList from "./routes/contact/Index.svelte";
import DocList from "./routes/document/List.svelte"; import DocList from "./routes/document/List.svelte";
import EditService from "./routes/user/EditService.svelte"; import EditService from "./routes/user/EditService.svelte";
import EditUser from "./routes/user/EditUser.svelte"; import EditUser from "./routes/user/EditUser.svelte";
@ -81,6 +82,7 @@
<Route path="/bookmark" component={Bookmarks} /> <Route path="/bookmark" component={Bookmarks} />
<Route path="/bookmark/:id/view" component={Bookmark} /> <Route path="/bookmark/:id/view" component={Bookmark} />
<Route path="/company" component={Companies} /> <Route path="/company" component={Companies} />
<Route path="/contact" component={ContactList} />
<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} />

1
frontend/src/Components/Menu.svelte

@ -62,6 +62,7 @@ onMount(fetchModules);
<a href="/files" {onclick}>{t('files')}</a> <a href="/files" {onclick}>{t('files')}</a>
<a href="/time" {onclick}>{t('timetracking')}</a> <a href="/time" {onclick}>{t('timetracking')}</a>
<a href="/wiki" {onclick}>{t('wiki')}</a> <a href="/wiki" {onclick}>{t('wiki')}</a>
<a href="/contact" {onclick}>{t('contacts')}</a>
{#if user.id == 2} {#if user.id == 2}
<a href="https://svelte.dev/tutorial/svelte/state" target="_blank">{t('tutorial')}</a> <a href="https://svelte.dev/tutorial/svelte/state" target="_blank">{t('tutorial')}</a>
{/if} {/if}

24
frontend/src/routes/contact/Index.svelte

@ -0,0 +1,24 @@
<script>
import { onMount } from 'svelte';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
let contacts = $state({})
async function load(){
const url = api('contact/list');
const res = await fetch(url,{credentials:'include'});
if (res.ok){
yikes();
var data = await res.json();
console.log(data);
} else {
error(res);
}
}
onMount(load);
</script>
<h1>{t('contacts')}</h1>

3
translations/src/main/resources/de.json

@ -31,7 +31,8 @@
"company_optional": "Firma (optional)", "company_optional": "Firma (optional)",
"confirmation": "Bestätigung", "confirmation": "Bestätigung",
"complete": "abschließen", "complete": "abschließen",
"contact": "Kontakte", "contact": "Kontakt",
"contacts": "Kontakte",
"contained_tax": "enthaltene Steuer", "contained_tax": "enthaltene Steuer",
"content": "Inhalt", "content": "Inhalt",
"context": "Kontext", "context": "Kontext",

1
translations/src/main/resources/en.json

@ -32,6 +32,7 @@
"confirmation": "confirmation", "confirmation": "confirmation",
"complete": "complete", "complete": "complete",
"contact": "contact", "contact": "contact",
"contacts": "contacts",
"contained_tax": "contained tax", "contained_tax": "contained tax",
"content": "content", "content": "content",
"context": "context", "context": "context",

Loading…
Cancel
Save