Compare commits

..

33 Commits

Author SHA1 Message Date
StephanRichter b7f82018f9 extended demo data
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-28 22:53:49 +01:00
StephanRichter a245f849e5 extended demo data
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-28 21:19:06 +01:00
StephanRichter 4c3312daca Merge branch 'main' into demodata 2026-01-28 20:45:37 +01:00
StephanRichter a6983780f5 enabling creation of tag database
Build Docker Image / Docker-Build (push) Successful in 2m13s
Build Docker Image / Clean-Registry (push) Successful in -2s
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-28 20:44:01 +01:00
StephanRichter 6e146c1577 extending demo data
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-28 20:42:56 +01:00
StephanRichter 49cbd328e6 started creating demo data
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-28 20:26:31 +01:00
StephanRichter 1db28b5bc0 improved note titles for documents
Build Docker Image / Docker-Build (push) Successful in 2m26s
Build Docker Image / Clean-Registry (push) Successful in -2s
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-28 15:52:27 +01:00
StephanRichter 285343218b added translations
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-28 15:33:59 +01:00
StephanRichter f402122d0c improved search: motes now showing name/title of related entities
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-28 15:22:20 +01:00
StephanRichter 4339e4fd98 added notification to search
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-28 08:44:52 +01:00
StephanRichter 16cbcdff60 Merge branch 'feature/wiki_fulltext_search'
Build Docker Image / Docker-Build (push) Successful in 2m17s
Build Docker Image / Clean-Registry (push) Successful in -2s
2026-01-27 23:38:46 +01:00
StephanRichter 8dc77f52e4 Merge branch 'search_order' 2026-01-27 23:38:31 +01:00
StephanRichter b04b9b92a6 Merge branch 'docker-optimize'
Build Docker Image / Clean-Registry (push) Has been cancelled
Build Docker Image / Docker-Build (push) Has been cancelled
2026-01-27 23:37:39 +01:00
StephanRichter 8d66ed4cbe implemented full-text search for wiki
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-27 22:05:56 +01:00
StephanRichter b0ee825e0a moved notes to the bottom of the search result list
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-27 21:41:12 +01:00
StephanRichter a321c813de working on timezone problems
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-27 15:34:33 +01:00
StephanRichter fda40d72f8 re-ordered docker commands
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-27 00:46:45 +01:00
StephanRichter 2375746d91 further optimization of dockerfile
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-27 00:29:43 +01:00
StephanRichter cf0cf2f5e9 fixed missing permissions for setting localtime
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-23 23:17:00 +01:00
StephanRichter 3e71ecc6cb optimized dockerfile
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-23 23:09:25 +01:00
StephanRichter 1302165ab2 fixing timezone for local deployment
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-23 23:00:41 +01:00
StephanRichter d08138c9e1 adding debug messages
Build Docker Image / Docker-Build (push) Successful in 2m32s
Build Docker Image / Clean-Registry (push) Successful in -1s
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-23 22:36:56 +01:00
StephanRichter 4cd1ea3277 now caching plantuml diagrams
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-22 23:46:20 +01:00
StephanRichter 1059164b4a bugfix:
Build Docker Image / Docker-Build (push) Successful in 2m36s
Build Docker Image / Clean-Registry (push) Successful in 0s
markdown rendering glitched when several @startuml…@enduml sections were present in one document

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-22 20:46:43 +01:00
StephanRichter f438bea4cc Merge branch 'bugfix/plantuml'
Build Docker Image / Docker-Build (push) Successful in 2m25s
Build Docker Image / Clean-Registry (push) Successful in 0s
2026-01-20 23:14:46 +01:00
StephanRichter 9394ca597c fixed another bug
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-20 22:52:30 +01:00
StephanRichter 53fe79fbbd fixed image heights when scaling width
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-20 22:44:30 +01:00
StephanRichter d62534b3eb fixed plantuml permissions
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-20 22:20:17 +01:00
StephanRichter 18e8e3ffd1 removed obsolete warning
Build Docker Image / Docker-Build (push) Successful in 2m32s
Build Docker Image / Clean-Registry (push) Successful in 0s
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-19 22:29:57 +01:00
StephanRichter ae55a76ca7 Merge branch 'feature/translation' 2026-01-16 17:07:25 +01:00
StephanRichter cf485055a6 improved file module GUI: now pushing a notification when markdown was copied
Build Docker Image / Docker-Build (push) Successful in 2m31s
Build Docker Image / Clean-Registry (push) Successful in 1s
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-16 15:48:56 +01:00
StephanRichter 968e5bfb95 improved file module GUI: files and directories are now sorted
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-16 09:25:32 +01:00
StephanRichter 6de5f1f660 working on translations
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
2026-01-15 22:51:23 +01:00
44 changed files with 517 additions and 271 deletions
+15 -10
View File
@@ -1,15 +1,15 @@
FROM alpine:3.22 AS svelte_build
RUN apk add npm
RUN adduser -Dh /home/svelte svelte
ADD . /home/svelte/Umbrella
ADD frontend /home/svelte/Umbrella/frontend
RUN chown -R svelte /home/svelte/Umbrella
USER svelte
WORKDIR /home/svelte/Umbrella/frontend
RUN npm install && npm run build
FROM alpine AS java_build
RUN apk add gradle fontconfig font-opensans openjdk21-jre
FROM alpine:3.22 AS java_build
RUN apk add gradle
ADD . /Umbrella
WORKDIR /Umbrella
COPY --from=svelte_build /home/svelte/Umbrella/frontend/dist web/src/main/resources/web
@@ -17,13 +17,18 @@ RUN gradle --no-daemon build
FROM alpine
RUN apk add bash fontconfig font-opensans graphviz openjdk21-jre weasyprint
RUN adduser -D umbrella
COPY --from=java_build /Umbrella/backend/build/libs/backend.jar /home/umbrella/jar/
RUN chown -R umbrella /home/umbrella
ADD https://github.com/plantuml/plantuml/releases/download/v1.2025.10/plantuml-1.2025.10.jar /home/umbrella/plantuml.jar
USER umbrella
RUN apk --no-cache add bash fontconfig font-opensans graphviz openjdk21-jre tzdata weasyprint \
&& adduser -D umbrella
WORKDIR /home/umbrella
RUN mkdir .config && ln -s /host/config.json .config/Umbrella.json
EXPOSE 80
CMD java -jar jar/backend.jar
ADD https://github.com/plantuml/plantuml/releases/download/v1.2025.10/plantuml-1.2025.10.jar /home/umbrella/plantuml.jar
COPY --from=java_build /Umbrella/backend/build/libs/backend.jar /home/umbrella/jar/
RUN mkdir .config \
&& ln -s /host/config.json .config/Umbrella.json \
&& chmod a+rx plantuml.jar \
&& chown -R umbrella . \
&& ln -s /usr/share/zoneinfo/Europe/Berlin /etc/localtime
USER umbrella
+1 -1
View File
@@ -1,5 +1,5 @@
FROM alpine:3.22
LABEL Maintainer "Stephan Richter <s.richter@srsoftware.de>"
LABEL Maintainer "Stephan Richter"
ARG UID=1000
ARG GID=1000
RUN apk add bash npm
@@ -35,11 +35,12 @@ import org.json.JSONObject;
public class Util {
public static final System.Logger LOG = System.getLogger("Util");
private static final Pattern UML_PATTERN = Pattern.compile("@start(\\w+)(.*)@end(\\1)",Pattern.DOTALL);
private static final Pattern UML_PATTERN = Pattern.compile("@start(\\w+)(.*?)@end(\\1)",Pattern.DOTALL);
private static File plantumlJar = null;
private static final JParsedown MARKDOWN = new JParsedown();
public static final String SHA1 = "SHA-1";
private static final MessageDigest SHA1_DIGEST;
private static final Map<Integer,String> umlCache = new HashMap<>();
static {
try {
@@ -79,11 +80,22 @@ public class Util {
try {
if (plantumlJar != null && plantumlJar.exists()) {
var matcher = UML_PATTERN.matcher(source);
if (matcher.find()) {
while (matcher.find()) {
var uml = matcher.group(0).trim();
var start = matcher.start(0);
var end = matcher.end(0);
var umlHash = uml.hashCode();
LOG.log(DEBUG,"Hash of Plantuml code: {0}",umlHash);
var svg = umlCache.get(umlHash);
if (svg != null){
LOG.log(DEBUG,"Serving Plantuml generated SVG from cache…");
source = source.substring(0, start) + svg + source.substring(end);
matcher = UML_PATTERN.matcher(source);
continue;
}
LOG.log(DEBUG,"Cache miss. Generating SVG from plantuml code…");
ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", plantumlJar.getAbsolutePath(), "-tsvg", "-pipe");
var ignored = processBuilder.redirectErrorStream();
var process = processBuilder.start();
@@ -94,8 +106,11 @@ public class Util {
try (InputStream is = process.getInputStream()) {
byte[] out = is.readAllBytes();
var svg = new String(out, UTF_8);
LOG.log(DEBUG,"Generated SVG. Pushing to cache…");
svg = new String(out, UTF_8);
umlCache.put(umlHash,svg);
source = source.substring(0, start) + svg + source.substring(end);
matcher = UML_PATTERN.matcher(source);
}
}
}
@@ -9,15 +9,15 @@ public class Constants {
private Constants(){
// prevent instantiation
}
public static final String FALLBACK_LANG = "de";
public static final String HOME = "home";
public static final String JSONARRAY = "json array";
public static final String JSONOBJECT = "json object";
public static final String KEEP_ALIVE = "keep-alive";
public static final String NO_CACHE = "no-cache";
public static final String NONE = "none";
public static final String TABLE_SETTINGS = "settings";
public static final String UMBRELLA = "Umbrella";
public static final String UTF8 = UTF_8.displayName();
public static final String FALLBACK_LANG = "de";
public static final String HOME = "home";
public static final String JSONARRAY = "json array";
public static final String JSONOBJECT = "json object";
public static final String KEEP_ALIVE = "keep-alive";
public static final String NO_CACHE = "no-cache";
public static final String NONE = "none";
public static final String TABLE_SETTINGS = "settings";
public static final String UMBRELLA = "Umbrella";
public static final String UTF8 = UTF_8.displayName();
}
@@ -2,170 +2,170 @@
package de.srsoftware.umbrella.core.constants;
public class Field {
public static final String ADDRESS = "address";
public static final String ADDRESS = "address";
public static final String ALLOWED_STATES = "allowed_states";
public static final String AMOUNT = "amount";
public static final String ATTACHMENTS = "attachments";
public static final String AUTHORIZATION = "Authorization";
public static final String AMOUNT = "amount";
public static final String ATTACHMENTS = "attachments";
public static final String AUTHORIZATION = "Authorization";
public static final String BANK_ACCOUNT = "bank_account";
public static final String BODY = "body";
public static final String BODY = "body";
public static final String CACHE_CONTROL = "Cache-Control";
public static final String CODE = "code";
public static final String COMMENT = "comment";
public static final String COMPANY = "company";
public static final String COMPANY_ID = "company_id";
public static final String CONNECTION = "Connection";
public static final String CONTENT = "content";
public static final String CONTENT_DISPOSITION = "Content-Disposition";
public static final String CONTENT_ENCODING = "Content-Encoding";
public static final String CONTENT_TYPE = "Content-Type";
public static final String COURT = "court";
public static final String CURRENCY = "currency";
public static final String CUSTOMER = "customer";
public static final String CUSTOMER_EMAIL = "customer_email";
public static final String CUSTOMER_NUMBER = "customer_number";
public static final String CACHE_CONTROL = "Cache-Control";
public static final String CODE = "code";
public static final String COMMENT = "comment";
public static final String COMPANY = "company";
public static final String COMPANY_ID = "company_id";
public static final String CONNECTION = "Connection";
public static final String CONTENT = "content";
public static final String CONTENT_DISPOSITION = "Content-Disposition";
public static final String CONTENT_ENCODING = "Content-Encoding";
public static final String CONTENT_TYPE = "Content-Type";
public static final String COURT = "court";
public static final String CURRENCY = "currency";
public static final String CUSTOMER = "customer";
public static final String CUSTOMER_EMAIL = "customer_email";
public static final String CUSTOMER_NUMBER = "customer_number";
public static final String CUSTOMER_NUMBER_PREFIX = "customer_number_prefix";
public static final String CUSTOMER_TAX_NUMBER = "customer_tax_number";
public static final String CUSTOMER_TAX_NUMBER = "customer_tax_number";
public static final String DATA = "data";
public static final String DB_VERSION = "db_version";
public static final String DATE = "date";
public static final String DECIMALS = "decimals";
public static final String DATA = "data";
public static final String DB_VERSION = "db_version";
public static final String DATE = "date";
public static final String DECIMALS = "decimals";
public static final String DECIMAL_SEPARATOR = "decimal_separator";
public static final String DEFAULT_HEADER = "default_header";
public static final String DEFAULT_FOOTER = "default_footer";
public static final String DEFAULT_MAIL = "type_mail_text";
public static final String DEFAULT_HEADER = "default_header";
public static final String DEFAULT_FOOTER = "default_footer";
public static final String DEFAULT_MAIL = "type_mail_text";
public static final String DELETED = "deleted";
public static final String DELIVERY = "delivery";
public static final String DELIVERY_DATE = "delivery_date";
public static final String DESCRIPTION = "description";
public static final String DOCUMENT = "document";
public static final String DOCUMENT_ID = "document_id";
public static final String DOC_TYPE_ID = "document_type_id";
public static final String DOMAIN = "domain";
public static final String DROP_MEMBER = "drop_member";
public static final String DUE_DATE = "due_date";
public static final String DURATION = "duration";
public static final String DELIVERY = "delivery";
public static final String DELIVERY_DATE = "delivery_date";
public static final String DESCRIPTION = "description";
public static final String DOCUMENT = "document";
public static final String DOCUMENT_ID = "document_id";
public static final String DOC_TYPE_ID = "document_type_id";
public static final String DOMAIN = "domain";
public static final String DROP_MEMBER = "drop_member";
public static final String DUE_DATE = "due_date";
public static final String DURATION = "duration";
public static final String EMAIL = "email";
public static final String END_TIME = "end_time";
public static final String ENTITY_ID = "entity_id";
public static final String EST_TIME = "est_time";
public static final String EXPECTED = "expected";
public static final String EMAIL = "email";
public static final String END_TIME = "end_time";
public static final String ENTITY_ID = "entity_id";
public static final String EST_TIME = "est_time";
public static final String EXPECTED = "expected";
public static final String EXPIRATION = "expiration";
public static final String FIELD = "field";
public static final String FOOTER = "footer";
public static final String FROM = "from";
public static final String FIELD = "field";
public static final String FOOTER = "footer";
public static final String FROM = "from";
public static final String FULLTEXT = "fulltext";
public static final String GROSS_SUM = "gross_sum";
public static final String GROSS_SUM = "gross_sum";
public static final String GUEST_ALLOWED = "guest_allowed";
public static final String HASH = "hash";
public static final String HEAD = "head";
public static final String ID = "id";
public static final String ITEM = "item";
public static final String ID = "id";
public static final String ITEM = "item";
public static final String ITEM_CODE = "item_code";
public static final String KEY = "key";
public static final String LANGUAGE = "language";
public static final String LANGUAGE = "language";
public static final String LAST_CUSTOMER_NUMBER = "last_customer_number";
public static final String LIMIT = "limit";
public static final String LOCATION = "location";
public static final String LIMIT = "limit";
public static final String LOCATION = "location";
public static final String LOCATION_ID = "location_id";
public static final String LOCATIONS = "locations";
public static final String LOGIN = "login";
public static final String MEMBERS = "members";
public static final String MIME = "mime";
public static final String MODULE = "module";
public static final String LOCATIONS = "locations";
public static final String LOGIN = "login";
public static final String MEMBERS = "members";
public static final String MIME = "mime";
public static final String MODULE = "module";
public static final String NAME = "name";
public static final String NAME = "name";
public static final String NEW_MEMBER = "new_member";
public static final String NET_PRICE = "net_price";
public static final String NET_SUM = "net_sum";
public static final String NEXT_TYPE = "next_type_id";
public static final String NO_INDEX = "no_index";
public static final String NOTE = "note";
public static final String NUMBER = "number";
public static final String NET_PRICE = "net_price";
public static final String NET_SUM = "net_sum";
public static final String NEXT_TYPE = "next_type_id";
public static final String NO_INDEX = "no_index";
public static final String NOTE = "note";
public static final String NUMBER = "number";
public static final String OBJECT = "object";
public static final String OFFSET = "offset";
public static final String OPTIONAL = "optional";
public static final String OWNER = "owner";
public static final String OBJECT = "object";
public static final String OFFSET = "offset";
public static final String OPTIONAL = "optional";
public static final String OWNER = "owner";
public static final String OWNER_NUMBER = "owner_number";
public static final String PARENT_LOCATION_ID = "parent_location_id";
public static final String PARENT_TASK_ID = "parent_task_id";
public static final String PASS = "pass";
public static final String PASSWORD = "password";
public static final String PATH = "path";
public static final String PERMISSION = "permission";
public static final String PHONE = "phone";
public static final String POS = "pos";
public static final String POSITIONS = "positions";
public static final String PRICE = "single_price";
public static final String PRICE_FORMAT = "price_format";
public static final String PRIORITY = "priority";
public static final String PROJECT_ID = "project_id";
public static final String PROPERTIES = "properties";
public static final String PARENT_TASK_ID = "parent_task_id";
public static final String PASS = "pass";
public static final String PASSWORD = "password";
public static final String PATH = "path";
public static final String PERMISSION = "permission";
public static final String PHONE = "phone";
public static final String POS = "pos";
public static final String POSITIONS = "positions";
public static final String PRICE = "single_price";
public static final String PRICE_FORMAT = "price_format";
public static final String PRIORITY = "priority";
public static final String PROJECT_ID = "project_id";
public static final String PROPERTIES = "properties";
public static final String RECEIVERS = "receivers";
public static final String REDIRECT = "redirect";
public static final String RECEIVERS = "receivers";
public static final String REDIRECT = "redirect";
public static final String RENDERED = "rendered";
public static final String REQUIRED_TASKS_IDS = "required_tasks_ids";
public static final String SENDER = "sender";
public static final String SETTINGS = "settings";
public static final String SENDER = "sender";
public static final String SETTINGS = "settings";
public static final String SHOW_CLOSED = "show_closed";
public static final String SOURCE = "source";
public static final String START_DATE = "start_date";
public static final String START_TIME = "start_time";
public static final String STATE = "state";
public static final String STATUS = "status";
public static final String SOURCE = "source";
public static final String START_DATE = "start_date";
public static final String START_TIME = "start_time";
public static final String STATE = "state";
public static final String STATUS = "status";
public static final String STATUS_CODE = "code";
public static final String SUBJECT = "subject";
public static final String SUBJECT = "subject";
public static final String TABLE = "table";
public static final String TABLE = "table";
public static final String TAGS = "tags";
public static final String TAG_COLORS = "tag_colors";
public static final String TASK = "task";
public static final String TASK = "task";
public static final String TASK_IDS = "task_ids";
public static final String TASKS = "tasks";
public static final String TASKS = "tasks";
public static final String TAX = "tax";
public static final String TAX_ID = "tax_id";
public static final String TAX_NUMBER = "tax_number";
public static final String TAX_ID = "tax_id";
public static final String TAX_NUMBER = "tax_number";
public static final String TAX_RATE = "tax_rate";
public static final String TEMPLATE = "template";
public static final String TEMPLATE_ID = "template_id";
public static final String TEMPLATE_ID = "template_id";
public static final String TEXT = "text";
public static final String THOUSANDS_SEPARATOR = "thousands_separator";
public static final String THEME = "theme";
public static final String TIME_ID = "time_id";
public static final String TIME_ID = "time_id";
public static final String TIMESTAMP = "timestamp";
public static final String TITLE = "title";
public static final String TO = "to";
public static final String TOKEN = "token";
public static final String TOKEN = "token";
public static final String TOTAL_PRIO = "total_prio";
public static final String TYPE = "type";
public static final String TYPE_ID = "type_id";
public static final String TYPE = "type";
public static final String TYPE_ID = "type_id";
public static final String UNIT = "unit";
public static final String UNIT_PRICE = "unit_price";
public static final String URI = "uri";
public static final String URL = "url";
public static final String USER = "user";
public static final String USERS = "users";
public static final String USER_ID = "user_id";
public static final String USER_LIST = "user_list";
public static final String URI = "uri";
public static final String URL = "url";
public static final String USER = "user";
public static final String USERS = "users";
public static final String USER_ID = "user_id";
public static final String USER_LIST = "user_list";
public static final String VALUE = "value";
public static final String VCARD = "vcard";
public static final String VERSION = "version";
public static final String VALUE = "value";
public static final String VCARD = "vcard";
public static final String VERSION = "version";
public static final String VERSIONS = "versions";
}
@@ -3,9 +3,9 @@ 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 PROJECT = "project";
public static final String TASK = "task";
public static final String USER = "user";
public static final String COMPANY = "company";
public static final String PROJECT = "project";
public static final String TASK = "task";
public static final String USER = "user";
}
@@ -4,46 +4,43 @@ package de.srsoftware.umbrella.core.constants;
public class Path {
private Path(){};
public static final String ADD = "add";
public static final String AVAILABLE = "available";
public static final String CSS = "css";
public static final String ADD = "add";
public static final String AVAILABLE = "available";
public static final String CSS = "css";
public static final String COMMON_TEMPLATES = "common_templates";
public static final String COMPANY = "company";
public static final String CONNECTED = "connected";
public static final String COMPANY = "company";
public static final String CONNECTED = "connected";
public static final String ITEM = "item";
public static final String JSON = "json";
public static final String LEGACY = "legacy";
public static final String LIST = "list";
public static final String LOCATION = "location";
public static final String LEGACY = "legacy";
public static final String LIST = "list";
public static final String LOCATION = "location";
public static final String LOCATIONS = "locations";
public static final String LOGIN = "login";
public static final String LOGIN = "login";
public static final String LOGOUT = "logout";
public static final String PAGE = "page";
public static final String PASSWORD = "password";
public static final String PROJECT = "project";
public static final String LOGOUT = "logout";
public static final String PAGE = "page";
public static final String PASSWORD = "password";
public static final String PROJECT = "project";
public static final String PROPERTIES = "properties";
public static final String PROPERTY = "property";
public static final String PROPERTY = "property";
public static final String REDIRECT = "redirect";
public static final String SEARCH = "search";
public static final String SERVICE = "service";
public static final String SEARCH = "search";
public static final String SETTINGS = "settings";
public static final String STATES = "states";
public static final String STARTED = "started";
public static final String STATE = "state";
public static final String STOP = "stop";
public static final String SUBMIT = "submit";
public static final String STATES = "states";
public static final String STARTED = "started";
public static final String STATE = "state";
public static final String STOP = "stop";
public static final String TAGGED = "tagged";
public static final String TOKEN = "token";
public static final String TOKEN = "token";
public static final String USER = "user";
public static final String USES = "uses";
public static final String VIEW = "view";
}
@@ -5,51 +5,62 @@ package de.srsoftware.umbrella.core.constants;
* This is a collection of messages that appear throughout the project
*/
public class Text {
public static final String BOOLEAN = "Boolean";
public static final String BOOKMARK = "bookmark";
public static final String COMPANIES = "companies";
public static final String COMPANY = "company";
public static final String COMPANY_WITH_ID = "company ({id})";
public static final String CONTACT = "Contact";
public static final String CONTACTS = "contacts";
public static final String CONTACT_WITH_ID = "contact ({id})";
public static final String CUSTOMER = "customer";
public static final String BOOLEAN = "Boolean";
public static final String COMPANIES = "companies";
public static final String COMPANY = "company";
public static final String COMPANY_WITH_ID = "company ({id})";
public static final String CONTACT = "Contact";
public static final String CONTACTS = "contacts";
public static final String CONTACT_WITH_ID = "contact ({id})";
public static final String CUSTOMER = "customer";
public static final String CUSTOMER_SETTINGS = "customer settings";
public static final String DOCUMENT = "document";
public static final String DOCUMENTS = "documents";
public static final String DOCUMENT = "document";
public static final String DOCUMENTS = "documents";
public static final String DOCUMENT_TYPE_ID = "document type id";
public static final String DOCUMENT_WITH_ID = "document ({id})";
public static final String INVALID_DB_CODE = "Encountered invalid dbCode: {code}";
public static final String ITEM = "item";
public static final String ITEMS = "items";
public static final String LOCATION = "location";
public static final String LOCATIONS = "locations";
public static final String ITEM = "item";
public static final String ITEMS = "items";
public static final String LOCATION = "location";
public static final String LOCATIONS = "locations";
public static final String LOGIN_SERVICE = "login service";
public static final String LONG = "Long";
public static final String NOTE = "note";
public static final String LONG = "Long";
public static final String NOTE = "note";
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 PROPERTIES = "properties";
public static final String NUMBER = "number";
public static final String PATH = "path";
public static final String PROJECT = "project";
public static final String PROJECT_WITH_ID = "project ({id})";
public static final String PROPERTY = "property";
public static final String SENDER = "sender";
public static final String SESSION = "session";
public static final String PROPERTIES = "properties";
public static final String PROPERTY = "property";
public static final String SENDER = "sender";
public static final String SERVICE_WITH_ID = "service ({id})";
public static final String SETTINGS = "settings";
public static final String STRING = "string";
public static final String SESSION = "session";
public static final String SETTINGS = "settings";
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 TIME_WITH_ID = "time ({id})";
public static final String TYPE = "type";
public static final String TAGS = "tags";
public static final String TASK = "task";
public static final String TASKS = "tasks";
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_PAGE = "wiki page";
public static final String WIKI_PAGE = "wiki page";
public static final String WIKI_PAGES = "wiki pages";
public static final String UNIT = "unit";
public static final String T_UNIT_PRICE = "unit price";
public static final String UNIT_PRICE = "unit price";
public static final String USER = "user";
public static final String USERS = "users";
+1
View File
@@ -0,0 +1 @@
*.db-journal
Binary file not shown.
Binary file not shown.
+68
View File
@@ -0,0 +1,68 @@
{
"umbrella": {
"base_url": "http://127.0.0.1:5173",
"logging": {
"rootLevel": "INFO"
},
"http": {
"port": 8080
},
"threads": 16,
"modules": {
"notes": {
"database": "demodata/notes.db"
},
"document": {
"database": "demodata/documents.db",
"templates": "demodata/templates"
},
"wiki": {
"database": "demodata/wiki.db"
},
"project": {
"database": "demodata/projects.db"
},
"message": {
"database": "demodata/message.db",
"smtp": {
"pass": "none",
"port": 587,
"host": "none",
"user": "none"
}
},
"tags": {
"database": "demodata/tags.db"
},
"bookmark": {
"database": "demodata/bookmark.db"
},
"task": {
"database": "demodata/tasks.db"
},
"journal": {
"database": "demodata/journal.db"
},
"contact": {
"database": "demodata/contacts.db"
},
"files": {
"database": "demodata/files.db",
"base_dir": "demodata/filestore"
},
"company": {
"database": "demodata/company.db"
},
"time": {
"database": "demodata/times.db"
},
"stock": {
"database": "demodata/stock.db"
},
"items": {},
"user": {
"database": "demodata/users.db"
}
}
}
}
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -24,6 +24,7 @@ import static de.srsoftware.umbrella.core.constants.Field.SENDER;
import static de.srsoftware.umbrella.core.constants.Field.STATE;
import static de.srsoftware.umbrella.core.constants.Field.TYPE;
import static de.srsoftware.umbrella.core.constants.Field.UNIT;
import static de.srsoftware.umbrella.core.constants.Field.UNIT_PRICE;
import static de.srsoftware.umbrella.core.constants.Field.USER;
import static de.srsoftware.umbrella.core.constants.Path.*;
import static de.srsoftware.umbrella.core.constants.Text.*;
@@ -540,6 +541,11 @@ public class DocumentApi extends BaseHandler implements DocumentService {
var userCompanyIds = companyService().listCompaniesOf(user).keySet();
var documents = db.find(userCompanyIds,keys,fulltext);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return sendContent(ex,mapValues(documents));
}
@@ -15,6 +15,7 @@ import static de.srsoftware.umbrella.core.constants.Field.NUMBER;
import static de.srsoftware.umbrella.core.constants.Field.SENDER;
import static de.srsoftware.umbrella.core.constants.Field.TYPE;
import static de.srsoftware.umbrella.core.constants.Field.UNIT;
import static de.srsoftware.umbrella.core.constants.Field.UNIT_PRICE;
import static de.srsoftware.umbrella.core.constants.Text.*;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.core.model.Document.DEFAULT_THOUSANDS_SEPARATOR;
+47 -33
View File
@@ -2,13 +2,14 @@
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { error, warn, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import { user } from '../../user.svelte';
const image_extensions = ['jpg','jpeg','gif','png','svg','webp'];
const router = useTinyRouter();
let children = $state({});
let new_dir = $state(null);
let files = $state();
let parent = $state(false);
@@ -17,23 +18,6 @@
let delete_allowed = $state(false);
let available = $derived.by(isAvailable);
function isAvailable(){
if (!new_dir) return false;
if (children){
if (children.dirs) {
for (let key of Object.values(children.dirs)){
if (key == new_dir) return false;
}
}
if (children.files) {
for (let key of Object.values(children.files)){
if (key == new_dir) return false;
}
}
}
return true;
}
async function create_dir(ev){
ev.preventDefault();
ev.stopPropagation();
@@ -74,13 +58,30 @@
async function handleDirectory(res){
let json = await res.json();
children.dirs = json.dirs ? json.dirs : {};
children.files = json.files ? json.files : {};
children.dirs = json.dirs ? val_sort(json.dirs) : {};
children.files = json.files ? val_sort(json.files) : {};
children.title = json.title ? json.title : path;
delete_allowed = json.delete;
yikes();
}
function isAvailable(){
if (!new_dir) return false;
if (children){
if (children.dirs) {
for (let key of Object.values(children.dirs)){
if (key == new_dir) return false;
}
}
if (children.files) {
for (let key of Object.values(children.files)){
if (key == new_dir) return false;
}
}
}
return true;
}
function is_image(file){
let parts = file.toLowerCase().split('.');
let ext = parts.pop();
@@ -90,12 +91,15 @@
async function loadChildren(p){
p = p.substring(6);
if (p == '') p = '/';
children = { dirs : {}, files : {}, title : p};
children = { dirs : [], files : [], title : p};
path = p;
if (p == '/'){
children.dirs[`/user/${user.id}`] = t('my_files');
children.dirs['/project'] = t('projects')
children.dirs['/company'] = t('companies');
children.dirs = [
{ path : `/user/${user.id}`, name : t('my files') },
{ path : '/project', name : t('projects')},
{ path : '/company', name : t('companies')},
]
parent = false;
form = false;
} else {
@@ -114,8 +118,12 @@
function markdown(file){
let parts = file.split('/');
let md = `![${parts.pop()}](/api/files${file})`;
let path = `/api/files${file}`;
path = encodeURI(path);
let md = `![${parts.pop()}](${path})`;
navigator.clipboard.writeText(md);
warn(t('Markdown has been copied to clipboard!'));
setTimeout(yikes, 2500);
}
function onclick(ev){
@@ -153,6 +161,12 @@
return false;
}
function val_sort(map){
return Object.entries(map)
.map(item => ({name:item[1],path:item[0]}))
.sort((a,b) => a.name.localeCompare(b.name));
}
onMount(() => loadChildren(window.location.pathname));
</script>
@@ -166,12 +180,12 @@
</li>
{/if}
{#if children?.dirs}
{#each Object.entries(children.dirs) as [k,v]}
{#each children.dirs as dir}
<li class="dir">
<span class="symbol"></span>
<a href={'/files'+k} {onclick}>{v}</a>
<a href={'/files'+dir.path} {onclick}>{dir.name}</a>
{#if delete_allowed}
<button class="symbol" onclick={e => dropDir(`/api/files${k}`,v)}></button>
<button class="symbol" onclick={e => dropDir(`/api/files${dir.path}`,dir.name)}></button>
{/if}
</li>
{/each}
@@ -186,15 +200,15 @@
</li>
{/if}
{#if children.files}
{#each Object.entries(children.files) as [k,v]}
{#each children.files as file}
<li class="file">
<span class="symbol"></span>
<a href={`/api/files${k}`} target="_blank">{v}</a>
{#if is_image(k)}
<button class="symbol" title={'markdown_code'} onclick={e => markdown(k)}></button>
<a href={`/api/files${file.path}`} target="_blank">{file.name}</a>
{#if is_image(file.path)}
<button class="symbol" title={'markdown_code'} onclick={e => markdown(file.path)}></button>
{/if}
{#if delete_allowed}
<button class="symbol" title={t('delete_object',{'object':t('file')})} onclick={e => dropFile(`/api/files${k}`,v)}></button>
<button class="symbol" title={t('delete_object',{'object':t('file')})} onclick={e => dropFile(`/api/files${file.path}`,file.name)}></button>
{/if}
</li>
{/each}
+62 -21
View File
@@ -1,14 +1,15 @@
<script>
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api, post, target } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { api, get, post, target } from '../../urls.svelte.js';
import { error, warn, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { display } from '../../time.svelte';
import Bookmark from '../bookmark/Template.svelte';
const router = useTinyRouter();
let counter = 9;
let bookmarks = $state(null);
let companies = $state(null);
let documents = $state(null);
@@ -34,12 +35,12 @@
});
function doSearch(ignored){
warn(t('searching…'));
let url = window.location.origin + window.location.pathname;
if (key) url += '?key=' + encodeURI(key);
window.history.replaceState(history.state, '', url);
const data = { key : key, fulltext : fulltext };
post(api('bookmark/search'),data).then(handleBookmarks);
post(api('company/search '),data).then(handleCompanies);
post(api('document/search'),data).then(handleDocuments);
@@ -51,6 +52,22 @@
post(api('wiki/search' ),data).then(handleWikiPages);
}
async function getTitle(key,module,entity_id){
get(api(module+'/'+entity_id)).then(res => setTitle(res,key,module))
}
async function setTitle(resp,key,module){
if (resp.ok){
const json = await resp.json();
if (json.name) notes[key].title = t(module)+": "+json.name;
if (json.title) notes[key].title = t(module)+": "+json.title;
if (module == 'document'){
notes[key].title = t(json.type)+" "+json.number;
}
}
}
function onclick(e){
e.preventDefault();
var target = e.target;
@@ -61,6 +78,7 @@
}
async function handleBookmarks(resp){
quitOne();
if (resp.ok){
const res = await resp.json();
bookmarks = Object.keys(res).length ? res : null;
@@ -70,6 +88,7 @@
}
async function handleCompanies(resp){
quitOne();
if (resp.ok){
const json = await resp.json();
companies = Object.keys(json).length ? json : null;
@@ -79,6 +98,7 @@
}
async function handleDocuments(resp){
quitOne();
if (resp.ok){
const json = await resp.json();
documents = Object.keys(json).length ? json : null;
@@ -88,15 +108,25 @@
}
async function handleNotes(resp){
quitOne();
if (resp.ok){
const json = await resp.json();
notes = Object.keys(json).length ? json : null;
if ( Object.keys(json).length ) {
for (let key of Object.keys(json)){
let module = json[key].module;
let entity_id = json[key].entity_id;
json[key].title = t(module)+' '+entity_id;
getTitle(key,module,entity_id);
}
notes = json;
} else notes = null;
} else {
error(resp);
}
}
async function handleProjects(resp){
quitOne();
if (resp.ok){
const res = await resp.json();
projects = Object.keys(res).length ? res : null;
@@ -106,6 +136,7 @@
}
async function handleStock(resp){
quitOne();
if (resp.ok){
const res = await resp.json();
stock = Object.keys(res).length ? res : null;
@@ -115,6 +146,7 @@
}
async function handleTasks(resp){
quitOne();
if (resp.ok){
const res = await resp.json();
tasks = Object.keys(res).length ? res : null;
@@ -124,6 +156,7 @@
}
async function handleTimes(resp){
quitOne();
if (resp.ok){
const res = await resp.json();
times = Object.keys(res).length ? res : null;
@@ -133,6 +166,7 @@
}
async function handleWikiPages(resp){
quitOne();
if (resp.ok){
const res = await resp.json();
pages = Object.keys(res).length ? res : null;
@@ -140,6 +174,13 @@
error(resp);
}
}
function quitOne(){
counter--;
if (counter > 0) {
warn(t('searching…')+" "+counter);
} else yikes();
}
$effect(() => doSearch(key))
</script>
@@ -236,23 +277,6 @@
</ul>
</fieldset>
{/if}
{#if notes}
<fieldset>
<legend>
{t('notes')}
</legend>
<ul>
{#each Object.values(notes) as note}
<li>
<b>
<a href="/{note.module}/{note.entity_id}/view" {onclick} >{t(note.module)} {note.entity_id}:</a>
</b>
{@html target(note.text.rendered)}
</li>
{/each}
</ul>
</fieldset>
{/if}
{#if times}
<fieldset>
<legend>
@@ -308,3 +332,20 @@
</ul>
</fieldset>
{/if}
{#if notes}
<fieldset>
<legend>
{t('notes')}
</legend>
<ul>
{#each Object.values(notes) as note}
<li>
<b>
<a href="/{note.module}/{note.entity_id}/view" {onclick} >{note.title}</a>
</b>
{@html target(note.text.rendered)}
</li>
{/each}
</ul>
</fieldset>
{/if}
+1 -1
View File
@@ -15,5 +15,5 @@ export async function warn(msg){
export function yikes(){
messages.error = null;
messages.warn = null;
messages.warning = null;
}
@@ -7,8 +7,8 @@ import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.ModuleRegistry.companyService;
import static de.srsoftware.umbrella.core.ModuleRegistry.translator;
import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.constants.Text.T_UNIT_PRICE;
import static de.srsoftware.umbrella.core.constants.Text.UNIT;
import static de.srsoftware.umbrella.core.constants.Text.UNIT_PRICE;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException;
import static de.srsoftware.umbrella.stock.Constants.TABLE_ITEMS;
import static java.lang.System.Logger.Level.DEBUG;
@@ -75,7 +75,7 @@ public class ItemDb {
LOG.log(DEBUG, " using location: {0}",location.resolve().name());
var stockItem = new Item(0,company,0,location,code,name,description);
var props = stockItem.properties();
var keyUnitPrice = translator().translate(lang,T_UNIT_PRICE);
var keyUnitPrice = translator().translate(lang, Text.UNIT_PRICE);
var keyUnit = translator().translate(lang, Text.UNIT);
var keyTax = translator().translate(lang,TAX_RATE);
var keyLegacyId = translator().translate(lang,"legacy_id");
@@ -132,7 +132,23 @@ public class StockModule extends BaseHandler implements StockService {
yield super.doGet(path,ex);
}
}
case null, default -> super.doGet(path,ex);
case null -> super.doGet(path,ex);
default -> {
try {
var id = Long.parseLong(head);
Item item = stockDb.loadItem(id);
Owner owner = item.owner().resolve();
if (owner instanceof Company company) {
if (!companyService().membership(company.id(),user.get().id())) throw forbidden("You are not allowed to access {0}",OBJECT);
}
if (owner instanceof UmbrellaUser u){
if (u.id() != user.get().id()) throw forbidden("You are not allowed to access {0}",OBJECT);
}
yield sendContent(ex,item);
} catch (NumberFormatException nfe){
yield super.doGet(path,ex);
}
}
};
} catch (UmbrellaException e){
return send(ex,e);
@@ -39,6 +39,7 @@ public class SqliteDb extends BaseDb implements TagDB{
public SqliteDb(Connection tagDb, Connection bmDb) {
super(tagDb);
bookmarks = new de.srsoftware.umbrella.bookmarks.SqliteDb(bmDb);
createTables();
}
@Override
@@ -40,7 +40,6 @@ public class Translations extends PathHandler implements Translator {
}
private JSONObject loadTranslations(String lang) throws IOException {
LOG.log(WARNING,"loadTranslations({0}) not implemented!",lang);
var filename = lang + ".json";
URL url = getClass().getClassLoader().getResource(filename);
if (url == null) return new JSONObject();
+26
View File
@@ -14,6 +14,7 @@
"basic_data": "Basis-Daten",
"bookmark": "Lesezeichen",
"bookmarks": "Lesezeichen",
"Boolean": "Boolean",
"by": "von",
"cancel": "abbrechen",
@@ -30,6 +31,7 @@
"confirm_state": "Status wirklich ändern?",
"companies": "Firmen",
"company": "Firma",
"company ({id})": "Firma ({id})",
"company_optional": "Firma (optional)",
"confirmation": "Bestätigung",
"complete": "abschließen",
@@ -53,6 +55,7 @@
"customer_address": "Adresse",
"customer_email": "Emailadresse des Kunden",
"customer_id": "Kundennummer",
"customer settings": "Kunden-Einstellungen",
"custom_tag_colors": "Nutzerdefinierte Tag-Farben",
"data_sent": "Daten übermittelt",
@@ -72,6 +75,8 @@
"document": "Dokument",
"document_list": "Dokumente",
"documents": "Dokumente",
"document type id": "Dokumenten-Typ-ID",
"document ({id})": "Dokument ({id})",
"do_login" : "anmelden",
"do_open" : "öffnen",
"do_send" : "versenden",
@@ -87,6 +92,7 @@
"edit_service": "Login-Service \"{name}\" bearbeiten",
"email": "E-Mail",
"email_or_username": "Email oder Nutzername",
"Encountered invalid dbCode: {code}": "Ungültiger dbCode aufgetreten: {code}",
"end": "Ende",
"estimated_time": "geschätzte Zeit",
"estimated_times": "geschätzte Zeiten",
@@ -167,12 +173,16 @@
"local_court": "Amtsgericht",
"locality": "Ort",
"location": "Ort",
"locations": "Orte",
"login" : "Anmeldung",
"login service": "Login-Service",
"login_services": "Login-Services",
"logout": "Abmelden",
"logout_user": "{user} abmelden",
"Long": "Ganzzahl",
"markdown_code": "Markdown-Code",
"Markdown has been copied to clipboard!": "Markdown wurde in die Zwischenablage kopiert!",
"markdown_supported": "Markdown & <a target=\"_blank\" href=\"https://plantuml.com\">Plantuml</a> nutzbar!",
"MANAGE_LOGIN_SERVICES": "Login-Services verwalten",
"member": "Mitarbeiter",
@@ -205,6 +215,7 @@
"month": "Monat",
"move_to_top": "nach ganz oben bewegen",
"must_not_be_empty": "darf nicht leer sein",
"my files": "Meine Dateien",
"name": "Name",
"net_price": "Nettopreis",
@@ -218,6 +229,7 @@
"no_project_for_id": "Kein Projekt mit ID {0} gefunden!",
"no_task_for_id": "Keine Aufgabe mit ID {0} gefunden!",
"note": "Notiz",
"note ({id})": "Notiz ({id})",
"notes": "Notizen",
"not_recent_version": "Die ist nicht die neuste Version dieser Seite!",
"number": "Nummer",
@@ -230,6 +242,7 @@
"page": "Seite",
"parent_task": "übergeordnete Aufgabe",
"password" : "Passwort",
"path": "Pfad",
"permission": {
"EDIT": "lesen/schreiben",
"OWNER": "Besitzer"
@@ -250,7 +263,10 @@
"priority": "Priorität",
"processing_code": "Code wird verarbeitet…",
"project": "Projekt",
"project ({id})": "Projekt ({id})",
"projects": "Projekte",
"properties": "Eigenschaften",
"property": "Eigenschaft",
"record": "Eintrag",
"region": "Bundesland",
@@ -262,6 +278,7 @@
"saved": "gespeichert",
"save_object": "{object} speichern",
"search": "Suche",
"searching…": "suche…",
"select_company" : "Wählen Sie eine ihrer Firmen:",
"select_customer": "Kunde auswählen",
"select_property": "Eigenschaft auswählen",
@@ -274,6 +291,8 @@
"sender_tax_id": "Steuernummer",
"sent_email": "Email gesendet",
"service": "Service",
"service ({id})": "Service ({id})",
"session": "Sitzung",
"settings" : "Einstellungen",
"share_with": "Teilen mit:",
"show": "anzeigen",
@@ -301,12 +320,14 @@
},
"stock": "Inventar",
"street": "Straße",
"string": "Text",
"subject": "Betreff",
"subtask": "Unteraufgabe",
"subtasks": "Unteraufgaben",
"succeeding_document": "Nachfolge-Dokument",
"sum_of_records": "Summe der ausgewählten Einträge",
"table {name}": "Tabelle {name}",
"tag_name": "Tag-Name",
"tag_uses": "Verwendung des Tags „{tag}“",
"tags": "Tags",
@@ -319,6 +340,7 @@
"tax_rate": "Steuersatz",
"template": "Vorlage",
"theme": "Design",
"time ({id})": "Zeit ({id})",
"times": "Zeiten",
"timetracking": "Zeiterfassung",
"title_not_available": "„{title}“ ist als Seitenname nicht mehr verfügbar!",
@@ -333,6 +355,7 @@
"unexpected_item_id_format": "Alte Artikel-ID sollte die Form tt:zz:zz haben, habe aber {0} gefunden!",
"unit": "Einheit",
"unit price": "Preis/Einheit",
"unit_price": "Preis/Einheit",
"unknown_item_location": "Artikel {0} von {1} {2} ist verknüpft mit unbekanntem Lagerort {3}!",
"unlink": "Trennen",
@@ -340,6 +363,7 @@
"UPDATE_USERS" : "Nutzer aktualisieren",
"upload_file": "Datei hochladen",
"user": "Benutzer",
"user ({id})": "Benutzer ({id})",
"user_list": "Benutzer-Liste",
"user_module" : "Umbrella User-Verwaltung",
"users": "Benutzer",
@@ -351,6 +375,8 @@
"welcome" : "Willkommen, {0}",
"wiki": "Wiki",
"wikis": "Wiki-Seiten",
"wiki page": "Wiki-Seite",
"wiki pages": "Wiki-Seiten",
"wiki_pages": "Wiki-Seiten",
"value": "Wert",
+30 -1
View File
@@ -14,6 +14,7 @@
"basic_data": "basic data",
"bookmark": "bookmark",
"bookmarks": "bookmarks",
"Boolean": "Boolean",
"by": "by",
"cancel": "cancel",
@@ -30,6 +31,7 @@
"confirm_state": "Really change state?",
"companies": "companies",
"company": "company",
"company ({id})": "company ({id})",
"company_optional": "company (optional)",
"confirmation": "confirmation",
"complete": "complete",
@@ -53,6 +55,7 @@
"customer_address": "address",
"customer_email": "customer email address",
"customer_id": "customer ID",
"customer settings": "customer settings",
"custom_tag_colors": "custom tag colors",
"data_sent": "data sent",
@@ -72,6 +75,8 @@
"document": "document",
"document_list": "document list",
"documents": "documents",
"document type id": "document type id",
"document ({id})":"document ({id})",
"do_login" : "do login",
"do_open" : "open",
"do_send" : "send",
@@ -87,6 +92,7 @@
"edit_service": "edit login service \"{name}\"",
"email": "email",
"email_or_username": "email or username",
"Encountered invalid dbCode: {code}": "Encountered invalid dbCode: {code}",
"end": "end",
"estimated_time": "estimated duration",
"estimated_times": "estimated durations",
@@ -167,12 +173,16 @@
"local_court": "local court",
"locality": "locality",
"location": "location",
"locations": "locations",
"login" : "login",
"login service": "login service",
"login_services": "login service",
"logout": "logout",
"logout_user": "logout {user}",
"Long": "Long",
"markdown_code": "Markdown-Code",
"Markdown has been copied to clipboard!": "Markdown has been copied to clipboard!",
"markdown_supported": "Markdown & <a target=\"_blank\" href=\"https://plantuml.com\">Plantuml</a> supported!",
"MANAGE_LOGIN_SERVICES": "manage login services",
"member": "member",
@@ -205,6 +215,7 @@
"month": "month",
"move_to_top": "move to top level",
"must_not_be_empty": "must not be empty",
"my files": "my files",
"name": "Name",
"net_price": "net price",
@@ -218,6 +229,7 @@
"no_project_for_id": "No project found for id {0}",
"no_task_for_id": "No task found for id {0}",
"note": "note",
"note ({id})":"note ({id})",
"notes": "notes",
"not_recent_version": "This is not the current version of this page!",
"number": "number",
@@ -230,6 +242,7 @@
"page": "page",
"parent_task": "parent task",
"password" : "password",
"path": "path",
"permission": {
"EDIT": "read/write",
"OWNER": "owner"
@@ -250,7 +263,10 @@
"priority": "priority",
"processing_code": "processing code…",
"project": "project",
"project ({id})": "project ({id})",
"projects": "projects",
"properties": "properties",
"property": "property",
"record": "record",
"region": "region",
@@ -262,6 +278,7 @@
"saved": "saved",
"save_object": "save {object}",
"search": "search",
"searching…": "searhcing…",
"select_company" : "select on of you companies:",
"select_customer": "select customer",
"select_property": "select property",
@@ -274,6 +291,8 @@
"sender_tax_id": "tax ID",
"sent_email": "email sent",
"service": "service",
"service ({id})": "service ({id})",
"session": "session",
"settings" : "settings",
"share_with": "share with:",
"show": "show",
@@ -301,12 +320,14 @@
},
"stock": "stock",
"street": "street",
"string": "string",
"subject": "subject",
"subtask": "subtask",
"subtasks": "subtasks",
"succeeding_document": "succeeding document",
"sum_of_records": "sum of records",
"table {name}": "table {name}",
"tag_name": "tag name",
"tag_uses": "usage of tag „{tag}“",
"tags": "tags",
@@ -319,13 +340,14 @@
"tax_rate": "tax rate",
"template": "template",
"theme": "design",
"time ({id})": "time ({id})",
"times": "times",
"timetracking": "time tracking",
"title_not_available": "„{title}“ is not available as page name!",
"title_or_desc": "title/description",
"toggle_objects": "toggle {objects}",
"tutorial": "tutorial",
"type": "document type",
"type": "type",
"type_confirmation": "confirmation",
"type_invoice": "invoice",
"type_offer": "offer",
@@ -333,6 +355,7 @@
"unexpected_item_id_format": "Expected old item ID to be of the form ss:dd:dd, encountered {0}!",
"unit": "unit",
"unit price": "unit price",
"unit_price": "price/unit",
"unknown_item_location": "Item {0} of {1} {2} refers to location {3}, which is unknown!",
"unlink": "unlink",
@@ -340,14 +363,20 @@
"UPDATE_USERS" : "update users",
"upload_file": "upload file",
"user": "user",
"user ({id})": "user ({id})",
"user_list": "user list",
"user_module" : "Umbrella user management",
"users": "users",
"user_created_entity": "{user} created \"{entity}\"",
"user_deleted_entity": "{user} deleted \"{entity}\"",
"user_updated_entity": "{user} updated \"{entity}\"",
"website": "website",
"welcome" : "Welcome, {0}",
"wiki": "Wiki",
"wikis": "wiki pages",
"wiki page": "wiki page",
"wiki pages": "wiki pages",
"wiki_pages": "wiki pages",
"value": "value",
+2 -1
View File
@@ -9,4 +9,5 @@ tasks.processResources {
from("../frontend/dist") {
into("web")
}
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
+2 -1
View File
@@ -57,8 +57,9 @@ footer {
margin: 5px;
}
img {
img, svg {
max-width: 100%;
height: auto !important;
}
nav {
+2 -1
View File
@@ -57,8 +57,9 @@ footer {
margin: 5px;
}
img {
img, svg {
max-width: 100%;
height: auto !important;
}
nav {
+8 -7
View File
@@ -57,17 +57,18 @@ footer {
margin: 5px;
}
img {
img, svg {
max-width: 100%;
height: auto !important;
}
nav {
position: sticky;
z-index: 100;
top: 0;
padding: 5px;
margin: 0 0 10px 0;
border-bottom: 1px solid;
position: sticky;
z-index: 100;
top: 0;
padding: 5px;
margin: 0 0 10px 0;
border-bottom: 1px solid;
}
td, tr{
@@ -152,15 +152,27 @@ public class SqliteDb extends BaseDb implements WikiDb {
@Override
public Map<Long, WikiPage> find(long userId, List<String> keys, boolean fulltext) {
try {
var query = select(ALL).from(TABLE_PAGES).leftJoin(ID,TABLE_PAGES_USERS,PAGE_ID).where(USER_ID,equal(userId));
for (var key : keys) query.where(TITLE,like("%"+key+"%"));
var rs = query.exec(db);
var map = new HashMap<Long,WikiPage>();
while (rs.next()) {
var page = WikiPage.of(rs);
map.put(page.id(),page);
{
var query = select(ALL).from(TABLE_PAGES).leftJoin(ID, TABLE_PAGES_USERS, PAGE_ID).where(USER_ID, equal(userId));
for (var key : keys) query.where(TITLE, like("%" + key.replaceAll("[ÄäÖöÜüß]", "%") + "%"));
var rs = query.exec(db);
while (rs.next()) {
var page = WikiPage.of(rs);
map.put(page.id(), page);
}
rs.close();
}
if (fulltext) {
var query = select(ALL).from(TABLE_PAGES).leftJoin(ID, TABLE_PAGES_USERS, PAGE_ID).where(USER_ID, equal(userId));
for (var key : keys) query.where(CONTENT, like("%" + key.replaceAll("[ÄäÖöÜüß]", "%") + "%"));
var rs = query.exec(db);
while (rs.next()) {
var page = WikiPage.of(rs);
map.put(page.id(), page);
}
rs.close();
}
rs.close();
return map;
} catch (SQLException e) {
throw failedToSearchDb(t(WIKI_PAGES)).causedBy(e);