Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa793b1b2a | ||
|
|
79195494cd | ||
|
|
7788df380e | ||
|
|
562612c070 | ||
|
|
5eead469d3 | ||
|
|
3a430a4d52 | ||
|
|
95c133389d | ||
|
|
5a6d9c4e80 | ||
|
|
05de8cfea0 | ||
|
|
2fd2566560 | ||
|
|
2884e85c4b | ||
|
|
f91f9a8ea3 | ||
|
|
20bc7a697e | ||
|
|
f3391b3b50 | ||
|
|
aca0d41ef0 | ||
|
|
cd218cc3b9 | ||
|
|
fd8187630d | ||
|
|
d497305da8 | ||
|
|
4ddfd337b6 | ||
|
|
55ea05c2ff | ||
|
|
e6c0bc0139 | ||
|
|
ac0b61dca0 | ||
|
|
07492d34de | ||
|
|
9e4158ad19 | ||
|
|
b81d518a2b | ||
|
|
e17fdbc619 | ||
|
|
433ea6ddd3 | ||
|
|
6249cdb7b9 |
@@ -3,11 +3,13 @@ package de.srsoftware.umbrella.core.api;
|
|||||||
|
|
||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import de.srsoftware.umbrella.core.model.Task;
|
import de.srsoftware.umbrella.core.model.Task;
|
||||||
|
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface TaskService {
|
public interface TaskService {
|
||||||
|
void clonePermissions(long project_id, UmbrellaUser source, UmbrellaUser dest);
|
||||||
Map<Long, Task> listCompanyTasks(long companyId) throws UmbrellaException;
|
Map<Long, Task> listCompanyTasks(long companyId) throws UmbrellaException;
|
||||||
Map<Long, Task> listProjectTasks(long projectId) throws UmbrellaException;
|
Map<Long, Task> listProjectTasks(long projectId) throws UmbrellaException;
|
||||||
Map<Long, Task> load(Collection<Long> taskIds);
|
Map<Long, Task> load(Collection<Long> taskIds);
|
||||||
|
|||||||
@@ -77,10 +77,11 @@ public class Field {
|
|||||||
public static final String HEAD = "head";
|
public static final String HEAD = "head";
|
||||||
public static final String HOURS = "hours";
|
public static final String HOURS = "hours";
|
||||||
|
|
||||||
public static final String ID = "id";
|
public static final String ID = "id";
|
||||||
public static final String INSTANTLY = "instantly";
|
public static final String INHERIT_FROM = "inherit_from";
|
||||||
public static final String ITEM = "item";
|
public static final String INSTANTLY = "instantly";
|
||||||
public static final String ITEM_CODE = "item_code";
|
public static final String ITEM = "item";
|
||||||
|
public static final String ITEM_CODE = "item_code";
|
||||||
|
|
||||||
public static final String KEY = "key";
|
public static final String KEY = "key";
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ public class Path {
|
|||||||
public static final String PROPERTY = "property";
|
public static final String PROPERTY = "property";
|
||||||
public static final String PURPOSES = "purposes";
|
public static final String PURPOSES = "purposes";
|
||||||
|
|
||||||
public static final String READ = "read";
|
public static final String READ = "read";
|
||||||
public static final String REDIRECT = "redirect";
|
public static final String REDIRECT = "redirect";
|
||||||
|
|
||||||
public static final String SEARCH = "search";
|
public static final String SEARCH = "search";
|
||||||
public static final String SELECT = "select";
|
public static final String SELECT = "select";
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
*.db-journal
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,74 +0,0 @@
|
|||||||
{
|
|
||||||
"umbrella": {
|
|
||||||
"base_url": "http://127.0.0.1:5173",
|
|
||||||
"logging": {
|
|
||||||
"rootLevel": "INFO"
|
|
||||||
},
|
|
||||||
"http": {
|
|
||||||
"port": 8080
|
|
||||||
},
|
|
||||||
"threads": 16,
|
|
||||||
"modules": {
|
|
||||||
"accounting": {
|
|
||||||
"database": "demodata/accounting.db"
|
|
||||||
},
|
|
||||||
"bookmark": {
|
|
||||||
"database": "demodata/bookmark.db"
|
|
||||||
},
|
|
||||||
"company": {
|
|
||||||
"database": "demodata/company.db"
|
|
||||||
},
|
|
||||||
"contact": {
|
|
||||||
"database": "demodata/contacts.db"
|
|
||||||
},
|
|
||||||
"document": {
|
|
||||||
"database": "demodata/documents.db",
|
|
||||||
"templates": "demodata/templates"
|
|
||||||
},
|
|
||||||
"files": {
|
|
||||||
"database": "demodata/files.db",
|
|
||||||
"base_dir": "demodata/filestore"
|
|
||||||
},
|
|
||||||
"journal": {
|
|
||||||
"database": "demodata/journal.db"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"database": "demodata/message.db",
|
|
||||||
"smtp": {
|
|
||||||
"from": "umbrella@example.com",
|
|
||||||
"host": "none",
|
|
||||||
"pass": "none",
|
|
||||||
"port": 587,
|
|
||||||
"user": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notes": {
|
|
||||||
"database": "demodata/notes.db"
|
|
||||||
},
|
|
||||||
"poll": {
|
|
||||||
"database": "demodata/poll.db"
|
|
||||||
},
|
|
||||||
"project": {
|
|
||||||
"database": "demodata/projects.db"
|
|
||||||
},
|
|
||||||
"stock": {
|
|
||||||
"database": "demodata/stock.db"
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"database": "demodata/tags.db"
|
|
||||||
},
|
|
||||||
"task": {
|
|
||||||
"database": "demodata/tasks.db"
|
|
||||||
},
|
|
||||||
"time": {
|
|
||||||
"database": "demodata/times.db"
|
|
||||||
},
|
|
||||||
"user": {
|
|
||||||
"database": "demodata/users.db"
|
|
||||||
},
|
|
||||||
"wiki": {
|
|
||||||
"database": "demodata/wiki.db"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
enabled = true,
|
||||||
id = null,
|
id = null,
|
||||||
autofocus = false,
|
autofocus = false,
|
||||||
getCandidates = dummyGetCandidates,
|
getCandidates = dummyGetCandidates,
|
||||||
@@ -115,7 +116,7 @@
|
|||||||
|
|
||||||
function select(index){
|
function select(index){
|
||||||
candidate = candidates[index];
|
candidate = candidates[index];
|
||||||
<disableDropdown></disableDropdown>();
|
disableDropdown();
|
||||||
onSelect(candidate);
|
onSelect(candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +149,7 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<span class="autocomplete">
|
<span class="autocomplete">
|
||||||
<input type="text" bind:value={candidate.display} {onkeyup} autofocus={autofocus} {id} {onblur} />
|
<input type="text" bind:value={candidate.display} {onkeyup} autofocus={autofocus} {id} {onblur} disabled={!enabled} />
|
||||||
{#if candidates && candidates.length > 0}
|
{#if candidates && candidates.length > 0}
|
||||||
<ul bind:this={list_elem} class="suggestions" tabindex="-1">
|
<ul bind:this={list_elem} class="suggestions" tabindex="-1">
|
||||||
{#each candidates as candidate,i}
|
{#each candidates as candidate,i}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
let {
|
let {
|
||||||
addMember = (entry) => console.log(`no handler for addMember(${entry})`),
|
addMember = (entry) => console.log(`no handler for addMember(${entry})`),
|
||||||
|
enabled = true,
|
||||||
dropMember = (member) => console.log(`no handler for dropMember(${member})`),
|
dropMember = (member) => console.log(`no handler for dropMember(${member})`),
|
||||||
getCandidates = defaultGetCandidates,
|
getCandidates = defaultGetCandidates,
|
||||||
members,
|
members,
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{t('add_object',{object:t('member')})}</td>
|
<td>{t('add_object',{object:t('member')})}</td>
|
||||||
<td>
|
<td>
|
||||||
<Autocomplete {getCandidates} onSelect={addMember} />
|
<Autocomplete {getCandidates} onSelect={addMember} {enabled} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import Autocomplete from './Autocomplete.svelte';
|
import Autocomplete from './Autocomplete.svelte';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
enabled = true,
|
||||||
getCandidates = async text => {},
|
getCandidates = async text => {},
|
||||||
heading = t('add_object',{object:t('user')}),
|
heading = t('add_object',{object:t('user')}),
|
||||||
users = $bindable({})
|
users = $bindable({})
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{heading}</td>
|
<td>{heading}</td>
|
||||||
<td>
|
<td>
|
||||||
<Autocomplete {getCandidates} {onSelect} />
|
<Autocomplete {getCandidates} {onSelect} {enabled} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -25,12 +25,13 @@
|
|||||||
let tasks = $state(null);
|
let tasks = $state(null);
|
||||||
let show_closed = $state(false);
|
let show_closed = $state(false);
|
||||||
let new_color = $state({tag:null,color:'#00aa00'})
|
let new_color = $state({tag:null,color:'#00aa00'})
|
||||||
|
let inherit_from = $state(0);
|
||||||
|
|
||||||
let new_state = $state({code:null,name:null})
|
let new_state = $state({code:null,name:null})
|
||||||
let state_available=$derived(new_state.name && new_state.code && !project.allowed_states[new_state.code]);
|
let state_available=$derived(new_state.name && new_state.code && !project.allowed_states[new_state.code]);
|
||||||
|
|
||||||
async function addMember(user){
|
async function addMember(user){
|
||||||
return await update({new_member:+user.id});
|
return await update({new_member:+user.id,inherit_from});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addState(){
|
async function addState(){
|
||||||
@@ -239,7 +240,16 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="em">{t('members')}</div>
|
<div class="em">{t('members')}</div>
|
||||||
<div class="em">
|
<div class="em">
|
||||||
<PermissionEditor members={project.members} {updatePermission} {addMember} {dropMember} />
|
<PermissionEditor members={project.members} {updatePermission} {addMember} {dropMember} enabled={inherit_from != 0}/>
|
||||||
|
<label>
|
||||||
|
└→ Inherit task permissions from:
|
||||||
|
<select bind:value={inherit_from}>
|
||||||
|
<option value={0}>{t('select user')}</option>
|
||||||
|
{#each Object.values(project.members) as member (member.user.id)}
|
||||||
|
<option value={member.user.id}>{member.user.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{#if project.allowed_states}
|
{#if project.allowed_states}
|
||||||
{#each Object.keys(project.allowed_states) as key,idx}
|
{#each Object.keys(project.allowed_states) as key,idx}
|
||||||
|
|||||||
@@ -130,6 +130,10 @@
|
|||||||
onMount(load);
|
onMount(load);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Umbrella – {t('Easylist')}: {tag}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<h2>{t('tasks_for_tag',{tag:decodeURI(tag)})}</h2>
|
<h2>{t('tasks_for_tag',{tag:decodeURI(tag)})}</h2>
|
||||||
|
|
||||||
<div class="easylist">
|
<div class="easylist">
|
||||||
|
|||||||
@@ -81,6 +81,10 @@
|
|||||||
router.navigate(`/project/${project.id}/view`)
|
router.navigate(`/project/${project.id}/view`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function gotoTask(tid){
|
||||||
|
if (id) router.navigate(`/task/${tid}/view`)
|
||||||
|
}
|
||||||
|
|
||||||
function handleUpdateEvent(evt){
|
function handleUpdateEvent(evt){
|
||||||
let json = JSON.parse(evt.data);
|
let json = JSON.parse(evt.data);
|
||||||
if (json.task) {
|
if (json.task) {
|
||||||
@@ -119,7 +123,8 @@
|
|||||||
if (task.show_closed) show_closed = true;
|
if (task.show_closed) show_closed = true;
|
||||||
loadChildren();
|
loadChildren();
|
||||||
if (task.project_id) loadProject();
|
if (task.project_id) loadProject();
|
||||||
if (task.parent_task_id) loadParent();
|
if (task.parent_task_id) await loadParent();
|
||||||
|
loadSiblings();
|
||||||
} else error(resp);
|
} else error(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,13 +134,36 @@
|
|||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
project = await resp.json();
|
project = await resp.json();
|
||||||
yikes();
|
yikes();
|
||||||
} else error(await resp.text());
|
} else error(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSiblings(){
|
||||||
|
const url = api(`task/list`);
|
||||||
|
const select = task.parent_task_id ? {parent_task_id:task.parent_task_id} : {project_id: task.project_id};
|
||||||
|
select.show_closed = false;
|
||||||
|
const res = await post(url,select);
|
||||||
|
if (res.ok){
|
||||||
|
task.siblings = await res.json();
|
||||||
|
let last = null;
|
||||||
|
let before = undefined;
|
||||||
|
let after = null;
|
||||||
|
for (let [sid, t] of Object.entries(task.siblings).toSorted((a,b) => a[1].name.localeCompare(b[1].name))){
|
||||||
|
if (before !== undefined) {
|
||||||
|
after = +sid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (id == sid) before = last;
|
||||||
|
last = +sid;
|
||||||
|
}
|
||||||
|
task.before = before;
|
||||||
|
task.after = after;
|
||||||
|
} else error(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentClick(ev){
|
function parentClick(ev){
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (!task.parent_task_id) return;
|
if (!task.parent_task_id) return;
|
||||||
router.navigate(`/task/${task.parent_task_id}/view`);
|
gotoTask(task.parent_task_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,10 +260,17 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<button class="symbol" title={t('edit')} onclick={parentRightClick}></button>
|
<button class="symbol" title={t('edit')} onclick={parentRightClick}></button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div>{t('task')}</div>
|
<div>{t('task')}</div>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<LineEditor bind:value={task.name} editable={true} onSet={val => update({name:val})} />
|
<LineEditor bind:value={task.name} editable={true} onSet={val => update({name:val})} />
|
||||||
|
{#if task.before}
|
||||||
|
<button class="symbol" title={task.siblings[task.before].name} onclick={e => gotoTask(task.before)}></button>
|
||||||
|
{/if}
|
||||||
|
{#if task.after}
|
||||||
|
<button class="symbol" title={task.siblings[task.after].name} onclick={e => gotoTask(task.after)}></button>
|
||||||
|
{/if}
|
||||||
<button class="symbol" title={t('settings')} onclick={toggleSettings}></button>
|
<button class="symbol" title={t('settings')} onclick={toggleSettings}></button>
|
||||||
<button class="symbol" title={t('timetracking')} onclick={addTime}></button>
|
<button class="symbol" title={t('timetracking')} onclick={addTime}></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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.messagebus.events.Event.EventType.CREATE;
|
||||||
import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE;
|
import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
|
import static java.lang.System.Logger.Level.WARNING;
|
||||||
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
@@ -138,6 +139,7 @@ public class ProjectModule extends BaseHandler implements ProjectService {
|
|||||||
private void dropMember(Project project, long userId) {
|
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");
|
if (project.members().get(userId).permission() == OWNER) throw forbidden("You may not remove the owner of the project");
|
||||||
projectDb.dropMember(project.id(),userId);
|
projectDb.dropMember(project.id(),userId);
|
||||||
|
LOG.log(WARNING,"Removing member from project tasks not implemented"); // TODO
|
||||||
project.members().remove(userId);
|
project.members().remove(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +225,16 @@ public class ProjectModule extends BaseHandler implements ProjectService {
|
|||||||
UmbrellaUser newMember = null;
|
UmbrellaUser newMember = null;
|
||||||
if (json.has(DROP_MEMBER) && json.get(DROP_MEMBER) instanceof Number id) dropMember(project,id.longValue());
|
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(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);
|
project = projectDb.save(project.patch(json), user);
|
||||||
messageBus().dispatch(newMember != null ? new ProjectEvent(user,project,newMember) : new ProjectEvent(user,project, old));
|
messageBus().dispatch(newMember != null ? new ProjectEvent(user,project,newMember) : new ProjectEvent(user,project, old));
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ CREATE TABLE IF NOT EXISTS {0} (
|
|||||||
try {
|
try {
|
||||||
var query = select(ALL).from(TABLE_TASKS).leftJoin(ID,TABLE_TASKS_USERS,TASK_ID).where(USER_ID,equal(userId));
|
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()));
|
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<Task>();
|
var map = new ArrayList<Task>();
|
||||||
while (rs.next()) map.add(Task.of(rs));
|
while (rs.next()) map.add(Task.of(rs));
|
||||||
rs.close();
|
rs.close();
|
||||||
|
|||||||
@@ -66,6 +66,28 @@ public class TaskModule extends BaseHandler implements TaskService {
|
|||||||
return user;
|
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 {
|
private boolean deleteTask(HttpExchange ex, long taskId, UmbrellaUser user) throws IOException {
|
||||||
var task = loadMembers(taskDb.load(taskId));
|
var task = loadMembers(taskDb.load(taskId));
|
||||||
var member = task.members().get(user.id());
|
var member = task.members().get(user.id());
|
||||||
|
|||||||
@@ -335,6 +335,7 @@
|
|||||||
"select_customer": "Kunde auswählen",
|
"select_customer": "Kunde auswählen",
|
||||||
"select_property": "Eigenschaft auswählen",
|
"select_property": "Eigenschaft auswählen",
|
||||||
"select_state": "Status wählen",
|
"select_state": "Status wählen",
|
||||||
|
"select user": "Nutzer auswählen",
|
||||||
"send_document": "Dokument versenden",
|
"send_document": "Dokument versenden",
|
||||||
"sender": "Absender",
|
"sender": "Absender",
|
||||||
"sender_bank_account": "Bankverbindung",
|
"sender_bank_account": "Bankverbindung",
|
||||||
|
|||||||
@@ -335,6 +335,7 @@
|
|||||||
"select_customer": "select customer",
|
"select_customer": "select customer",
|
||||||
"select_property": "select property",
|
"select_property": "select property",
|
||||||
"select_state": "select state",
|
"select_state": "select state",
|
||||||
|
"select user": "select user",
|
||||||
"send_document": "send document",
|
"send_document": "send document",
|
||||||
"sender": "sender",
|
"sender": "sender",
|
||||||
"sender_bank_account": "bank account",
|
"sender_bank_account": "bank account",
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ input{
|
|||||||
color: orange;
|
color: orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:disabled{
|
||||||
|
border: 1px solid gray;
|
||||||
|
background: #444;
|
||||||
|
}
|
||||||
|
|
||||||
legend.date,
|
legend.date,
|
||||||
legend.time{
|
legend.time{
|
||||||
background-color: black;
|
background-color: black;
|
||||||
|
|||||||
Reference in New Issue
Block a user