From b51fb2ed896885c2c405522708f8330c23de897b Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Mon, 6 Jul 2026 15:53:55 +0200 Subject: [PATCH] implemented patching of project tasks when new user is added to project Signed-off-by: Stephan Richter --- .../umbrella/core/api/TaskService.java | 2 ++ .../umbrella/core/constants/Field.java | 9 ++++---- frontend/src/Components/Autocomplete.svelte | 3 ++- .../src/Components/PermissionEditor.svelte | 3 ++- frontend/src/Components/UserSelector.svelte | 3 ++- frontend/src/routes/project/View.svelte | 19 ++++++++++++++-- .../umbrella/project/ProjectModule.java | 13 ++++++++++- .../de/srsoftware/umbrella/task/SqliteDb.java | 2 +- .../srsoftware/umbrella/task/TaskModule.java | 22 +++++++++++++++++++ translations/src/main/resources/de.json | 1 + translations/src/main/resources/en.json | 1 + .../main/resources/web/css/default-color.css | 5 +++++ 12 files changed, 72 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/TaskService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/TaskService.java index a919208d..59f65fe1 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/TaskService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/TaskService.java @@ -3,11 +3,13 @@ package de.srsoftware.umbrella.core.api; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Task; +import de.srsoftware.umbrella.core.model.UmbrellaUser; import java.util.Collection; import java.util.List; import java.util.Map; public interface TaskService { + void clonePermissions(long project_id, UmbrellaUser source, UmbrellaUser dest); Map listCompanyTasks(long companyId) throws UmbrellaException; Map listProjectTasks(long projectId) throws UmbrellaException; Map load(Collection taskIds); diff --git a/core/src/main/java/de/srsoftware/umbrella/core/constants/Field.java b/core/src/main/java/de/srsoftware/umbrella/core/constants/Field.java index c2e35b0a..7f2b2663 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/constants/Field.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/constants/Field.java @@ -77,10 +77,11 @@ public class Field { public static final String HEAD = "head"; public static final String HOURS = "hours"; - public static final String ID = "id"; - public static final String INSTANTLY = "instantly"; - public static final String ITEM = "item"; - public static final String ITEM_CODE = "item_code"; + public static final String ID = "id"; + public static final String INHERIT_FROM = "inherit_from"; + public static final String INSTANTLY = "instantly"; + public static final String ITEM = "item"; + public static final String ITEM_CODE = "item_code"; public static final String KEY = "key"; diff --git a/frontend/src/Components/Autocomplete.svelte b/frontend/src/Components/Autocomplete.svelte index 1fe556ed..4b908650 100644 --- a/frontend/src/Components/Autocomplete.svelte +++ b/frontend/src/Components/Autocomplete.svelte @@ -4,6 +4,7 @@ let { + enabled = true, id = null, autofocus = false, getCandidates = dummyGetCandidates, @@ -148,7 +149,7 @@ - + {#if candidates && candidates.length > 0}
    {#each candidates as candidate,i} diff --git a/frontend/src/Components/PermissionEditor.svelte b/frontend/src/Components/PermissionEditor.svelte index f28dc2c8..114f281d 100644 --- a/frontend/src/Components/PermissionEditor.svelte +++ b/frontend/src/Components/PermissionEditor.svelte @@ -10,6 +10,7 @@ let { addMember = (entry) => console.log(`no handler for addMember(${entry})`), + enabled = true, dropMember = (member) => console.log(`no handler for dropMember(${member})`), getCandidates = defaultGetCandidates, members, @@ -61,7 +62,7 @@ {t('add_object',{object:t('member')})} - + diff --git a/frontend/src/Components/UserSelector.svelte b/frontend/src/Components/UserSelector.svelte index 836db2f3..04aca6a6 100644 --- a/frontend/src/Components/UserSelector.svelte +++ b/frontend/src/Components/UserSelector.svelte @@ -6,6 +6,7 @@ import Autocomplete from './Autocomplete.svelte'; let { + enabled = true, getCandidates = async text => {}, heading = t('add_object',{object:t('user')}), users = $bindable({}) @@ -37,7 +38,7 @@ {heading} - + diff --git a/frontend/src/routes/project/View.svelte b/frontend/src/routes/project/View.svelte index 52209178..b2f2b023 100644 --- a/frontend/src/routes/project/View.svelte +++ b/frontend/src/routes/project/View.svelte @@ -25,12 +25,13 @@ let tasks = $state(null); let show_closed = $state(false); let new_color = $state({tag:null,color:'#00aa00'}) + let inherit_from = $state(0); let new_state = $state({code:null,name:null}) let state_available=$derived(new_state.name && new_state.code && !project.allowed_states[new_state.code]); async function addMember(user){ - return await update({new_member:+user.id}); + return await update({new_member:+user.id,inherit_from}); } async function addState(){ @@ -239,7 +240,21 @@
    {t('members')}
    - + +
    {#if project.allowed_states} {#each Object.keys(project.allowed_states) as key,idx} diff --git a/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java b/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java index e1d979a8..20a5e6eb 100644 --- a/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java +++ b/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java @@ -22,6 +22,7 @@ import static de.srsoftware.umbrella.messagebus.MessageBus.messageBus; import static de.srsoftware.umbrella.messagebus.events.Event.EventType.CREATE; import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE; import static java.lang.Boolean.TRUE; +import static java.lang.System.Logger.Level.WARNING; import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; import com.sun.net.httpserver.HttpExchange; @@ -138,6 +139,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { private void dropMember(Project project, long userId) { if (project.members().get(userId).permission() == OWNER) throw forbidden("You may not remove the owner of the project"); projectDb.dropMember(project.id(),userId); + LOG.log(WARNING,"Removing member from project tasks not implemented"); // TODO project.members().remove(userId); } @@ -223,7 +225,16 @@ public class ProjectModule extends BaseHandler implements ProjectService { UmbrellaUser newMember = null; if (json.has(DROP_MEMBER) && json.get(DROP_MEMBER) instanceof Number id) dropMember(project,id.longValue()); if (json.has(MEMBERS) && json.get(MEMBERS) instanceof JSONObject memberJson) patchMembers(project,memberJson); - if (json.has(NEW_MEMBER) && json.get(NEW_MEMBER) instanceof Number num) newMember = addMember(project,num.longValue()); + if (json.has(NEW_MEMBER) && json.get(NEW_MEMBER) instanceof Number num) { + UmbrellaUser inheritFrom = null; + if (json.has(INHERIT_FROM)){ + if (!(json.get(INHERIT_FROM) instanceof Number uid)) throw invalidField(INHERIT_FROM,Text.NUMBER); + inheritFrom = userService().loadUser(uid.longValue()); + if (!project.hasMember(inheritFrom)) throw notAmember(inheritFrom); + } + newMember = addMember(project,num.longValue()); + if (inheritFrom != null) taskService().clonePermissions(project.id(),inheritFrom,newMember); + } project = projectDb.save(project.patch(json), user); messageBus().dispatch(newMember != null ? new ProjectEvent(user,project,newMember) : new ProjectEvent(user,project, old)); diff --git a/task/src/main/java/de/srsoftware/umbrella/task/SqliteDb.java b/task/src/main/java/de/srsoftware/umbrella/task/SqliteDb.java index 2ef242c9..6f35a3f9 100644 --- a/task/src/main/java/de/srsoftware/umbrella/task/SqliteDb.java +++ b/task/src/main/java/de/srsoftware/umbrella/task/SqliteDb.java @@ -260,7 +260,7 @@ CREATE TABLE IF NOT EXISTS {0} ( try { var query = select(ALL).from(TABLE_TASKS).leftJoin(ID,TABLE_TASKS_USERS,TASK_ID).where(USER_ID,equal(userId)); if (!showClosed) query.where(STATUS,lessThan(COMPLETE.code())); - var rs = query.sort("(CASE due_date WHEN \"\" THEN '9999-99-99' ELSE IFNULL(due_date,'9999-99-99') END), status COLLATE NOCASE").limit(limit).skip(offset).exec(db); + var rs = query.sort("(CASE due_date WHEN \"\" THEN '9999-99-99' ELSE IFNULL(due_date,'9999-99-99') END), status COLLATE NOCASE").limit(limit == null ? -1 : limit).skip(offset).exec(db); var map = new ArrayList(); while (rs.next()) map.add(Task.of(rs)); rs.close(); diff --git a/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java b/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java index a7071e29..4863a8fc 100644 --- a/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java +++ b/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java @@ -66,6 +66,28 @@ public class TaskModule extends BaseHandler implements TaskService { return user; } + @Override + public void clonePermissions(long project_id, UmbrellaUser source, UmbrellaUser dest) { + var taskMap = taskDb.listProjectTasks(project_id,null,true); + loadMembers(taskMap.values()); + for (var task : taskMap.values()) { + var memberShip = task.members().get(source.id()); + + Permission granted = memberShip == null ? EDIT : switch (memberShip.permission()) { + case ASSIGNEE, + EDIT, + OWNER -> + EDIT; + case READ_ONLY -> + READ_ONLY; + }; + var newMember = new Member(dest, granted); + task.members().put(dest.id(), newMember); + task.dirty(MEMBERS); + taskDb.save(task); + } + } + private boolean deleteTask(HttpExchange ex, long taskId, UmbrellaUser user) throws IOException { var task = loadMembers(taskDb.load(taskId)); var member = task.members().get(user.id()); diff --git a/translations/src/main/resources/de.json b/translations/src/main/resources/de.json index 27f7474a..0be17a0d 100644 --- a/translations/src/main/resources/de.json +++ b/translations/src/main/resources/de.json @@ -335,6 +335,7 @@ "select_customer": "Kunde auswählen", "select_property": "Eigenschaft auswählen", "select_state": "Status wählen", + "select user": "Nutzer auswählen", "send_document": "Dokument versenden", "sender": "Absender", "sender_bank_account": "Bankverbindung", diff --git a/translations/src/main/resources/en.json b/translations/src/main/resources/en.json index 666d4ef2..9462038d 100644 --- a/translations/src/main/resources/en.json +++ b/translations/src/main/resources/en.json @@ -335,6 +335,7 @@ "select_customer": "select customer", "select_property": "select property", "select_state": "select state", + "select user": "select user", "send_document": "send document", "sender": "sender", "sender_bank_account": "bank account", diff --git a/web/src/main/resources/web/css/default-color.css b/web/src/main/resources/web/css/default-color.css index 9716b73b..9e9551d2 100644 --- a/web/src/main/resources/web/css/default-color.css +++ b/web/src/main/resources/web/css/default-color.css @@ -26,6 +26,11 @@ input{ color: orange; } +input:disabled{ + border: 1px solid gray; + background: #444; +} + legend.date, legend.time{ background-color: black;