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 6e0b94f0..aa47ccf1 100644 --- a/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java +++ b/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java @@ -12,6 +12,7 @@ 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.SettingsService; import de.srsoftware.umbrella.core.Util; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.documents.DocumentApi; @@ -88,6 +89,7 @@ public class Application { new WebHandler().bindPath("/").on(server); new WikiModule(config).bindPath("/api/wiki").on(server); new FileModule(config).bindPath("/api/files").on(server); + new SettingsService(config).bindPath("/api/settings").on(server); } catch (Exception e) { LOG.log(ERROR,"Startup failed",e); System.exit(-1); diff --git a/core/src/main/java/de/srsoftware/umbrella/core/SettingsService.java b/core/src/main/java/de/srsoftware/umbrella/core/SettingsService.java new file mode 100644 index 00000000..1addfcb8 --- /dev/null +++ b/core/src/main/java/de/srsoftware/umbrella/core/SettingsService.java @@ -0,0 +1,115 @@ +/* © SRSoftware 2025 */ +package de.srsoftware.umbrella.core; + +import static de.srsoftware.umbrella.core.ModuleRegistry.userService; +import static de.srsoftware.umbrella.core.constants.Constants.CLASS; +import static de.srsoftware.umbrella.core.constants.Field.*; +import static de.srsoftware.umbrella.core.constants.Path.MENU; +import static de.srsoftware.umbrella.core.constants.Text.*; +import static de.srsoftware.umbrella.core.constants.Text.USERS; +import static java.text.MessageFormat.format; + +import com.sun.net.httpserver.HttpExchange; +import de.srsoftware.configuration.Configuration; +import de.srsoftware.tools.Mappable; +import de.srsoftware.tools.Path; +import de.srsoftware.tools.SessionToken; +import de.srsoftware.umbrella.core.constants.Module; +import de.srsoftware.umbrella.core.constants.Text; +import de.srsoftware.umbrella.core.exceptions.UmbrellaException; +import de.srsoftware.umbrella.core.model.Token; +import de.srsoftware.umbrella.core.model.UmbrellaUser; +import java.io.IOException; +import java.util.*; +import org.json.JSONObject; + +public class SettingsService extends BaseHandler { + + private final Configuration config; + + public SettingsService(Configuration config) { + this.config = config; + } + + @Override + 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 MENU -> getMenuSettings(user.get(), ex); + case null, default -> super.doGet(path, ex); + }; + } catch (UmbrellaException e) { + return send(ex, e); + } + } + + private record MenuEntry(int pos, String module, String clazz, String title) implements Mappable { + public static MenuEntry of(int pos, String module){ + return new MenuEntry(pos, module, module, module); + } + + public static MenuEntry of(int pos, String module, String title){ + return new MenuEntry(pos,module,module,title); + } + public static MenuEntry of(int pos, String module, String clazz, String title){ + return new MenuEntry(pos,module,clazz,title); + } + + @Override + public Map toMap() { + return Map.of(MODULE,module,CLASS,clazz,TITLE,title); + } + } + private boolean getMenuSettings(UmbrellaUser user, HttpExchange ex) throws IOException { + Optional modules = config.get("umbrella.modules"); + if (modules.isEmpty()) throw UmbrellaException.missingConfig("umbrella.modules"); + + var entries = new ArrayList(); + entries.add(MenuEntry.of(1, Module.USER, USERS)); + entries.add(MenuEntry.of(2, Module.COMPANY, COMPANIES)); + entries.add(MenuEntry.of(3, Module.PROJECT,Text.PROJECTS)); + entries.add(MenuEntry.of(4,Module.TASK,Text.TASKS)); + entries.add(MenuEntry.of(5,Module.TAGS)); + entries.add(MenuEntry.of(6,Module.DOCUMENT,"doc",Text.DOCUMENTS)); + entries.add(MenuEntry.of(7,Module.BOOKMARK,"mark",BOOKMARKS)); + entries.add(MenuEntry.of(8,Module.NOTES,"note",Text.NOTES)); + entries.add(MenuEntry.of(9,Module.FILES,"file", FILES)); + entries.add(MenuEntry.of(10,Module.TIME, Text.TIMETRACKING)); + entries.add(MenuEntry.of(11,Module.WIKI)); + entries.add(MenuEntry.of(12,Module.CONTACT, CONTACTS)); + entries.add(MenuEntry.of(13,Module.STOCK)); + entries.add(MenuEntry.of(14,Module.MESSAGE, MESSAGES)); + entries.add(MenuEntry.of(15,Module.POLL,Text.POLLS)); + + for (var i=0; i val = config.get(key); + if (val.isPresent()) { + var index = val.get(); + if (index<0) { + entries.remove(i); + i--; + continue; + } else { + entry = MenuEntry.of(index,entry.module,entry.clazz,entry.title); + entries.set(i,entry); + } + } + key = format("umbrella.modules.{0}.baseUrl",entry.module); + Optional baseUrl = config.get(key); + if (baseUrl.isPresent()) { + entry = MenuEntry.of(entry.pos,baseUrl.get(), entry.clazz,entry.title); + entries.set(i,entry); + } + } + var list = entries.stream().sorted((a,b) -> a.pos - b.pos) + .map(MenuEntry::toMap).toList(); + return sendContent(ex,list); + } +} \ No newline at end of file diff --git a/core/src/main/java/de/srsoftware/umbrella/core/constants/Constants.java b/core/src/main/java/de/srsoftware/umbrella/core/constants/Constants.java index b806bc56..8977aa1b 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/constants/Constants.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/constants/Constants.java @@ -12,6 +12,7 @@ public class Constants { // prevent instantiation } + public static final String CLASS = "class"; public static final String CONFIG_SESSION_DURATION = "umbrella.session.duration"; public static final String COUNT = "COUNT(*)"; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/constants/Field.java b/core/src/main/java/de/srsoftware/umbrella/core/constants/Field.java index fa05afed..8ec434c6 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/constants/Field.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/constants/Field.java @@ -86,6 +86,7 @@ public class Field { public static final String LOGIN = "login"; public static final String MEMBERS = "members"; + public static final String MENU_INDEX = "menu_index"; public static final String MESSAGE_ID = "message_id"; public static final String MIME = "mime"; public static final String MODULE = "module"; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/constants/Module.java b/core/src/main/java/de/srsoftware/umbrella/core/constants/Module.java index fa319e50..f3b8512d 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/constants/Module.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/constants/Module.java @@ -4,8 +4,17 @@ package de.srsoftware.umbrella.core.constants; public class Module { public static final String BOOKMARK = "bookmark"; public static final String COMPANY = "company"; + public static final String CONTACT = "contact"; + public static final String DOCUMENT = "document"; + public static final String FILES = "files"; + public static final String MESSAGE = "message"; + public static final String NOTES = "notes"; + public static final String POLL = "poll"; public static final String PROJECT = "project"; + public static final String STOCK = "stock"; + public static final String TAGS = "tags"; public static final String TASK = "task"; + public static final String TIME = "time"; public static final String USER = "user"; public static final String WIKI = "wiki"; } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/constants/Path.java b/core/src/main/java/de/srsoftware/umbrella/core/constants/Path.java index c7e61716..decc82f2 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/constants/Path.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/constants/Path.java @@ -22,6 +22,9 @@ public class Path { public static final String LOGIN = "login"; public static final String LOGOUT = "logout"; + + public static final String MENU = "menu"; + public static final String PAGE = "page"; public static final String PASSWORD = "password"; public static final String PROJECT = "project"; diff --git a/core/src/main/java/de/srsoftware/umbrella/core/constants/Text.java b/core/src/main/java/de/srsoftware/umbrella/core/constants/Text.java index bf91a7c5..562c3e46 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/constants/Text.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/constants/Text.java @@ -5,8 +5,9 @@ package de.srsoftware.umbrella.core.constants; * This is a collection of messages that appear throughout the project */ public class Text { - public static final String BOOKMARK = "bookmark"; - public static final String BOOLEAN = "Boolean"; + public static final String BOOKMARK = "bookmark"; + public static final String BOOKMARKS = "bookmarks"; + public static final String BOOLEAN = "Boolean"; public static final String COMPANIES = "companies"; public static final String COMPANY = "company"; @@ -24,6 +25,8 @@ public class Text { public static final String EMAILS_FOR_RECEIVER = "emails for {email}"; + public static final String FILES = "files"; + public static final String INVALID_DB_CODE = "Encountered invalid dbCode: {code}"; public static final String ITEM = "item"; public static final String ITEMS = "items"; @@ -34,13 +37,16 @@ public class Text { public static final String LONG = "Long"; public static final String MESSAGE = "message"; + public static final String MESSAGES = "messages"; public static final String NOTE = "note"; + public static final String NOTES = "notes"; public static final String NOTE_WITH_ID = "note ({id})"; public static final String NUMBER = "number"; public static final String PATH = "path"; - public static final String PROJECT = "project"; + public static final String POLLS = "polls"; + public static final String PROJECTS = "projects"; public static final String PROJECT_WITH_ID = "project ({id})"; public static final String PROPERTIES = "properties"; public static final String PROPERTY = "property"; @@ -52,18 +58,21 @@ public class Text { public static final String SERVICE_WITH_ID = "service ({id})"; public static final String SESSION = "session"; public static final String SETTINGS = "settings"; + public static final String STOCK = "stock"; public static final String STRING = "string"; public static final String TABLE_WITH_NAME = "table {name}"; public static final String TAGS = "tags"; public static final String TASK = "task"; public static final String TASKS = "tasks"; + public static final String TIMETRACKING = "timetracking"; public static final String TIME_WITH_ID = "time ({id})"; public static final String TYPE = "type"; public static final String UNIT = "unit"; public static final String USER_WITH_ID = "user ({id})"; + public static final String WIKI = "wiki"; public static final String WIKI_PAGE = "wiki page"; public static final String WIKI_PAGES = "wiki pages"; diff --git a/frontend/src/Components/Menu.svelte b/frontend/src/Components/Menu.svelte index 603ccc41..afb6035b 100644 --- a/frontend/src/Components/Menu.svelte +++ b/frontend/src/Components/Menu.svelte @@ -2,27 +2,25 @@ import { onMount } from 'svelte'; import { useTinyRouter } from 'svelte-tiny-router'; -import { logout, user } from '../user.svelte.js'; -import { t } from '../translations.svelte.js'; +import { api, get } from '../urls.svelte'; +import { error } from '../warn.svelte'; +import { logout, user } from '../user.svelte'; +import { t } from '../translations.svelte'; import TimeRecorder from './TimeRecorder.svelte'; let key = $state(null); const router = useTinyRouter(); -const modules = $state([]); +let modules = $state(null); let expand = $state(false); async function fetchModules(){ - const url = `${location.protocol}//${location.host.replace('5173','8080')}/legacy/user/modules`; - const resp = await fetch(url,{credentials:'include'}); - if (resp.ok){ - const arr = await resp.json(); - for (let entry of arr) { - let name = t('module.'+entry.module); - if (name) modules.push({name:name,url:entry.url}); - } + let url = api('settings/menu'); + const res = await get(url); + if (res.ok){ + modules = await res.json(); } else { - console.log('error'); + error(res); } } @@ -30,7 +28,11 @@ function onclick(e){ e.preventDefault(); expand = false; let href = e.target.getAttribute('href'); - if (href) router.navigate(href); + if (href) { + if (href.includes('://')) { + window.location.href = href; + } else router.navigate(href); + } return false; } @@ -55,27 +57,13 @@ onMount(fetchModules); - - {t('users')} - {t('companies')} - {t('projects')} - {t('tasks')} - {t('tags')} - {t('documents')} - {t('bookmarks')} - {t('notes')} - {t('files')} - {t('timetracking')} - {t('wiki')} - {t('contacts')} - {t('stock')} - {@html t('messages')} + + {#each modules as module,i} + {@html t(module.title)} + {/each} {#if user.id == 2} {t('tutorial')} {/if} - {#each modules as module,i} - {#if module.name.trim()}{module.name}{/if} - {/each} {#if user.name } {t('logout_user',{user:user.name})} {/if} diff --git a/web/src/main/resources/web/css/bloodshed-color.css b/web/src/main/resources/web/css/bloodshed-color.css index 3dd8414d..3bfa709a 100644 --- a/web/src/main/resources/web/css/bloodshed-color.css +++ b/web/src/main/resources/web/css/bloodshed-color.css @@ -74,6 +74,10 @@ tr:hover .taglist .tag button { color: black; } +.code{ + color: orangered; +} + .error { background-color: red; color: black; diff --git a/web/src/main/resources/web/css/default-color.css b/web/src/main/resources/web/css/default-color.css index aebd5788..0556d377 100644 --- a/web/src/main/resources/web/css/default-color.css +++ b/web/src/main/resources/web/css/default-color.css @@ -72,6 +72,10 @@ tr:hover .taglist .tag button { color: black; } +.code{ + color: chocolate; +} + .em { background: rgba(255, 215, 0, 0.09); } diff --git a/web/src/main/resources/web/css/winter-color.css b/web/src/main/resources/web/css/winter-color.css index cef9b6fc..f0c53407 100644 --- a/web/src/main/resources/web/css/winter-color.css +++ b/web/src/main/resources/web/css/winter-color.css @@ -67,6 +67,10 @@ tr:hover .taglist .tag button { color: black; } +.code{ + color: black; +} + .error { background-color: red; color: black;