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 FROM alpine:3.22 AS svelte_build
RUN apk add npm RUN apk add npm
RUN adduser -Dh /home/svelte svelte RUN adduser -Dh /home/svelte svelte
ADD . /home/svelte/Umbrella ADD frontend /home/svelte/Umbrella/frontend
RUN chown -R svelte /home/svelte/Umbrella RUN chown -R svelte /home/svelte/Umbrella
USER svelte USER svelte
WORKDIR /home/svelte/Umbrella/frontend WORKDIR /home/svelte/Umbrella/frontend
RUN npm install && npm run build RUN npm install && npm run build
FROM alpine AS java_build FROM alpine:3.22 AS java_build
RUN apk add gradle fontconfig font-opensans openjdk21-jre RUN apk add gradle
ADD . /Umbrella ADD . /Umbrella
WORKDIR /Umbrella WORKDIR /Umbrella
COPY --from=svelte_build /home/svelte/Umbrella/frontend/dist web/src/main/resources/web 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 FROM alpine
RUN apk add bash fontconfig font-opensans graphviz openjdk21-jre weasyprint RUN apk --no-cache add bash fontconfig font-opensans graphviz openjdk21-jre tzdata weasyprint \
RUN adduser -D umbrella && 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
WORKDIR /home/umbrella WORKDIR /home/umbrella
RUN mkdir .config && ln -s /host/config.json .config/Umbrella.json
EXPOSE 80 EXPOSE 80
CMD java -jar jar/backend.jar 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 FROM alpine:3.22
LABEL Maintainer "Stephan Richter <s.richter@srsoftware.de>" LABEL Maintainer "Stephan Richter"
ARG UID=1000 ARG UID=1000
ARG GID=1000 ARG GID=1000
RUN apk add bash npm RUN apk add bash npm
@@ -35,11 +35,12 @@ import org.json.JSONObject;
public class Util { public class Util {
public static final System.Logger LOG = System.getLogger("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 File plantumlJar = null;
private static final JParsedown MARKDOWN = new JParsedown(); private static final JParsedown MARKDOWN = new JParsedown();
public static final String SHA1 = "SHA-1"; public static final String SHA1 = "SHA-1";
private static final MessageDigest SHA1_DIGEST; private static final MessageDigest SHA1_DIGEST;
private static final Map<Integer,String> umlCache = new HashMap<>();
static { static {
try { try {
@@ -79,11 +80,22 @@ public class Util {
try { try {
if (plantumlJar != null && plantumlJar.exists()) { if (plantumlJar != null && plantumlJar.exists()) {
var matcher = UML_PATTERN.matcher(source); var matcher = UML_PATTERN.matcher(source);
if (matcher.find()) { while (matcher.find()) {
var uml = matcher.group(0).trim(); var uml = matcher.group(0).trim();
var start = matcher.start(0); var start = matcher.start(0);
var end = matcher.end(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"); ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", plantumlJar.getAbsolutePath(), "-tsvg", "-pipe");
var ignored = processBuilder.redirectErrorStream(); var ignored = processBuilder.redirectErrorStream();
var process = processBuilder.start(); var process = processBuilder.start();
@@ -94,8 +106,11 @@ public class Util {
try (InputStream is = process.getInputStream()) { try (InputStream is = process.getInputStream()) {
byte[] out = is.readAllBytes(); 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); source = source.substring(0, start) + svg + source.substring(end);
matcher = UML_PATTERN.matcher(source);
} }
} }
} }
@@ -31,19 +31,16 @@ public class Path {
public static final String REDIRECT = "redirect"; public static final String REDIRECT = "redirect";
public static final String SEARCH = "search"; public static final String SEARCH = "search";
public static final String SERVICE = "service";
public static final String SETTINGS = "settings"; public static final String SETTINGS = "settings";
public static final String STATES = "states"; public static final String STATES = "states";
public static final String STARTED = "started"; public static final String STARTED = "started";
public static final String STATE = "state"; public static final String STATE = "state";
public static final String STOP = "stop"; public static final String STOP = "stop";
public static final String SUBMIT = "submit";
public static final String TAGGED = "tagged"; 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 USER = "user";
public static final String USES = "uses"; public static final String USES = "uses";
public static final String VIEW = "view";
} }
@@ -5,8 +5,9 @@ package de.srsoftware.umbrella.core.constants;
* This is a collection of messages that appear throughout the project * This is a collection of messages that appear throughout the project
*/ */
public class Text { public class Text {
public static final String BOOLEAN = "Boolean";
public static final String BOOKMARK = "bookmark"; public static final String BOOKMARK = "bookmark";
public static final String BOOLEAN = "Boolean";
public static final String COMPANIES = "companies"; public static final String COMPANIES = "companies";
public static final String COMPANY = "company"; public static final String COMPANY = "company";
public static final String COMPANY_WITH_ID = "company ({id})"; public static final String COMPANY_WITH_ID = "company ({id})";
@@ -15,41 +16,51 @@ public class Text {
public static final String CONTACT_WITH_ID = "contact ({id})"; public static final String CONTACT_WITH_ID = "contact ({id})";
public static final String CUSTOMER = "customer"; public static final String CUSTOMER = "customer";
public static final String CUSTOMER_SETTINGS = "customer settings"; public static final String CUSTOMER_SETTINGS = "customer settings";
public static final String DOCUMENT = "document"; public static final String DOCUMENT = "document";
public static final String DOCUMENTS = "documents"; public static final String DOCUMENTS = "documents";
public static final String DOCUMENT_TYPE_ID = "document type id"; public static final String DOCUMENT_TYPE_ID = "document type id";
public static final String DOCUMENT_WITH_ID = "document ({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 INVALID_DB_CODE = "Encountered invalid dbCode: {code}";
public static final String ITEM = "item"; public static final String ITEM = "item";
public static final String ITEMS = "items"; public static final String ITEMS = "items";
public static final String LOCATION = "location"; public static final String LOCATION = "location";
public static final String LOCATIONS = "locations"; public static final String LOCATIONS = "locations";
public static final String LOGIN_SERVICE = "login service"; public static final String LOGIN_SERVICE = "login service";
public static final String LONG = "Long"; public static final String LONG = "Long";
public static final String NOTE = "note"; public static final String NOTE = "note";
public static final String NOTE_WITH_ID = "note ({id})"; public static final String NOTE_WITH_ID = "note ({id})";
public static final String NUMBER = "number"; public static final String NUMBER = "number";
public static final String PATH = "path"; public static final String PATH = "path";
public static final String PROJECT = "project"; public static final String PROJECT = "project";
public static final String PROPERTIES = "properties";
public static final String PROJECT_WITH_ID = "project ({id})"; public static final String PROJECT_WITH_ID = "project ({id})";
public static final String PROPERTIES = "properties";
public static final String PROPERTY = "property"; public static final String PROPERTY = "property";
public static final String SENDER = "sender"; public static final String SENDER = "sender";
public static final String SESSION = "session";
public static final String SERVICE_WITH_ID = "service ({id})"; 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 SETTINGS = "settings";
public static final String STRING = "string"; public static final String STRING = "string";
public static final String TABLE_WITH_NAME = "table {name}"; public static final String TABLE_WITH_NAME = "table {name}";
public static final String TAGS = "tags"; public static final String TAGS = "tags";
public static final String TASK = "task"; public static final String TASK = "task";
public static final String TASKS = "tasks"; public static final String TASKS = "tasks";
public static final String TIME_WITH_ID = "time ({id})"; public static final String TIME_WITH_ID = "time ({id})";
public static final String TYPE = "type"; 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 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 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 USER = "user";
public static final String USERS = "users"; 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.STATE;
import static de.srsoftware.umbrella.core.constants.Field.TYPE; 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;
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.Field.USER;
import static de.srsoftware.umbrella.core.constants.Path.*; import static de.srsoftware.umbrella.core.constants.Path.*;
import static de.srsoftware.umbrella.core.constants.Text.*; 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 userCompanyIds = companyService().listCompaniesOf(user).keySet();
var documents = db.find(userCompanyIds,keys,fulltext); var documents = db.find(userCompanyIds,keys,fulltext);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return sendContent(ex,mapValues(documents)); 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.SENDER;
import static de.srsoftware.umbrella.core.constants.Field.TYPE; 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;
import static de.srsoftware.umbrella.core.constants.Field.UNIT_PRICE;
import static de.srsoftware.umbrella.core.constants.Text.*; import static de.srsoftware.umbrella.core.constants.Text.*;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.core.model.Document.DEFAULT_THOUSANDS_SEPARATOR; import static de.srsoftware.umbrella.core.model.Document.DEFAULT_THOUSANDS_SEPARATOR;
+47 -33
View File
@@ -2,13 +2,14 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router'; import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte'; import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte'; import { error, warn, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte'; import { t } from '../../translations.svelte';
import { user } from '../../user.svelte'; import { user } from '../../user.svelte';
const image_extensions = ['jpg','jpeg','gif','png','svg','webp']; const image_extensions = ['jpg','jpeg','gif','png','svg','webp'];
const router = useTinyRouter(); const router = useTinyRouter();
let children = $state({}); let children = $state({});
let new_dir = $state(null); let new_dir = $state(null);
let files = $state(); let files = $state();
let parent = $state(false); let parent = $state(false);
@@ -17,23 +18,6 @@
let delete_allowed = $state(false); let delete_allowed = $state(false);
let available = $derived.by(isAvailable); 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){ async function create_dir(ev){
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
@@ -74,13 +58,30 @@
async function handleDirectory(res){ async function handleDirectory(res){
let json = await res.json(); let json = await res.json();
children.dirs = json.dirs ? json.dirs : {}; children.dirs = json.dirs ? val_sort(json.dirs) : {};
children.files = json.files ? json.files : {}; children.files = json.files ? val_sort(json.files) : {};
children.title = json.title ? json.title : path; children.title = json.title ? json.title : path;
delete_allowed = json.delete; delete_allowed = json.delete;
yikes(); 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){ function is_image(file){
let parts = file.toLowerCase().split('.'); let parts = file.toLowerCase().split('.');
let ext = parts.pop(); let ext = parts.pop();
@@ -90,12 +91,15 @@
async function loadChildren(p){ async function loadChildren(p){
p = p.substring(6); p = p.substring(6);
if (p == '') p = '/'; if (p == '') p = '/';
children = { dirs : {}, files : {}, title : p}; children = { dirs : [], files : [], title : p};
path = p; path = p;
if (p == '/'){ if (p == '/'){
children.dirs[`/user/${user.id}`] = t('my_files'); children.dirs = [
children.dirs['/project'] = t('projects') { path : `/user/${user.id}`, name : t('my files') },
children.dirs['/company'] = t('companies'); { path : '/project', name : t('projects')},
{ path : '/company', name : t('companies')},
]
parent = false; parent = false;
form = false; form = false;
} else { } else {
@@ -114,8 +118,12 @@
function markdown(file){ function markdown(file){
let parts = file.split('/'); 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); navigator.clipboard.writeText(md);
warn(t('Markdown has been copied to clipboard!'));
setTimeout(yikes, 2500);
} }
function onclick(ev){ function onclick(ev){
@@ -153,6 +161,12 @@
return false; 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)); onMount(() => loadChildren(window.location.pathname));
</script> </script>
@@ -166,12 +180,12 @@
</li> </li>
{/if} {/if}
{#if children?.dirs} {#if children?.dirs}
{#each Object.entries(children.dirs) as [k,v]} {#each children.dirs as dir}
<li class="dir"> <li class="dir">
<span class="symbol"></span> <span class="symbol"></span>
<a href={'/files'+k} {onclick}>{v}</a> <a href={'/files'+dir.path} {onclick}>{dir.name}</a>
{#if delete_allowed} {#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} {/if}
</li> </li>
{/each} {/each}
@@ -186,15 +200,15 @@
</li> </li>
{/if} {/if}
{#if children.files} {#if children.files}
{#each Object.entries(children.files) as [k,v]} {#each children.files as file}
<li class="file"> <li class="file">
<span class="symbol"></span> <span class="symbol"></span>
<a href={`/api/files${k}`} target="_blank">{v}</a> <a href={`/api/files${file.path}`} target="_blank">{file.name}</a>
{#if is_image(k)} {#if is_image(file.path)}
<button class="symbol" title={'markdown_code'} onclick={e => markdown(k)}></button> <button class="symbol" title={'markdown_code'} onclick={e => markdown(file.path)}></button>
{/if} {/if}
{#if delete_allowed} {#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} {/if}
</li> </li>
{/each} {/each}
+62 -21
View File
@@ -1,14 +1,15 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router'; import { useTinyRouter } from 'svelte-tiny-router';
import { api, post, target } from '../../urls.svelte.js'; import { api, get, post, target } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte'; import { error, warn, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js'; import { t } from '../../translations.svelte.js';
import { display } from '../../time.svelte'; import { display } from '../../time.svelte';
import Bookmark from '../bookmark/Template.svelte'; import Bookmark from '../bookmark/Template.svelte';
const router = useTinyRouter(); const router = useTinyRouter();
let counter = 9;
let bookmarks = $state(null); let bookmarks = $state(null);
let companies = $state(null); let companies = $state(null);
let documents = $state(null); let documents = $state(null);
@@ -34,12 +35,12 @@
}); });
function doSearch(ignored){ function doSearch(ignored){
warn(t('searching…'));
let url = window.location.origin + window.location.pathname; let url = window.location.origin + window.location.pathname;
if (key) url += '?key=' + encodeURI(key); if (key) url += '?key=' + encodeURI(key);
window.history.replaceState(history.state, '', url); window.history.replaceState(history.state, '', url);
const data = { key : key, fulltext : fulltext }; const data = { key : key, fulltext : fulltext };
post(api('bookmark/search'),data).then(handleBookmarks); post(api('bookmark/search'),data).then(handleBookmarks);
post(api('company/search '),data).then(handleCompanies); post(api('company/search '),data).then(handleCompanies);
post(api('document/search'),data).then(handleDocuments); post(api('document/search'),data).then(handleDocuments);
@@ -51,6 +52,22 @@
post(api('wiki/search' ),data).then(handleWikiPages); 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){ function onclick(e){
e.preventDefault(); e.preventDefault();
var target = e.target; var target = e.target;
@@ -61,6 +78,7 @@
} }
async function handleBookmarks(resp){ async function handleBookmarks(resp){
quitOne();
if (resp.ok){ if (resp.ok){
const res = await resp.json(); const res = await resp.json();
bookmarks = Object.keys(res).length ? res : null; bookmarks = Object.keys(res).length ? res : null;
@@ -70,6 +88,7 @@
} }
async function handleCompanies(resp){ async function handleCompanies(resp){
quitOne();
if (resp.ok){ if (resp.ok){
const json = await resp.json(); const json = await resp.json();
companies = Object.keys(json).length ? json : null; companies = Object.keys(json).length ? json : null;
@@ -79,6 +98,7 @@
} }
async function handleDocuments(resp){ async function handleDocuments(resp){
quitOne();
if (resp.ok){ if (resp.ok){
const json = await resp.json(); const json = await resp.json();
documents = Object.keys(json).length ? json : null; documents = Object.keys(json).length ? json : null;
@@ -88,15 +108,25 @@
} }
async function handleNotes(resp){ async function handleNotes(resp){
quitOne();
if (resp.ok){ if (resp.ok){
const json = await resp.json(); 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 { } else {
error(resp); error(resp);
} }
} }
async function handleProjects(resp){ async function handleProjects(resp){
quitOne();
if (resp.ok){ if (resp.ok){
const res = await resp.json(); const res = await resp.json();
projects = Object.keys(res).length ? res : null; projects = Object.keys(res).length ? res : null;
@@ -106,6 +136,7 @@
} }
async function handleStock(resp){ async function handleStock(resp){
quitOne();
if (resp.ok){ if (resp.ok){
const res = await resp.json(); const res = await resp.json();
stock = Object.keys(res).length ? res : null; stock = Object.keys(res).length ? res : null;
@@ -115,6 +146,7 @@
} }
async function handleTasks(resp){ async function handleTasks(resp){
quitOne();
if (resp.ok){ if (resp.ok){
const res = await resp.json(); const res = await resp.json();
tasks = Object.keys(res).length ? res : null; tasks = Object.keys(res).length ? res : null;
@@ -124,6 +156,7 @@
} }
async function handleTimes(resp){ async function handleTimes(resp){
quitOne();
if (resp.ok){ if (resp.ok){
const res = await resp.json(); const res = await resp.json();
times = Object.keys(res).length ? res : null; times = Object.keys(res).length ? res : null;
@@ -133,6 +166,7 @@
} }
async function handleWikiPages(resp){ async function handleWikiPages(resp){
quitOne();
if (resp.ok){ if (resp.ok){
const res = await resp.json(); const res = await resp.json();
pages = Object.keys(res).length ? res : null; pages = Object.keys(res).length ? res : null;
@@ -141,6 +175,13 @@
} }
} }
function quitOne(){
counter--;
if (counter > 0) {
warn(t('searching…')+" "+counter);
} else yikes();
}
$effect(() => doSearch(key)) $effect(() => doSearch(key))
</script> </script>
@@ -236,23 +277,6 @@
</ul> </ul>
</fieldset> </fieldset>
{/if} {/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} {#if times}
<fieldset> <fieldset>
<legend> <legend>
@@ -308,3 +332,20 @@
</ul> </ul>
</fieldset> </fieldset>
{/if} {/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(){ export function yikes(){
messages.error = null; 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.companyService;
import static de.srsoftware.umbrella.core.ModuleRegistry.translator; import static de.srsoftware.umbrella.core.ModuleRegistry.translator;
import static de.srsoftware.umbrella.core.constants.Field.*; 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;
import static de.srsoftware.umbrella.core.constants.Text.UNIT_PRICE;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException;
import static de.srsoftware.umbrella.stock.Constants.TABLE_ITEMS; import static de.srsoftware.umbrella.stock.Constants.TABLE_ITEMS;
import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.DEBUG;
@@ -75,7 +75,7 @@ public class ItemDb {
LOG.log(DEBUG, " using location: {0}",location.resolve().name()); LOG.log(DEBUG, " using location: {0}",location.resolve().name());
var stockItem = new Item(0,company,0,location,code,name,description); var stockItem = new Item(0,company,0,location,code,name,description);
var props = stockItem.properties(); 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 keyUnit = translator().translate(lang, Text.UNIT);
var keyTax = translator().translate(lang,TAX_RATE); var keyTax = translator().translate(lang,TAX_RATE);
var keyLegacyId = translator().translate(lang,"legacy_id"); var keyLegacyId = translator().translate(lang,"legacy_id");
@@ -132,7 +132,23 @@ public class StockModule extends BaseHandler implements StockService {
yield super.doGet(path,ex); 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){ } catch (UmbrellaException e){
return send(ex,e); return send(ex,e);
@@ -39,6 +39,7 @@ public class SqliteDb extends BaseDb implements TagDB{
public SqliteDb(Connection tagDb, Connection bmDb) { public SqliteDb(Connection tagDb, Connection bmDb) {
super(tagDb); super(tagDb);
bookmarks = new de.srsoftware.umbrella.bookmarks.SqliteDb(bmDb); bookmarks = new de.srsoftware.umbrella.bookmarks.SqliteDb(bmDb);
createTables();
} }
@Override @Override
@@ -40,7 +40,6 @@ public class Translations extends PathHandler implements Translator {
} }
private JSONObject loadTranslations(String lang) throws IOException { private JSONObject loadTranslations(String lang) throws IOException {
LOG.log(WARNING,"loadTranslations({0}) not implemented!",lang);
var filename = lang + ".json"; var filename = lang + ".json";
URL url = getClass().getClassLoader().getResource(filename); URL url = getClass().getClassLoader().getResource(filename);
if (url == null) return new JSONObject(); if (url == null) return new JSONObject();
+26
View File
@@ -14,6 +14,7 @@
"basic_data": "Basis-Daten", "basic_data": "Basis-Daten",
"bookmark": "Lesezeichen", "bookmark": "Lesezeichen",
"bookmarks": "Lesezeichen", "bookmarks": "Lesezeichen",
"Boolean": "Boolean",
"by": "von", "by": "von",
"cancel": "abbrechen", "cancel": "abbrechen",
@@ -30,6 +31,7 @@
"confirm_state": "Status wirklich ändern?", "confirm_state": "Status wirklich ändern?",
"companies": "Firmen", "companies": "Firmen",
"company": "Firma", "company": "Firma",
"company ({id})": "Firma ({id})",
"company_optional": "Firma (optional)", "company_optional": "Firma (optional)",
"confirmation": "Bestätigung", "confirmation": "Bestätigung",
"complete": "abschließen", "complete": "abschließen",
@@ -53,6 +55,7 @@
"customer_address": "Adresse", "customer_address": "Adresse",
"customer_email": "Emailadresse des Kunden", "customer_email": "Emailadresse des Kunden",
"customer_id": "Kundennummer", "customer_id": "Kundennummer",
"customer settings": "Kunden-Einstellungen",
"custom_tag_colors": "Nutzerdefinierte Tag-Farben", "custom_tag_colors": "Nutzerdefinierte Tag-Farben",
"data_sent": "Daten übermittelt", "data_sent": "Daten übermittelt",
@@ -72,6 +75,8 @@
"document": "Dokument", "document": "Dokument",
"document_list": "Dokumente", "document_list": "Dokumente",
"documents": "Dokumente", "documents": "Dokumente",
"document type id": "Dokumenten-Typ-ID",
"document ({id})": "Dokument ({id})",
"do_login" : "anmelden", "do_login" : "anmelden",
"do_open" : "öffnen", "do_open" : "öffnen",
"do_send" : "versenden", "do_send" : "versenden",
@@ -87,6 +92,7 @@
"edit_service": "Login-Service \"{name}\" bearbeiten", "edit_service": "Login-Service \"{name}\" bearbeiten",
"email": "E-Mail", "email": "E-Mail",
"email_or_username": "Email oder Nutzername", "email_or_username": "Email oder Nutzername",
"Encountered invalid dbCode: {code}": "Ungültiger dbCode aufgetreten: {code}",
"end": "Ende", "end": "Ende",
"estimated_time": "geschätzte Zeit", "estimated_time": "geschätzte Zeit",
"estimated_times": "geschätzte Zeiten", "estimated_times": "geschätzte Zeiten",
@@ -167,12 +173,16 @@
"local_court": "Amtsgericht", "local_court": "Amtsgericht",
"locality": "Ort", "locality": "Ort",
"location": "Ort", "location": "Ort",
"locations": "Orte",
"login" : "Anmeldung", "login" : "Anmeldung",
"login service": "Login-Service",
"login_services": "Login-Services", "login_services": "Login-Services",
"logout": "Abmelden", "logout": "Abmelden",
"logout_user": "{user} abmelden", "logout_user": "{user} abmelden",
"Long": "Ganzzahl",
"markdown_code": "Markdown-Code", "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!", "markdown_supported": "Markdown & <a target=\"_blank\" href=\"https://plantuml.com\">Plantuml</a> nutzbar!",
"MANAGE_LOGIN_SERVICES": "Login-Services verwalten", "MANAGE_LOGIN_SERVICES": "Login-Services verwalten",
"member": "Mitarbeiter", "member": "Mitarbeiter",
@@ -205,6 +215,7 @@
"month": "Monat", "month": "Monat",
"move_to_top": "nach ganz oben bewegen", "move_to_top": "nach ganz oben bewegen",
"must_not_be_empty": "darf nicht leer sein", "must_not_be_empty": "darf nicht leer sein",
"my files": "Meine Dateien",
"name": "Name", "name": "Name",
"net_price": "Nettopreis", "net_price": "Nettopreis",
@@ -218,6 +229,7 @@
"no_project_for_id": "Kein Projekt mit ID {0} gefunden!", "no_project_for_id": "Kein Projekt mit ID {0} gefunden!",
"no_task_for_id": "Keine Aufgabe mit ID {0} gefunden!", "no_task_for_id": "Keine Aufgabe mit ID {0} gefunden!",
"note": "Notiz", "note": "Notiz",
"note ({id})": "Notiz ({id})",
"notes": "Notizen", "notes": "Notizen",
"not_recent_version": "Die ist nicht die neuste Version dieser Seite!", "not_recent_version": "Die ist nicht die neuste Version dieser Seite!",
"number": "Nummer", "number": "Nummer",
@@ -230,6 +242,7 @@
"page": "Seite", "page": "Seite",
"parent_task": "übergeordnete Aufgabe", "parent_task": "übergeordnete Aufgabe",
"password" : "Passwort", "password" : "Passwort",
"path": "Pfad",
"permission": { "permission": {
"EDIT": "lesen/schreiben", "EDIT": "lesen/schreiben",
"OWNER": "Besitzer" "OWNER": "Besitzer"
@@ -250,7 +263,10 @@
"priority": "Priorität", "priority": "Priorität",
"processing_code": "Code wird verarbeitet…", "processing_code": "Code wird verarbeitet…",
"project": "Projekt", "project": "Projekt",
"project ({id})": "Projekt ({id})",
"projects": "Projekte", "projects": "Projekte",
"properties": "Eigenschaften",
"property": "Eigenschaft",
"record": "Eintrag", "record": "Eintrag",
"region": "Bundesland", "region": "Bundesland",
@@ -262,6 +278,7 @@
"saved": "gespeichert", "saved": "gespeichert",
"save_object": "{object} speichern", "save_object": "{object} speichern",
"search": "Suche", "search": "Suche",
"searching…": "suche…",
"select_company" : "Wählen Sie eine ihrer Firmen:", "select_company" : "Wählen Sie eine ihrer Firmen:",
"select_customer": "Kunde auswählen", "select_customer": "Kunde auswählen",
"select_property": "Eigenschaft auswählen", "select_property": "Eigenschaft auswählen",
@@ -274,6 +291,8 @@
"sender_tax_id": "Steuernummer", "sender_tax_id": "Steuernummer",
"sent_email": "Email gesendet", "sent_email": "Email gesendet",
"service": "Service", "service": "Service",
"service ({id})": "Service ({id})",
"session": "Sitzung",
"settings" : "Einstellungen", "settings" : "Einstellungen",
"share_with": "Teilen mit:", "share_with": "Teilen mit:",
"show": "anzeigen", "show": "anzeigen",
@@ -301,12 +320,14 @@
}, },
"stock": "Inventar", "stock": "Inventar",
"street": "Straße", "street": "Straße",
"string": "Text",
"subject": "Betreff", "subject": "Betreff",
"subtask": "Unteraufgabe", "subtask": "Unteraufgabe",
"subtasks": "Unteraufgaben", "subtasks": "Unteraufgaben",
"succeeding_document": "Nachfolge-Dokument", "succeeding_document": "Nachfolge-Dokument",
"sum_of_records": "Summe der ausgewählten Einträge", "sum_of_records": "Summe der ausgewählten Einträge",
"table {name}": "Tabelle {name}",
"tag_name": "Tag-Name", "tag_name": "Tag-Name",
"tag_uses": "Verwendung des Tags „{tag}“", "tag_uses": "Verwendung des Tags „{tag}“",
"tags": "Tags", "tags": "Tags",
@@ -319,6 +340,7 @@
"tax_rate": "Steuersatz", "tax_rate": "Steuersatz",
"template": "Vorlage", "template": "Vorlage",
"theme": "Design", "theme": "Design",
"time ({id})": "Zeit ({id})",
"times": "Zeiten", "times": "Zeiten",
"timetracking": "Zeiterfassung", "timetracking": "Zeiterfassung",
"title_not_available": "„{title}“ ist als Seitenname nicht mehr verfügbar!", "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!", "unexpected_item_id_format": "Alte Artikel-ID sollte die Form tt:zz:zz haben, habe aber {0} gefunden!",
"unit": "Einheit", "unit": "Einheit",
"unit price": "Preis/Einheit",
"unit_price": "Preis/Einheit", "unit_price": "Preis/Einheit",
"unknown_item_location": "Artikel {0} von {1} {2} ist verknüpft mit unbekanntem Lagerort {3}!", "unknown_item_location": "Artikel {0} von {1} {2} ist verknüpft mit unbekanntem Lagerort {3}!",
"unlink": "Trennen", "unlink": "Trennen",
@@ -340,6 +363,7 @@
"UPDATE_USERS" : "Nutzer aktualisieren", "UPDATE_USERS" : "Nutzer aktualisieren",
"upload_file": "Datei hochladen", "upload_file": "Datei hochladen",
"user": "Benutzer", "user": "Benutzer",
"user ({id})": "Benutzer ({id})",
"user_list": "Benutzer-Liste", "user_list": "Benutzer-Liste",
"user_module" : "Umbrella User-Verwaltung", "user_module" : "Umbrella User-Verwaltung",
"users": "Benutzer", "users": "Benutzer",
@@ -351,6 +375,8 @@
"welcome" : "Willkommen, {0}", "welcome" : "Willkommen, {0}",
"wiki": "Wiki", "wiki": "Wiki",
"wikis": "Wiki-Seiten", "wikis": "Wiki-Seiten",
"wiki page": "Wiki-Seite",
"wiki pages": "Wiki-Seiten",
"wiki_pages": "Wiki-Seiten", "wiki_pages": "Wiki-Seiten",
"value": "Wert", "value": "Wert",
+30 -1
View File
@@ -14,6 +14,7 @@
"basic_data": "basic data", "basic_data": "basic data",
"bookmark": "bookmark", "bookmark": "bookmark",
"bookmarks": "bookmarks", "bookmarks": "bookmarks",
"Boolean": "Boolean",
"by": "by", "by": "by",
"cancel": "cancel", "cancel": "cancel",
@@ -30,6 +31,7 @@
"confirm_state": "Really change state?", "confirm_state": "Really change state?",
"companies": "companies", "companies": "companies",
"company": "company", "company": "company",
"company ({id})": "company ({id})",
"company_optional": "company (optional)", "company_optional": "company (optional)",
"confirmation": "confirmation", "confirmation": "confirmation",
"complete": "complete", "complete": "complete",
@@ -53,6 +55,7 @@
"customer_address": "address", "customer_address": "address",
"customer_email": "customer email address", "customer_email": "customer email address",
"customer_id": "customer ID", "customer_id": "customer ID",
"customer settings": "customer settings",
"custom_tag_colors": "custom tag colors", "custom_tag_colors": "custom tag colors",
"data_sent": "data sent", "data_sent": "data sent",
@@ -72,6 +75,8 @@
"document": "document", "document": "document",
"document_list": "document list", "document_list": "document list",
"documents": "documents", "documents": "documents",
"document type id": "document type id",
"document ({id})":"document ({id})",
"do_login" : "do login", "do_login" : "do login",
"do_open" : "open", "do_open" : "open",
"do_send" : "send", "do_send" : "send",
@@ -87,6 +92,7 @@
"edit_service": "edit login service \"{name}\"", "edit_service": "edit login service \"{name}\"",
"email": "email", "email": "email",
"email_or_username": "email or username", "email_or_username": "email or username",
"Encountered invalid dbCode: {code}": "Encountered invalid dbCode: {code}",
"end": "end", "end": "end",
"estimated_time": "estimated duration", "estimated_time": "estimated duration",
"estimated_times": "estimated durations", "estimated_times": "estimated durations",
@@ -167,12 +173,16 @@
"local_court": "local court", "local_court": "local court",
"locality": "locality", "locality": "locality",
"location": "location", "location": "location",
"locations": "locations",
"login" : "login", "login" : "login",
"login service": "login service",
"login_services": "login service", "login_services": "login service",
"logout": "logout", "logout": "logout",
"logout_user": "logout {user}", "logout_user": "logout {user}",
"Long": "Long",
"markdown_code": "Markdown-Code", "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!", "markdown_supported": "Markdown & <a target=\"_blank\" href=\"https://plantuml.com\">Plantuml</a> supported!",
"MANAGE_LOGIN_SERVICES": "manage login services", "MANAGE_LOGIN_SERVICES": "manage login services",
"member": "member", "member": "member",
@@ -205,6 +215,7 @@
"month": "month", "month": "month",
"move_to_top": "move to top level", "move_to_top": "move to top level",
"must_not_be_empty": "must not be empty", "must_not_be_empty": "must not be empty",
"my files": "my files",
"name": "Name", "name": "Name",
"net_price": "net price", "net_price": "net price",
@@ -218,6 +229,7 @@
"no_project_for_id": "No project found for id {0}", "no_project_for_id": "No project found for id {0}",
"no_task_for_id": "No task found for id {0}", "no_task_for_id": "No task found for id {0}",
"note": "note", "note": "note",
"note ({id})":"note ({id})",
"notes": "notes", "notes": "notes",
"not_recent_version": "This is not the current version of this page!", "not_recent_version": "This is not the current version of this page!",
"number": "number", "number": "number",
@@ -230,6 +242,7 @@
"page": "page", "page": "page",
"parent_task": "parent task", "parent_task": "parent task",
"password" : "password", "password" : "password",
"path": "path",
"permission": { "permission": {
"EDIT": "read/write", "EDIT": "read/write",
"OWNER": "owner" "OWNER": "owner"
@@ -250,7 +263,10 @@
"priority": "priority", "priority": "priority",
"processing_code": "processing code…", "processing_code": "processing code…",
"project": "project", "project": "project",
"project ({id})": "project ({id})",
"projects": "projects", "projects": "projects",
"properties": "properties",
"property": "property",
"record": "record", "record": "record",
"region": "region", "region": "region",
@@ -262,6 +278,7 @@
"saved": "saved", "saved": "saved",
"save_object": "save {object}", "save_object": "save {object}",
"search": "search", "search": "search",
"searching…": "searhcing…",
"select_company" : "select on of you companies:", "select_company" : "select on of you companies:",
"select_customer": "select customer", "select_customer": "select customer",
"select_property": "select property", "select_property": "select property",
@@ -274,6 +291,8 @@
"sender_tax_id": "tax ID", "sender_tax_id": "tax ID",
"sent_email": "email sent", "sent_email": "email sent",
"service": "service", "service": "service",
"service ({id})": "service ({id})",
"session": "session",
"settings" : "settings", "settings" : "settings",
"share_with": "share with:", "share_with": "share with:",
"show": "show", "show": "show",
@@ -301,12 +320,14 @@
}, },
"stock": "stock", "stock": "stock",
"street": "street", "street": "street",
"string": "string",
"subject": "subject", "subject": "subject",
"subtask": "subtask", "subtask": "subtask",
"subtasks": "subtasks", "subtasks": "subtasks",
"succeeding_document": "succeeding document", "succeeding_document": "succeeding document",
"sum_of_records": "sum of records", "sum_of_records": "sum of records",
"table {name}": "table {name}",
"tag_name": "tag name", "tag_name": "tag name",
"tag_uses": "usage of tag „{tag}“", "tag_uses": "usage of tag „{tag}“",
"tags": "tags", "tags": "tags",
@@ -319,13 +340,14 @@
"tax_rate": "tax rate", "tax_rate": "tax rate",
"template": "template", "template": "template",
"theme": "design", "theme": "design",
"time ({id})": "time ({id})",
"times": "times", "times": "times",
"timetracking": "time tracking", "timetracking": "time tracking",
"title_not_available": "„{title}“ is not available as page name!", "title_not_available": "„{title}“ is not available as page name!",
"title_or_desc": "title/description", "title_or_desc": "title/description",
"toggle_objects": "toggle {objects}", "toggle_objects": "toggle {objects}",
"tutorial": "tutorial", "tutorial": "tutorial",
"type": "document type", "type": "type",
"type_confirmation": "confirmation", "type_confirmation": "confirmation",
"type_invoice": "invoice", "type_invoice": "invoice",
"type_offer": "offer", "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}!", "unexpected_item_id_format": "Expected old item ID to be of the form ss:dd:dd, encountered {0}!",
"unit": "unit", "unit": "unit",
"unit price": "unit price",
"unit_price": "price/unit", "unit_price": "price/unit",
"unknown_item_location": "Item {0} of {1} {2} refers to location {3}, which is unknown!", "unknown_item_location": "Item {0} of {1} {2} refers to location {3}, which is unknown!",
"unlink": "unlink", "unlink": "unlink",
@@ -340,14 +363,20 @@
"UPDATE_USERS" : "update users", "UPDATE_USERS" : "update users",
"upload_file": "upload file", "upload_file": "upload file",
"user": "user", "user": "user",
"user ({id})": "user ({id})",
"user_list": "user list", "user_list": "user list",
"user_module" : "Umbrella user management", "user_module" : "Umbrella user management",
"users": "users", "users": "users",
"user_created_entity": "{user} created \"{entity}\"",
"user_deleted_entity": "{user} deleted \"{entity}\"",
"user_updated_entity": "{user} updated \"{entity}\"",
"website": "website", "website": "website",
"welcome" : "Welcome, {0}", "welcome" : "Welcome, {0}",
"wiki": "Wiki", "wiki": "Wiki",
"wikis": "wiki pages", "wikis": "wiki pages",
"wiki page": "wiki page",
"wiki pages": "wiki pages",
"wiki_pages": "wiki pages", "wiki_pages": "wiki pages",
"value": "value", "value": "value",
+1
View File
@@ -9,4 +9,5 @@ tasks.processResources {
from("../frontend/dist") { from("../frontend/dist") {
into("web") into("web")
} }
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
} }
+2 -1
View File
@@ -57,8 +57,9 @@ footer {
margin: 5px; margin: 5px;
} }
img { img, svg {
max-width: 100%; max-width: 100%;
height: auto !important;
} }
nav { nav {
+2 -1
View File
@@ -57,8 +57,9 @@ footer {
margin: 5px; margin: 5px;
} }
img { img, svg {
max-width: 100%; max-width: 100%;
height: auto !important;
} }
nav { nav {
+2 -1
View File
@@ -57,8 +57,9 @@ footer {
margin: 5px; margin: 5px;
} }
img { img, svg {
max-width: 100%; max-width: 100%;
height: auto !important;
} }
nav { nav {
@@ -152,15 +152,27 @@ public class SqliteDb extends BaseDb implements WikiDb {
@Override @Override
public Map<Long, WikiPage> find(long userId, List<String> keys, boolean fulltext) { public Map<Long, WikiPage> find(long userId, List<String> keys, boolean fulltext) {
try { 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>(); var map = new HashMap<Long,WikiPage>();
{
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()) { while (rs.next()) {
var page = WikiPage.of(rs); var page = WikiPage.of(rs);
map.put(page.id(),page); map.put(page.id(), page);
} }
rs.close(); 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();
}
return map; return map;
} catch (SQLException e) { } catch (SQLException e) {
throw failedToSearchDb(t(WIKI_PAGES)).causedBy(e); throw failedToSearchDb(t(WIKI_PAGES)).causedBy(e);