implemented display of document positions
This commit is contained in:
@@ -9,6 +9,7 @@ import com.sun.net.httpserver.HttpServer;
|
||||
import de.srsoftware.configuration.JsonConfig;
|
||||
import de.srsoftware.tools.ColorLogger;
|
||||
import de.srsoftware.umbrella.company.CompanyModule;
|
||||
import de.srsoftware.umbrella.core.Util;
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||
import de.srsoftware.umbrella.documents.DocumentApi;
|
||||
import de.srsoftware.umbrella.legacy.LegacyApi;
|
||||
@@ -17,6 +18,7 @@ import de.srsoftware.umbrella.message.MessageSystem;
|
||||
import de.srsoftware.umbrella.translations.Translations;
|
||||
import de.srsoftware.umbrella.user.UserModule;
|
||||
import de.srsoftware.umbrella.web.WebHandler;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -45,6 +47,8 @@ public class Application {
|
||||
var port = config.get("umbrella.http.port", 8080);
|
||||
var threads = config.get("umbrella.threads", 16);
|
||||
|
||||
config.get("umbrella.plantuml").map(Object::toString).map(File::new).filter(File::exists).ifPresent(Util::setPlantUmlJar);
|
||||
|
||||
var translationModule = new Translations();
|
||||
var messageSystem = new MessageSystem(translationModule,config);
|
||||
var server = HttpServer.create(new InetSocketAddress(port), 0);
|
||||
|
||||
@@ -32,10 +32,6 @@ public abstract class BaseHandler extends PathHandler {
|
||||
return ex;
|
||||
}
|
||||
|
||||
public boolean forbidden(HttpExchange ex) throws IOException {
|
||||
return sendEmptyResponse(HTTP_FORBIDDEN,ex);
|
||||
}
|
||||
|
||||
public record Page(String mime, byte[] bytes){}
|
||||
|
||||
public boolean load(Path path, HttpExchange ex) throws IOException {
|
||||
@@ -73,7 +69,7 @@ public abstract class BaseHandler extends PathHandler {
|
||||
}
|
||||
|
||||
public boolean unauthorized(HttpExchange ex) throws IOException {
|
||||
return sendEmptyResponse(HTTP_FORBIDDEN,ex);
|
||||
return sendEmptyResponse(HTTP_UNAUTHORIZED,ex);
|
||||
}
|
||||
|
||||
public boolean notImplemented(HttpExchange ex,String message,Object clazz) throws IOException{
|
||||
|
||||
@@ -8,18 +8,23 @@ import static java.lang.System.Logger.Level.*;
|
||||
import static java.lang.System.Logger.Level.WARNING;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.xrbpowered.jparsedown.JParsedown;
|
||||
import de.srsoftware.tools.Query;
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
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 File plantumlJar = null;
|
||||
private static final JParsedown MARKDOWN = new JParsedown();
|
||||
|
||||
private Util(){}
|
||||
|
||||
public static System.Logger.Level mapLogLevel(String lbl) {
|
||||
@@ -32,9 +37,38 @@ public class Util {
|
||||
};
|
||||
}
|
||||
|
||||
public static String markdown(String code){
|
||||
LOG.log(ERROR,"{0}.markdown(…) not implemented",Util.class.getCanonicalName());
|
||||
return code;
|
||||
public static String markdown(String source){
|
||||
try {
|
||||
if (plantumlJar.exists()) {
|
||||
var matcher = UML_PATTERN.matcher(source);
|
||||
if (matcher.find()) {
|
||||
var uml = matcher.group(0).trim();
|
||||
var start = matcher.start(0);
|
||||
var end = matcher.end(0);
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", plantumlJar.getAbsolutePath(), "-tsvg", "-pipe");
|
||||
var ignored = processBuilder.redirectErrorStream();
|
||||
var process = processBuilder.start();
|
||||
try (OutputStream os = process.getOutputStream()) {
|
||||
os.write(uml.getBytes(UTF_8));
|
||||
os.flush();
|
||||
}
|
||||
|
||||
try (InputStream is = process.getInputStream()) {
|
||||
byte[] out = is.readAllBytes();
|
||||
var svg = new String(out, UTF_8);
|
||||
source = source.substring(0, start) + svg + source.substring(end);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return MARKDOWN.text(source);
|
||||
} catch (Exception e){
|
||||
if (LOG.isLoggable(TRACE)){
|
||||
LOG.log(TRACE,"Failed to render markdown, input was: \n{0}",source);
|
||||
} else LOG.log(WARNING,"Failed to render markdown. Enable TRACE log level for details.");
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
public static HttpURLConnection open(URL url) throws IOException {
|
||||
@@ -101,4 +135,9 @@ public class Util {
|
||||
throw new UmbrellaException(500,"Request to {0} failed!",target).causedBy(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setPlantUmlJar(File file){
|
||||
LOG.log(INFO,"Using plantuml @ {0}",file.getAbsolutePath());
|
||||
plantumlJar = file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import static de.srsoftware.umbrella.core.Constants.*;
|
||||
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR;
|
||||
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_UNPROCESSABLE;
|
||||
import static java.lang.System.Logger.Level.ERROR;
|
||||
import static java.lang.System.Logger.Level.WARNING;
|
||||
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
|
||||
import static java.text.MessageFormat.format;
|
||||
|
||||
|
||||
@@ -25,10 +27,15 @@ public class UmbrellaException extends Exception{
|
||||
return this;
|
||||
}
|
||||
|
||||
public static UmbrellaException databaseException(String message, Object fills) {
|
||||
public static UmbrellaException databaseException(String message, Object... fills) {
|
||||
System.getLogger("Configuration").log(WARNING,message,fills);
|
||||
return new UmbrellaException(HTTP_SERVER_ERROR,message,fills);
|
||||
}
|
||||
|
||||
public static UmbrellaException forbidden(String message, Object... fills) {
|
||||
return new UmbrellaException(HTTP_FORBIDDEN,message,fills);
|
||||
}
|
||||
|
||||
public static UmbrellaException invalidFieldException(String field,String expected){
|
||||
return new UmbrellaException(HTTP_UNPROCESSABLE, ERROR_INVALID_FIELD, field, expected);
|
||||
}
|
||||
|
||||
@@ -90,5 +90,5 @@ public class Constants {
|
||||
public static final String TABLE_POSITIONS = "document_positions";
|
||||
public static final String TABLE_PRICES = "customer_prices";
|
||||
public static final String TABLE_TEMPLATES = "templates";
|
||||
|
||||
public static final String TEMPLATES = "templates";
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
|
||||
import static de.srsoftware.umbrella.core.Constants.*;
|
||||
import static de.srsoftware.umbrella.core.Paths.LIST;
|
||||
import static de.srsoftware.umbrella.core.Util.request;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
|
||||
import static de.srsoftware.umbrella.documents.Constants.*;
|
||||
import static de.srsoftware.umbrella.documents.model.Document.State.NEW;
|
||||
@@ -28,11 +29,9 @@ import de.srsoftware.umbrella.documents.model.*;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -75,10 +74,7 @@ public class DocumentApi extends BaseHandler {
|
||||
private boolean deleteDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException {
|
||||
var doc = db.loadDoc(docId);
|
||||
var companyId = doc.companyId();
|
||||
var members = companies.getMembers(companyId);
|
||||
var isMember = false;
|
||||
for (var member : members) isMember |= user.equals(member);
|
||||
if (!isMember) throw new UmbrellaException(HTTP_FORBIDDEN,"You are mot a member of company {0}",doc.companyId());
|
||||
if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",doc.companyId());
|
||||
if (doc.state() != NEW) throw new UmbrellaException(HTTP_BAD_REQUEST,"This document has already been sent");
|
||||
return sendContent(ex,db.deleteDoc(docId));
|
||||
}
|
||||
@@ -99,7 +95,8 @@ public class DocumentApi extends BaseHandler {
|
||||
case null -> super.doGet(path,ex);
|
||||
default -> {
|
||||
try {
|
||||
yield getDocument(ex,Long.parseLong(head),user.get());
|
||||
var docId = Long.parseLong(head);
|
||||
yield getDocument(ex,docId,user.get());
|
||||
} catch (NumberFormatException ignored) {}
|
||||
yield super.doGet(path,ex);
|
||||
}
|
||||
@@ -123,7 +120,8 @@ public class DocumentApi extends BaseHandler {
|
||||
if (user.isEmpty()) return unauthorized(ex);
|
||||
var head = path.pop();
|
||||
return switch (head){
|
||||
case LIST -> listDocuments(ex,user.get(),token.orElse(null));
|
||||
case LIST -> listCompaniesDocuments(ex,user.get(),token.orElse(null));
|
||||
case TEMPLATES -> postTemplateList(ex,user.get());
|
||||
case null -> postDocument(ex,user.get());
|
||||
default -> super.doPost(path,ex);
|
||||
};
|
||||
@@ -145,6 +143,8 @@ public class DocumentApi extends BaseHandler {
|
||||
return sendContent(ex,map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean getDocTypes(HttpExchange ex) throws UmbrellaException, IOException {
|
||||
var types = db.listTypes();
|
||||
var map = types.values().stream().collect(Collectors.toMap(Type::id, Type::name));
|
||||
@@ -155,10 +155,7 @@ public class DocumentApi extends BaseHandler {
|
||||
var doc = db.loadDoc(docId);
|
||||
var companyId = doc.companyId();
|
||||
var company = companies.get(companyId);
|
||||
var members = companies.getMembers(companyId);
|
||||
var isMember = false;
|
||||
for (var member : members) isMember |= user.equals(member);
|
||||
if (!isMember) return sendContent(ex,HTTP_FORBIDDEN,"You are mot a member of company "+doc.companyId());
|
||||
if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name());
|
||||
var sep = company.decimalSeparator();
|
||||
if (sep != null) doc.setDecimalSeparator(sep);
|
||||
doc.setCompanyName(company.name());
|
||||
@@ -173,13 +170,13 @@ public class DocumentApi extends BaseHandler {
|
||||
return new JSONArray(s);
|
||||
}
|
||||
|
||||
private boolean listDocuments(HttpExchange ex, UmbrellaUser user, Token token) throws UmbrellaException {
|
||||
private boolean listCompaniesDocuments(HttpExchange ex, UmbrellaUser user, Token token) throws UmbrellaException {
|
||||
try {
|
||||
var json = json(ex);
|
||||
if (!json.has(COMPANY)) throw missingFieldException(COMPANY);
|
||||
long companyId = json.getLong(COMPANY);
|
||||
var company = companies.get(companyId);
|
||||
if (!companies.membership(companyId,user.id())) throw new UmbrellaException(HTTP_FORBIDDEN,"You are mot a member of company {0}",company);
|
||||
if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company);
|
||||
var docs = db.listDocs(companyId);
|
||||
var map = new HashMap<Long,Object>();
|
||||
for (var entry : docs.entrySet()) map.put(entry.getKey(),entry.getValue().summary());
|
||||
@@ -196,7 +193,7 @@ public class DocumentApi extends BaseHandler {
|
||||
if (!senderData.has(FIELD_COMPANY) || !(senderData.get(FIELD_COMPANY) instanceof Number companyId)) throw missingFieldException(FIELD_COMPANY);
|
||||
|
||||
var company = companies.get(companyId.longValue());
|
||||
if (!companies.membership(companyId.longValue(),user.id())) throw new UmbrellaException(HTTP_FORBIDDEN,"You are mot a member of company {0}",company);
|
||||
if (!companies.membership(companyId.longValue(),user.id())) throw forbidden("You are mot a member of company {0}",company);
|
||||
|
||||
if (!json.has(FIELD_CUSTOMER) || !(json.get(FIELD_CUSTOMER) instanceof JSONObject customerData)) throw missingFieldException(FIELD_CUSTOMER);
|
||||
if (!json.has(FIELD_TYPE) || !(json.get(FIELD_TYPE) instanceof Number docTypeId)) throw missingFieldException(FIELD_TYPE);
|
||||
@@ -217,4 +214,13 @@ public class DocumentApi extends BaseHandler {
|
||||
db.step(companySettings);
|
||||
return sendContent(ex,saved.toMap());
|
||||
}
|
||||
|
||||
private boolean postTemplateList(HttpExchange ex, UmbrellaUser user) throws UmbrellaException, IOException {
|
||||
var json = json(ex);
|
||||
if (!(json.has(COMPANY) && json.get(COMPANY) instanceof Number companyId)) throw missingFieldException(COMPANY);
|
||||
var company = companies.get(companyId.longValue());
|
||||
if (!companies.membership(companyId.longValue(),user.id())) throw forbidden("You are not a member of {0}",company.name());
|
||||
var templates = db.getCompanyTemplates(companyId.longValue());
|
||||
return sendContent(ex,templates.stream().map(Template::toMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,8 @@ package de.srsoftware.umbrella.documents;
|
||||
|
||||
import de.srsoftware.tools.Pair;
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||
import de.srsoftware.umbrella.documents.model.CompanySettings;
|
||||
import de.srsoftware.umbrella.documents.model.CustomerSettings;
|
||||
import de.srsoftware.umbrella.documents.model.Document;
|
||||
import de.srsoftware.umbrella.documents.model.Type;
|
||||
import de.srsoftware.umbrella.documents.model.*;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -52,4 +50,6 @@ public interface DocumentDb {
|
||||
void step(CompanySettings settings);
|
||||
|
||||
Pair<Integer> switchPositions(long docId, Pair<Integer> longPair) throws UmbrellaException;
|
||||
|
||||
Collection<Template> getCompanyTemplates(long l) throws UmbrellaException;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import static de.srsoftware.tools.jdbc.Condition.equal;
|
||||
import static de.srsoftware.tools.jdbc.Condition.in;
|
||||
import static de.srsoftware.tools.jdbc.Query.*;
|
||||
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
|
||||
import static de.srsoftware.umbrella.company.Constants.*;
|
||||
import static de.srsoftware.umbrella.core.Constants.*;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException;
|
||||
import static de.srsoftware.umbrella.documents.Constants.*;
|
||||
import static de.srsoftware.umbrella.documents.model.Document.DEFAULT_THOUSANDS_SEPARATOR;
|
||||
import static de.srsoftware.umbrella.documents.model.Document.State;
|
||||
@@ -24,6 +24,7 @@ import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -252,11 +253,22 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255)
|
||||
if (rs.next()) settings = CompanySettings.of(rs);
|
||||
rs.close();
|
||||
if (settings != null) return settings;
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOG.log(WARNING,"Failed to load customer settings (company: {0}, document type: {1}",companyId, docType.name(),e);
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
throw databaseException("Failed to load customer settings (company: {0}, document type: {1})",companyId, docType.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Template> getCompanyTemplates(long companyId) throws UmbrellaException {
|
||||
try {
|
||||
var rs = select(ALL).from(TABLE_TEMPLATES).where(COMPANY_ID,equal(companyId)).exec(db);
|
||||
var templates = new HashSet<Template>();
|
||||
while (rs.next()) templates.add(Template.of(rs));
|
||||
rs.close();
|
||||
return templates;
|
||||
} catch (SQLException e) {
|
||||
throw databaseException("Failed to load templates for company {0}",companyId);
|
||||
}
|
||||
throw new UmbrellaException(500,"Failed to load customer settings (company: {0}, document type: {1}",companyId, docType.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
/* © SRSoftware 2025 */
|
||||
package de.srsoftware.umbrella.documents.model;
|
||||
|
||||
import static de.srsoftware.umbrella.core.Constants.ID;
|
||||
import static de.srsoftware.umbrella.core.Constants.NAME;
|
||||
import static de.srsoftware.umbrella.core.Constants.*;
|
||||
import static de.srsoftware.umbrella.documents.Constants.FIELD_COMPANY;
|
||||
|
||||
import de.srsoftware.tools.Mappable;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
public record Template(long id, long company, String name, byte[] data) implements Mappable {
|
||||
|
||||
public static Template of(ResultSet rs) throws SQLException {
|
||||
return new Template(rs.getLong(ID),rs.getLong(COMPANY_ID),rs.getString(NAME),rs.getBytes(TEMPLATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> toMap() {
|
||||
return Map.of(ID,id,FIELD_COMPANY,company, NAME,name);
|
||||
|
||||
26
frontend/src/routes/document/Position.svelte
Normal file
26
frontend/src/routes/document/Position.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<script>
|
||||
import { useTinyRouter } from 'svelte-tiny-router';
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from '../../translations.svelte.js';
|
||||
|
||||
var { currency, pos = $bindable(null) } = $props();
|
||||
|
||||
console.log(pos);
|
||||
</script>
|
||||
{#if pos}
|
||||
<tr>
|
||||
<td>{pos.number}</td>
|
||||
<td>{pos.item}</td>
|
||||
<td>{pos.title}</td>
|
||||
<td>{pos.amount}</td>
|
||||
<td>{pos.unit}</td>
|
||||
<td>{pos.unit_price/100} {currency}</td>
|
||||
<td>{pos.net_price/100} {currency}</td>
|
||||
<td>{pos.tax} %</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="error">buttons</td>
|
||||
<td colspan="6">{@html pos.description}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{/if}
|
||||
29
frontend/src/routes/document/PositionList.svelte
Normal file
29
frontend/src/routes/document/PositionList.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<script>
|
||||
import Position from './Position.svelte';
|
||||
import { useTinyRouter } from 'svelte-tiny-router';
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from '../../translations.svelte.js';
|
||||
|
||||
var { document = $bindable(null) } = $props();
|
||||
</script>
|
||||
{#if document.positions}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('document.pos')}</th>
|
||||
<th>{t('document.code')}</th>
|
||||
<th>{t('document.title_or_desc')}</th>
|
||||
<th>{t('document.amount')}</th>
|
||||
<th>{t('document.unit')}</th>
|
||||
<th>{t('document.unit_price')}</th>
|
||||
<th>{t('document.net_price')}</th>
|
||||
<th>{t('document.tax_rate')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each Object.entries(document.positions) as [id,pos]}
|
||||
<Position currency={document.currency} bind:pos={document.positions[id]} />
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/if}
|
||||
35
frontend/src/routes/document/TemplateSelector.svelte
Normal file
35
frontend/src/routes/document/TemplateSelector.svelte
Normal file
@@ -0,0 +1,35 @@
|
||||
<script>
|
||||
import {onMount} from 'svelte';
|
||||
import {t} from '../../translations.svelte.js';
|
||||
let { caption, company, value = $bindable(0), onchange = () => console.log('changed')} = $props();
|
||||
let message = t('document.loading');
|
||||
let templates = $state(null);
|
||||
|
||||
async function loadTemplates(){
|
||||
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/document/templates`;
|
||||
var resp = await fetch(url,{
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
body: JSON.stringify({company:company})
|
||||
});
|
||||
if (resp.ok){
|
||||
templates = await resp.json();
|
||||
} else {
|
||||
message = await resp.text();
|
||||
}
|
||||
}
|
||||
|
||||
onMount(loadTemplates)
|
||||
</script>
|
||||
|
||||
{#if templates}
|
||||
<select bind:value onchange={onchange}>
|
||||
<option value={0}>{caption}</option>
|
||||
{#each Object.entries(templates) as [id,template]}
|
||||
<option value={template.id}>{template.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<span>{message}</span>
|
||||
{/if}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from '../../translations.svelte.js';
|
||||
import { useTinyRouter } from 'svelte-tiny-router';
|
||||
import PositionList from './PositionList.svelte';
|
||||
import StateSelector from './StateSelector.svelte';
|
||||
import TemplateSelector from './TemplateSelector.svelte';
|
||||
let { id } = $props();
|
||||
let error = null;
|
||||
let doc = $state(null);
|
||||
@@ -80,19 +82,19 @@
|
||||
<div>{t('document.state')}: <StateSelector selected={doc.state} onchange={changeState} /></div>
|
||||
<div>{t('document.date')}: {doc.date}</div>
|
||||
<div>{t('document.delivery')}: {doc.delivery}</div>
|
||||
<div>{t('document.template')}: {doc.template.name} <span class="error">SElektor hier!</span></div>
|
||||
<div>{t('document.template')}: <TemplateSelector company={doc.company.id} bind:value={doc.template.id} /></div>
|
||||
</fieldset>
|
||||
<fieldset class="clear">
|
||||
<legend>{t('document.head')}</legend>
|
||||
{doc.head}
|
||||
{@html doc.head}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{t('document.positions')}</legend>
|
||||
<span class="error">laden!</span>
|
||||
<PositionList bind:document={doc} />
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{t('document.footer')}</legend>
|
||||
{doc.footer}
|
||||
{@html doc.footer}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{t('document.actions')}</legend>
|
||||
|
||||
@@ -12,8 +12,7 @@ import static de.srsoftware.umbrella.core.ResponseCode.*;
|
||||
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR;
|
||||
import static de.srsoftware.umbrella.core.Util.open;
|
||||
import static de.srsoftware.umbrella.core.Util.request;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingConfigException;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
|
||||
import static de.srsoftware.umbrella.user.Constants.*;
|
||||
import static de.srsoftware.umbrella.user.Paths.*;
|
||||
import static de.srsoftware.umbrella.user.Paths.IMPERSONATE;
|
||||
@@ -185,9 +184,9 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
|
||||
};
|
||||
long userId = Long.parseLong(head);
|
||||
if (user.isEmpty()) return forbidden(ex);
|
||||
if (!(user.get() instanceof DbUser dbUser)) return forbidden(ex);
|
||||
if (!(dbUser.id() == userId || dbUser.permissions().contains(LIST_USERS))) return forbidden(ex);
|
||||
if (user.isEmpty()) return unauthorized(ex);
|
||||
if (!(user.get() instanceof DbUser dbUser)) return unauthorized(ex);
|
||||
if (!(dbUser.id() == userId || dbUser.permissions().contains(LIST_USERS))) throw forbidden("You are not allowed to access that user!");
|
||||
return sendContent(ex,users.load(userId));
|
||||
} catch (UmbrellaException e) {
|
||||
return send(ex,e);
|
||||
@@ -215,7 +214,7 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
userId = Long.parseLong(head);
|
||||
DbUser editedUser = (DbUser) users.load(userId);
|
||||
|
||||
if (!(requestingUser.get() instanceof DbUser dbUser) || !(dbUser.id() == userId || dbUser.permissions().contains(UPDATE_USERS))) return sendContent(ex,HTTP_FORBIDDEN,"You are not allowed to update user "+editedUser.name());
|
||||
if (!(requestingUser.get() instanceof DbUser dbUser) || !(dbUser.id() == userId || dbUser.permissions().contains(UPDATE_USERS))) throw forbidden("You are not allowed to update user "+editedUser.name());
|
||||
|
||||
JSONObject json;
|
||||
try {
|
||||
@@ -242,14 +241,18 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
targetId = Long.parseLong(head);
|
||||
head = path.pop();
|
||||
} catch (NumberFormatException ignored) {}
|
||||
switch (head){
|
||||
case CREATE: return postCreate(ex);
|
||||
case OIDC: return postOIDC(ex,path);
|
||||
case IMPERSONATE: return impersonate(ex,targetId);
|
||||
case LOGIN: return postLogin(ex);
|
||||
case RESET_PW: return postResetPassword(ex);
|
||||
try {
|
||||
return switch (head) {
|
||||
case CREATE -> postCreate(ex);
|
||||
case OIDC -> postOIDC(ex, path);
|
||||
case IMPERSONATE -> impersonate(ex, targetId);
|
||||
case LOGIN -> postLogin(ex);
|
||||
case RESET_PW -> postResetPassword(ex);
|
||||
case null, default -> super.doPost(path,ex);
|
||||
};
|
||||
} catch (UmbrellaException e){
|
||||
return send(ex,e);
|
||||
}
|
||||
return super.doPost(path, ex);
|
||||
}
|
||||
|
||||
private boolean exchangeToken(HttpExchange ex) throws IOException {
|
||||
@@ -303,7 +306,7 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
|
||||
}
|
||||
|
||||
private boolean getOIDC(HttpExchange ex, UmbrellaUser user, Path path) throws IOException {
|
||||
private boolean getOIDC(HttpExchange ex, UmbrellaUser user, Path path) throws IOException, UmbrellaException {
|
||||
var head = path.pop();
|
||||
return switch (head){
|
||||
case BUTTONS -> getOidcButtons(ex);
|
||||
@@ -315,15 +318,9 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
};
|
||||
}
|
||||
|
||||
private boolean getOIDC(HttpExchange ex, UmbrellaUser user, String serviceId) throws IOException {
|
||||
if (!(user instanceof DbUser dbUser && dbUser.permissions().contains(MANAGE_LOGIN_SERVICES))) return forbidden(ex);
|
||||
try {
|
||||
private boolean getOIDC(HttpExchange ex, UmbrellaUser user, String serviceId) throws IOException, UmbrellaException {
|
||||
if (!(user instanceof DbUser dbUser && dbUser.permissions().contains(MANAGE_LOGIN_SERVICES))) throw forbidden("You are not allowed to manage that service!");
|
||||
return sendContent(ex,logins.loadLoginService(serviceId).toMap());
|
||||
} catch (UmbrellaException e) {
|
||||
return send(ex,e);
|
||||
} catch (IOException e) {
|
||||
return sendContent(ex,HTTP_SERVER_ERROR,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject getOidcConfig(LoginService service) throws UmbrellaException {
|
||||
@@ -375,40 +372,26 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getServiceList(HttpExchange ex, UmbrellaUser user) throws IOException {
|
||||
if (!(user instanceof DbUser dbUser && dbUser.permissions().contains(MANAGE_LOGIN_SERVICES))) return forbidden(ex);
|
||||
try {
|
||||
private boolean getServiceList(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException {
|
||||
if (!(user instanceof DbUser dbUser && dbUser.permissions().contains(MANAGE_LOGIN_SERVICES))) throw forbidden("You are not allowed to manage that service!");
|
||||
var services = logins.listLoginServices().stream().map(LoginService::toMap);
|
||||
return sendContent(ex,services);
|
||||
} catch (UmbrellaException e) {
|
||||
return send(ex,e);
|
||||
} catch (IOException e) {
|
||||
return sendContent(ex,HTTP_SERVER_ERROR,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getUserList(HttpExchange ex, UmbrellaUser user) throws IOException {
|
||||
if (!(user instanceof DbUser dbUser && dbUser.permissions().contains(LIST_USERS))) return sendContent(ex,HTTP_FORBIDDEN,"You are not allowed to list users!");
|
||||
try {
|
||||
private boolean getUserList(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException {
|
||||
if (!(user instanceof DbUser dbUser && dbUser.permissions().contains(LIST_USERS))) throw forbidden("You are not allowed to list users!");
|
||||
var list = users.list(0, null).stream().map(UmbrellaUser::toMap).toList();
|
||||
return sendContent(ex,list);
|
||||
} catch (UmbrellaException e) {
|
||||
return send(ex,e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean impersonate(HttpExchange ex, Long targetId) throws IOException {
|
||||
try {
|
||||
private boolean impersonate(HttpExchange ex, Long targetId) throws IOException, UmbrellaException {
|
||||
var requestingUser = loadUser(ex);
|
||||
if (!(requestingUser.isPresent() && requestingUser.get() instanceof DbUser dbUser)) return unauthorized(ex);
|
||||
if (!dbUser.permissions().contains(PERMISSION.IMPERSONATE)) return forbidden(ex);
|
||||
if (!dbUser.permissions().contains(PERMISSION.IMPERSONATE)) throw forbidden("You are not allowed to impersonate other users!");
|
||||
if (targetId == null) return sendContent(ex,HTTP_UNPROCESSABLE,"user id missing");
|
||||
var targetUser = users.load(targetId);
|
||||
users.getSession(targetUser).cookie().addTo(ex);
|
||||
return sendContent(ex,targetUser.toMap());
|
||||
} catch (UmbrellaException e) {
|
||||
return send(ex,e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean logout(HttpExchange ex, Optional<Token> optToken) throws IOException {
|
||||
@@ -456,12 +439,10 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean postCreate(HttpExchange ex) throws IOException {
|
||||
|
||||
try {
|
||||
private boolean postCreate(HttpExchange ex) throws IOException, UmbrellaException {
|
||||
var optUser = loadUser(ex);
|
||||
if (!(optUser.isPresent() && optUser.get() instanceof DbUser dbUser)) return unauthorized(ex);
|
||||
if (!dbUser.permissions().contains(PERMISSION.CREATE_USERS)) return forbidden(ex);
|
||||
if (!dbUser.permissions().contains(PERMISSION.CREATE_USERS)) throw forbidden("You are not allowed to create new users!");
|
||||
var json = json(ex);
|
||||
|
||||
if (json.has(USER)) json = json.getJSONObject(USER);
|
||||
@@ -475,9 +456,6 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
|
||||
var user = users.save(newUser);
|
||||
return sendContent(ex,HTTP_OK,user);
|
||||
} catch (UmbrellaException e) {
|
||||
return send(ex,e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean postResetPassword(HttpExchange ex) throws IOException {
|
||||
@@ -503,9 +481,8 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
return ok(ex);
|
||||
}
|
||||
|
||||
private boolean patchService(HttpExchange ex, String serviceName, UmbrellaUser requestingUser) throws IOException {
|
||||
if (!(requestingUser instanceof DbUser user && user.permissions().contains(MANAGE_LOGIN_SERVICES))) return forbidden(ex);
|
||||
try {
|
||||
private boolean patchService(HttpExchange ex, String serviceName, UmbrellaUser requestingUser) throws IOException, UmbrellaException {
|
||||
if (!(requestingUser instanceof DbUser user && user.permissions().contains(MANAGE_LOGIN_SERVICES))) throw forbidden("You are not allowed to manage that service!");
|
||||
var json = json(ex);
|
||||
if (!json.has(NAME) || !(json.get(NAME) instanceof String name) || name.isBlank()) throw missingFieldException(NAME);
|
||||
if (!json.has(URL) || !(json.get(URL) instanceof String url) || url.isBlank()) throw missingFieldException(URL);
|
||||
@@ -513,9 +490,6 @@ public class UserModule extends BaseHandler implements UserService {
|
||||
if (!json.has(CLIENT_SECRET) || !(json.get(CLIENT_SECRET) instanceof String secret) || secret.isBlank()) throw missingFieldException(CLIENT_SECRET);
|
||||
var service = logins.save(new LoginService(name,url,clientId,secret, DEFAULT_FIELD));
|
||||
return sendContent(ex,service.toMap());
|
||||
} catch (UmbrellaException e) {
|
||||
return send(ex,e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean postLogin(HttpExchange ex) throws IOException {
|
||||
|
||||
Reference in New Issue
Block a user