preparing to re-implement message settings
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -67,7 +67,7 @@ public class Application {
|
|||||||
new Translations().bindPath("/api/translations").on(server);
|
new Translations().bindPath("/api/translations").on(server);
|
||||||
new JournalModule(config).bindPath("/api/journal").on(server);
|
new JournalModule(config).bindPath("/api/journal").on(server);
|
||||||
new MessageApi().bindPath("/api/bus").on(server);
|
new MessageApi().bindPath("/api/bus").on(server);
|
||||||
new MessageSystem(config);
|
new MessageSystem(config).bindPath("/api/message").on(server);
|
||||||
new UserModule(config).bindPath("/api/user").on(server);
|
new UserModule(config).bindPath("/api/user").on(server);
|
||||||
new TagModule(config).bindPath("/api/tags").on(server);
|
new TagModule(config).bindPath("/api/tags").on(server);
|
||||||
new BookmarkApi(config).bindPath("/api/bookmark").on(server);
|
new BookmarkApi(config).bindPath("/api/bookmark").on(server);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
package de.srsoftware.umbrella.core.constants;
|
package de.srsoftware.umbrella.core.constants;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
@@ -17,6 +19,7 @@ public class Constants {
|
|||||||
public static final String NO_CACHE = "no-cache";
|
public static final String NO_CACHE = "no-cache";
|
||||||
public static final String NONE = "none";
|
public static final String NONE = "none";
|
||||||
public static final String TABLE_SETTINGS = "settings";
|
public static final String TABLE_SETTINGS = "settings";
|
||||||
|
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
|
||||||
public static final String UMBRELLA = "Umbrella";
|
public static final String UMBRELLA = "Umbrella";
|
||||||
public static final String UTF8 = UTF_8.displayName();
|
public static final String UTF8 = UTF_8.displayName();
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ public class EmailAddress {
|
|||||||
return obj instanceof EmailAddress other && Objects.equals(email,other.email);
|
return obj instanceof EmailAddress other && Objects.equals(email,other.email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return email.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return email;
|
return email;
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import static de.srsoftware.umbrella.core.model.Translatable.t;
|
|||||||
import static java.text.MessageFormat.format;
|
import static java.text.MessageFormat.format;
|
||||||
|
|
||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -17,8 +19,9 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class Envelope {
|
public class Envelope {
|
||||||
private Message message;
|
private final Message message;
|
||||||
private Set<User> receivers;
|
private final Set<User> receivers;
|
||||||
|
private final LocalDateTime time;
|
||||||
|
|
||||||
public Envelope(Message message, User receiver){
|
public Envelope(Message message, User receiver){
|
||||||
this(message,new HashSet<>(Set.of(receiver)));
|
this(message,new HashSet<>(Set.of(receiver)));
|
||||||
@@ -27,6 +30,7 @@ public class Envelope {
|
|||||||
public Envelope(Message message, HashSet<User> receivers) {
|
public Envelope(Message message, HashSet<User> receivers) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.receivers = receivers;
|
this.receivers = receivers;
|
||||||
|
time = LocalDateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,6 +65,10 @@ public class Envelope {
|
|||||||
return receivers;
|
return receivers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LocalDateTime time(){
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return format("{0} (to: {1}), subject: {2}",getClass().getSimpleName(),receivers.stream().map(User::email).map(EmailAddress::toString).collect(Collectors.joining(", ")),message.subject());
|
return format("{0} (to: {1}), subject: {2}",getClass().getSimpleName(),receivers.stream().map(User::email).map(EmailAddress::toString).collect(Collectors.joining(", ")),message.subject());
|
||||||
|
|||||||
@@ -23,6 +23,17 @@ public class User {
|
|||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof User user)) return false;
|
||||||
|
return email.equals(user.email);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return email.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
public String language(){
|
public String language(){
|
||||||
return lang;
|
return lang;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
import Kanban from "./routes/project/Kanban.svelte";
|
import Kanban from "./routes/project/Kanban.svelte";
|
||||||
import Login from "./Components/Login.svelte";
|
import Login from "./Components/Login.svelte";
|
||||||
import Messages from "./routes/message/Messages.svelte";
|
import Messages from "./routes/message/Messages.svelte";
|
||||||
|
import MsgSettings from "./routes/message/Settings.svelte";
|
||||||
import Menu from "./Components/Menu.svelte";
|
import Menu from "./Components/Menu.svelte";
|
||||||
import NewPage from "./routes/wiki/AddPage.svelte";
|
import NewPage from "./routes/wiki/AddPage.svelte";
|
||||||
import Notes from "./routes/notes/Index.svelte";
|
import Notes from "./routes/notes/Index.svelte";
|
||||||
@@ -91,7 +92,8 @@
|
|||||||
<Route path="/document/:id/send" component={SendDoc} />
|
<Route path="/document/:id/send" component={SendDoc} />
|
||||||
<Route path="/document/:id/view" component={ViewDoc} />
|
<Route path="/document/:id/view" component={ViewDoc} />
|
||||||
<Route path="/files/*" component={FileIndex} />
|
<Route path="/files/*" component={FileIndex} />
|
||||||
<Route path="/message/settings" component={Messages} />
|
<Route path="/message" component={Messages} />
|
||||||
|
<Route path="/message/settings" component={MsgSettings} />
|
||||||
<Route path="/notes" component={Notes} />
|
<Route path="/notes" component={Notes} />
|
||||||
<Route path="/project" component={ProjectList} />
|
<Route path="/project" component={ProjectList} />
|
||||||
<Route path="/project/add" component={ProjectAdd} />
|
<Route path="/project/add" component={ProjectAdd} />
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ onMount(fetchModules);
|
|||||||
<a href="/wiki" {onclick} class="wiki">{t('wiki')}</a>
|
<a href="/wiki" {onclick} class="wiki">{t('wiki')}</a>
|
||||||
<a href="/contact" {onclick} class="contact">{t('contacts')}</a>
|
<a href="/contact" {onclick} class="contact">{t('contacts')}</a>
|
||||||
<a href="/stock" {onclick} class="stock">{t('stock')}</a>
|
<a href="/stock" {onclick} class="stock">{t('stock')}</a>
|
||||||
|
<a href="/message" {onclick} class="message">{t('messages')}</a>
|
||||||
{#if user.id == 2}
|
{#if user.id == 2}
|
||||||
<a href="https://svelte.dev/tutorial/svelte/state" target="_blank">{t('tutorial')}</a>
|
<a href="https://svelte.dev/tutorial/svelte/state" target="_blank">{t('tutorial')}</a>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,7 +1,31 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { useTinyRouter } from 'svelte-tiny-router';
|
||||||
import { t } from '../../translations.svelte';
|
import { t } from '../../translations.svelte';
|
||||||
|
import { api, get } from '../../urls.svelte';
|
||||||
|
import { error, yikes } from '../../warn.svelte';
|
||||||
|
|
||||||
|
let router = useTinyRouter();
|
||||||
|
|
||||||
|
async function load(){
|
||||||
|
const url = api('message');
|
||||||
|
const res = await get(url);
|
||||||
|
if (res.ok){
|
||||||
|
let json = await res.json();
|
||||||
|
console.log(json);
|
||||||
|
yikes();
|
||||||
|
} else {
|
||||||
|
error(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSettings(){
|
||||||
|
router.navigate("message/settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(load);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{t('messages')}</legend>
|
<legend>{t('messages')} <button onclick={showSettings}>{t('settings')}</button></legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
7
frontend/src/routes/message/Settings.svelte
Normal file
7
frontend/src/routes/message/Settings.svelte
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import { t } from '../../translations.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{t('message settingss')}</legend>
|
||||||
|
</fieldset>
|
||||||
@@ -1,20 +1,27 @@
|
|||||||
/* © SRSoftware 2025 */
|
/* © SRSoftware 2025 */
|
||||||
package de.srsoftware.umbrella.message;
|
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.ConnectionProvider.connect;
|
||||||
|
import static de.srsoftware.umbrella.core.constants.Constants.TIME_FORMATTER;
|
||||||
import static de.srsoftware.umbrella.core.constants.Constants.UTF8;
|
import static de.srsoftware.umbrella.core.constants.Constants.UTF8;
|
||||||
|
import static de.srsoftware.umbrella.core.constants.Field.*;
|
||||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingConfig;
|
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingConfig;
|
||||||
import static de.srsoftware.umbrella.core.model.Translatable.t;
|
import static de.srsoftware.umbrella.core.model.Translatable.t;
|
||||||
import static de.srsoftware.umbrella.message.Constants.*;
|
import static de.srsoftware.umbrella.message.Constants.*;
|
||||||
import static de.srsoftware.umbrella.messagebus.MessageBus.messageBus;
|
import static de.srsoftware.umbrella.messagebus.MessageBus.messageBus;
|
||||||
import static java.lang.System.Logger.Level.*;
|
import static java.lang.System.Logger.Level.*;
|
||||||
|
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import de.srsoftware.configuration.Configuration;
|
import de.srsoftware.configuration.Configuration;
|
||||||
|
import de.srsoftware.tools.Path;
|
||||||
|
import de.srsoftware.tools.SessionToken;
|
||||||
|
import de.srsoftware.umbrella.core.BaseHandler;
|
||||||
import de.srsoftware.umbrella.core.ModuleRegistry;
|
import de.srsoftware.umbrella.core.ModuleRegistry;
|
||||||
import de.srsoftware.umbrella.core.api.PostBox;
|
import de.srsoftware.umbrella.core.api.PostBox;
|
||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import de.srsoftware.umbrella.core.model.Envelope;
|
import de.srsoftware.umbrella.core.model.Envelope;
|
||||||
|
import de.srsoftware.umbrella.core.model.Token;
|
||||||
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
||||||
import de.srsoftware.umbrella.core.model.User;
|
import de.srsoftware.umbrella.core.model.User;
|
||||||
import de.srsoftware.umbrella.message.model.CombinedMessage;
|
import de.srsoftware.umbrella.message.model.CombinedMessage;
|
||||||
@@ -29,10 +36,13 @@ import jakarta.mail.internet.MimeBodyPart;
|
|||||||
import jakarta.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import jakarta.mail.internet.MimeMultipart;
|
import jakarta.mail.internet.MimeMultipart;
|
||||||
import jakarta.mail.util.ByteArrayDataSource;
|
import jakarta.mail.util.ByteArrayDataSource;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
public class MessageSystem implements PostBox, EventListener {
|
public class MessageSystem extends BaseHandler implements PostBox, EventListener {
|
||||||
public static final System.Logger LOG = System.getLogger(MessageSystem.class.getSimpleName());
|
public static final System.Logger LOG = System.getLogger(MessageSystem.class.getSimpleName());
|
||||||
private final Timer timer = new Timer();
|
private final Timer timer = new Timer();
|
||||||
|
|
||||||
@@ -91,6 +101,37 @@ public class MessageSystem implements PostBox, EventListener {
|
|||||||
messageBus().register(this);
|
messageBus().register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doGet(Path path, HttpExchange ex) throws IOException {
|
||||||
|
addCors(ex);
|
||||||
|
try {
|
||||||
|
Optional<Token> token = SessionToken.from(ex).map(Token::of);
|
||||||
|
var user = ModuleRegistry.userService().loadUser(token);
|
||||||
|
if (user.isEmpty()) return unauthorized(ex);
|
||||||
|
var head = path.pop();
|
||||||
|
return switch (head){
|
||||||
|
case null -> listMessages(ex,user.get());
|
||||||
|
default -> super.doGet(path,ex);
|
||||||
|
};
|
||||||
|
} catch (NumberFormatException e){
|
||||||
|
return sendContent(ex,HTTP_BAD_REQUEST,"Invalid project id");
|
||||||
|
} catch (UmbrellaException e){
|
||||||
|
return send(ex,e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean listMessages(HttpExchange ex, UmbrellaUser user) throws IOException {
|
||||||
|
var messages = queue.stream().filter(e -> e.isFor(user)).map(e -> summary(e, user.language())).toList();
|
||||||
|
return sendContent(ex,messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JSONObject summary(Envelope envelope, String lang) {
|
||||||
|
var sender = envelope.message().sender().name();
|
||||||
|
var subject = envelope.message().subject().translate(lang);
|
||||||
|
var time = envelope.time().format(TIME_FORMATTER);
|
||||||
|
return new JSONObject(Map.of(SENDER,sender,SUBJECT,subject,TIMESTAMP,time));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(Event<?> event) {
|
public void onEvent(Event<?> event) {
|
||||||
for (var user : event.audience()){
|
for (var user : event.audience()){
|
||||||
@@ -102,12 +143,6 @@ public class MessageSystem implements PostBox, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(Envelope envelope) {
|
|
||||||
queue.add(envelope);
|
|
||||||
new Thread(() -> processMessages(null)).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void processMessages(Integer scheduledHour) {
|
private synchronized void processMessages(Integer scheduledHour) {
|
||||||
LOG.log(INFO,"Running {0}…",scheduledHour == null ? "instantly" : "scheduled at "+scheduledHour);
|
LOG.log(INFO,"Running {0}…",scheduledHour == null ? "instantly" : "scheduled at "+scheduledHour);
|
||||||
var queue = new ArrayList<>(this.queue);
|
var queue = new ArrayList<>(this.queue);
|
||||||
@@ -149,24 +184,6 @@ public class MessageSystem implements PostBox, EventListener {
|
|||||||
if (scheduledHour != null) new SubmissionTask(scheduledHour).schedule();
|
if (scheduledHour != null) new SubmissionTask(scheduledHour).schedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Session session() {
|
|
||||||
if (session == null){
|
|
||||||
Properties props = new Properties();
|
|
||||||
props.put(HOST, host);
|
|
||||||
props.put(PORT, port);
|
|
||||||
props.put(AUTH, true);
|
|
||||||
props.put(SSL, true);
|
|
||||||
props.put(ENVELOPE_FROM,from);
|
|
||||||
session = Session.getInstance(props);
|
|
||||||
}
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDebugAddress(String newVal) {
|
|
||||||
this.debugAddress = newVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void send(CombinedMessage message, Date date) throws MessagingException {
|
private void send(CombinedMessage message, Date date) throws MessagingException {
|
||||||
var receiver = message.receiver();
|
var receiver = message.receiver();
|
||||||
LOG.log(TRACE,"Sending combined message to {0}…",receiver);
|
LOG.log(TRACE,"Sending combined message to {0}…",receiver);
|
||||||
@@ -202,4 +219,28 @@ public class MessageSystem implements PostBox, EventListener {
|
|||||||
Transport.send(msg,user,pass);
|
Transport.send(msg,user,pass);
|
||||||
LOG.log(DEBUG, "Sent message to {0}.", receiver);
|
LOG.log(DEBUG, "Sent message to {0}.", receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(Envelope envelope) {
|
||||||
|
queue.add(envelope);
|
||||||
|
new Thread(() -> processMessages(null)).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Session session() {
|
||||||
|
if (session == null){
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.put(HOST, host);
|
||||||
|
props.put(PORT, port);
|
||||||
|
props.put(AUTH, true);
|
||||||
|
props.put(SSL, true);
|
||||||
|
props.put(ENVELOPE_FROM,from);
|
||||||
|
session = Session.getInstance(props);
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDebugAddress(String newVal) {
|
||||||
|
this.debugAddress = newVal;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ nav a.mark::before {
|
|||||||
content: " ";
|
content: " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav a.message::before {
|
||||||
|
content: " ";
|
||||||
|
}
|
||||||
|
|
||||||
nav a.note::before {
|
nav a.note::before {
|
||||||
content: " ";
|
content: " ";
|
||||||
}
|
}
|
||||||
@@ -133,6 +137,7 @@ nav a.contact,
|
|||||||
nav a.doc,
|
nav a.doc,
|
||||||
nav a.file,
|
nav a.file,
|
||||||
nav a.mark,
|
nav a.mark,
|
||||||
|
nav a.message,
|
||||||
nav a.note,
|
nav a.note,
|
||||||
nav a.project,
|
nav a.project,
|
||||||
nav a.stock,
|
nav a.stock,
|
||||||
|
|||||||
Reference in New Issue
Block a user