Compare commits
15 Commits
feature/tr
...
feature/fa
| Author | SHA1 | Date | |
|---|---|---|---|
| 33ff47a133 | |||
| d08138c9e1 | |||
| 4cd1ea3277 | |||
| 1059164b4a | |||
| c1beda1669 | |||
| df39e6a57f | |||
| f438bea4cc | |||
| 9394ca597c | |||
| 53fe79fbbd | |||
| d62534b3eb | |||
| 18e8e3ffd1 | |||
| ae55a76ca7 | |||
| cf485055a6 | |||
| 968e5bfb95 | |||
| 6de5f1f660 |
@@ -22,8 +22,9 @@ 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
|
||||
WORKDIR /home/umbrella
|
||||
RUN chmod a+rx plantuml.jar
|
||||
USER umbrella
|
||||
RUN mkdir .config && ln -s /host/config.json .config/Umbrella.json
|
||||
EXPOSE 80
|
||||
CMD java -jar jar/backend.jar
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,19 +31,16 @@ public class Path {
|
||||
public static final String REDIRECT = "redirect";
|
||||
|
||||
public static final String SEARCH = "search";
|
||||
public static final String SERVICE = "service";
|
||||
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 TAGGED = "tagged";
|
||||
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,8 +5,9 @@ package de.srsoftware.umbrella.core.constants;
|
||||
* This is a collection of messages that appear throughout the project
|
||||
*/
|
||||
public class Text {
|
||||
public static final String BOOLEAN = "Boolean";
|
||||
public static final String BOOKMARK = "bookmark";
|
||||
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})";
|
||||
@@ -15,41 +16,51 @@ public class Text {
|
||||
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_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 LOGIN_SERVICE = "login service";
|
||||
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 PROJECT_WITH_ID = "project ({id})";
|
||||
public static final String PROPERTIES = "properties";
|
||||
public static final String PROPERTY = "property";
|
||||
|
||||
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 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 UNIT = "unit";
|
||||
public static final String USER_WITH_ID = "user ({id})";
|
||||
|
||||
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";
|
||||
|
||||
|
||||
@@ -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.*;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = ``;
|
||||
let path = `/api/files${file}`;
|
||||
path = encodeURI(path);
|
||||
let md = ``;
|
||||
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}
|
||||
|
||||
@@ -15,5 +15,5 @@ export async function warn(msg){
|
||||
|
||||
export function yikes(){
|
||||
messages.error = null;
|
||||
messages.warn = null;
|
||||
messages.warning = null;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ public class Constants {
|
||||
public static final String AUTH = "mail.smtp.auth";
|
||||
|
||||
public static final String CONFIG_DB = "umbrella.modules.message.database";
|
||||
public static final String CONFIG_SMTP_FROM = "umbrella.modules.message.smtp.from";
|
||||
public static final String CONFIG_SMTP_HOST = "umbrella.modules.message.smtp.host";
|
||||
public static final String CONFIG_SMTP_PASS = "umbrella.modules.message.smtp.pass";
|
||||
public static final String CONFIG_SMTP_PORT = "umbrella.modules.message.smtp.port";
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.srsoftware.umbrella.message;
|
||||
|
||||
import static de.srsoftware.tools.PathHandler.CONTENT_TYPE;
|
||||
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
|
||||
import static de.srsoftware.umbrella.core.ModuleRegistry.translator;
|
||||
import static de.srsoftware.umbrella.core.constants.Constants.UTF8;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingConfig;
|
||||
import static de.srsoftware.umbrella.message.Constants.*;
|
||||
@@ -12,6 +13,7 @@ import de.srsoftware.configuration.Configuration;
|
||||
import de.srsoftware.umbrella.core.ModuleRegistry;
|
||||
import de.srsoftware.umbrella.core.api.PostBox;
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||
import de.srsoftware.umbrella.core.model.EmailAddress;
|
||||
import de.srsoftware.umbrella.core.model.Envelope;
|
||||
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
||||
import de.srsoftware.umbrella.core.model.User;
|
||||
@@ -76,10 +78,10 @@ public class MessageSystem implements PostBox {
|
||||
db = new SqliteMessageDb(connect(dbFile));
|
||||
debugAddress = config.get(DEBUG_ADDREESS).map(Object::toString).orElse(null);
|
||||
port = config.get(CONFIG_SMTP_PORT,587);
|
||||
host = config.get(CONFIG_SMTP_HOST).map(Object::toString).orElseThrow(() -> new RuntimeException("umbrella.modules.message.smtp.host not configured!"));
|
||||
user = config.get(CONFIG_SMTP_USER).map(Object::toString).orElseThrow(() -> new RuntimeException("umbrella.modules.message.smtp.user not configured!"));
|
||||
pass = config.get(CONFIG_SMTP_PASS).map(Object::toString).orElseThrow(() -> new RuntimeException("umbrella.modules.message.smtp.pass not configured!"));
|
||||
from = user;
|
||||
host = config.get(CONFIG_SMTP_HOST).map(Object::toString).orElseThrow(() -> missingConfig(CONFIG_SMTP_HOST));
|
||||
user = config.get(CONFIG_SMTP_USER).map(Object::toString).orElseThrow(() -> missingConfig(CONFIG_SMTP_USER));
|
||||
pass = config.get(CONFIG_SMTP_PASS).map(Object::toString).orElseThrow(() -> missingConfig(CONFIG_SMTP_PASS));
|
||||
from = config.get(CONFIG_SMTP_FROM).map(Object::toString).orElseThrow(() -> missingConfig(CONFIG_SMTP_FROM));
|
||||
ModuleRegistry.add(this);
|
||||
new SubmissionTask(8).schedule();
|
||||
new SubmissionTask(10).schedule();
|
||||
@@ -116,9 +118,9 @@ public class MessageSystem implements PostBox {
|
||||
var date = new Date();
|
||||
|
||||
for (var receiver : dueRecipients){
|
||||
BiFunction<String,Map<String,String>,String> translateFunction = (text,fills) -> ModuleRegistry.translator().translate(receiver.language(),text,fills);
|
||||
|
||||
var combined = new CombinedMessage("Collected messages",translateFunction);
|
||||
BiFunction<String,Map<String,String>,String> translateFunction = (text,fills) -> translator().translate(receiver.language(),text,fills);
|
||||
var fallbackSender = new User("Umbrella",new EmailAddress(from),null);
|
||||
var combined = new CombinedMessage("Collected messages",translateFunction,fallbackSender);
|
||||
var envelopes = queue.stream().filter(env -> env.isFor(receiver)).toList();
|
||||
for (var envelope : envelopes) combined.merge(envelope.message());
|
||||
|
||||
|
||||
@@ -5,9 +5,7 @@ import static java.lang.System.Logger.Level.DEBUG;
|
||||
import static java.lang.System.Logger.Level.TRACE;
|
||||
import static java.text.MessageFormat.format;
|
||||
|
||||
import de.srsoftware.umbrella.core.model.Attachment;
|
||||
import de.srsoftware.umbrella.core.model.Message;
|
||||
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
||||
import de.srsoftware.umbrella.core.model.*;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@@ -16,15 +14,17 @@ public class CombinedMessage {
|
||||
|
||||
private final Set<Attachment> attachments = new HashSet<>();
|
||||
private final StringBuilder combinedBody = new StringBuilder();
|
||||
private final User fallbackSender;
|
||||
private String combinedSubject = null;
|
||||
private final List<Message> mergedMessages = new ArrayList<>();
|
||||
private final String subjectForCombinedMessage;
|
||||
private final BiFunction<String,Map<String,String>,String> translate;
|
||||
private UmbrellaUser sender = null;
|
||||
private User sender = null;
|
||||
|
||||
public CombinedMessage(String subjectForCombinedMessage, BiFunction<String, Map<String,String>,String> translateFunction){
|
||||
public CombinedMessage(String subjectForCombinedMessage, BiFunction<String, Map<String,String>,String> translateFunction, User fallbackSender){
|
||||
LOG.log(DEBUG,"Creating combined message…");
|
||||
this.subjectForCombinedMessage = subjectForCombinedMessage;
|
||||
this.subjectForCombinedMessage = translateFunction.apply(subjectForCombinedMessage,null);
|
||||
this.fallbackSender = fallbackSender;
|
||||
translate = translateFunction;
|
||||
}
|
||||
|
||||
@@ -39,12 +39,12 @@ public class CombinedMessage {
|
||||
combinedSubject = subject;
|
||||
break;
|
||||
case 1:
|
||||
combinedBody.insert(0,format("# {0}:\n# {1}:\n\n",sender,subject)); // insert sender and subject of first message right before the body of the first message
|
||||
if (!sender.equals(message.sender())) sender = fallbackSender;
|
||||
combinedBody.insert(0,format("# {0} / {1}:\n\n",sender,subject)); // insert sender and subject of first message right before the body of the first message
|
||||
combinedSubject = subjectForCombinedMessage;
|
||||
// no break here, we need to append the subject and content
|
||||
default:
|
||||
combinedBody.append("\n\n# ").append(message.sender()).append(":\n");
|
||||
combinedBody.append("# ").append(subject).append(":\n\n");
|
||||
combinedBody.append("\n-----\n# ").append(message.sender()).append(" / ").append(subject).append(":\n\n");
|
||||
combinedBody.append(body);
|
||||
}
|
||||
if (message.attachments() != null) attachments.addAll(message.attachments());
|
||||
@@ -59,7 +59,7 @@ public class CombinedMessage {
|
||||
return combinedBody.toString();
|
||||
}
|
||||
|
||||
public UmbrellaUser sender() {
|
||||
public User sender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
@@ -274,6 +290,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 +319,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 +339,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 +354,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 +362,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 +374,8 @@
|
||||
"welcome" : "Willkommen, {0}",
|
||||
"wiki": "Wiki",
|
||||
"wikis": "Wiki-Seiten",
|
||||
"wiki page": "Wiki-Seite",
|
||||
"wiki pages": "Wiki-Seiten",
|
||||
"wiki_pages": "Wiki-Seiten",
|
||||
|
||||
"value": "Wert",
|
||||
|
||||
@@ -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",
|
||||
@@ -274,6 +290,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 +319,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 +339,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 +354,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 +362,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",
|
||||
|
||||
@@ -57,8 +57,9 @@ footer {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
img {
|
||||
img, svg {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
nav {
|
||||
|
||||
@@ -57,8 +57,9 @@ footer {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
img {
|
||||
img, svg {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
nav {
|
||||
|
||||
@@ -57,8 +57,9 @@ footer {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
img {
|
||||
img, svg {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
nav {
|
||||
|
||||
Reference in New Issue
Block a user