implemented storing selectedweights for logged-in user
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -51,6 +51,7 @@ public class Field {
|
|||||||
public static final String DUE_DATE = "due_date";
|
public static final String DUE_DATE = "due_date";
|
||||||
public static final String DURATION = "duration";
|
public static final String DURATION = "duration";
|
||||||
|
|
||||||
|
public static final String EDITOR = "editor";
|
||||||
public static final String EMAIL = "email";
|
public static final String EMAIL = "email";
|
||||||
public static final String END_TIME = "end_time";
|
public static final String END_TIME = "end_time";
|
||||||
public static final String ENTITY_ID = "entity_id";
|
public static final String ENTITY_ID = "entity_id";
|
||||||
@@ -131,6 +132,7 @@ public class Field {
|
|||||||
public static final String RENDERED = "rendered";
|
public static final String RENDERED = "rendered";
|
||||||
public static final String REQUIRED_TASKS_IDS = "required_tasks_ids";
|
public static final String REQUIRED_TASKS_IDS = "required_tasks_ids";
|
||||||
|
|
||||||
|
public static final String SELECTION = "selection";
|
||||||
public static final String SENDER = "sender";
|
public static final String SENDER = "sender";
|
||||||
public static final String SENDER_USER_ID = "sender_user_id";
|
public static final String SENDER_USER_ID = "sender_user_id";
|
||||||
public static final String SETTINGS = "settings";
|
public static final String SETTINGS = "settings";
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public class Path {
|
|||||||
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 SETTINGS = "settings";
|
public static final String SETTINGS = "settings";
|
||||||
public static final String STATES = "states";
|
public static final String STATES = "states";
|
||||||
public static final String STARTED = "started";
|
public static final String STARTED = "started";
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public class Text {
|
|||||||
public static final String RECEIVER = "receiver";
|
public static final String RECEIVER = "receiver";
|
||||||
public static final String RECEIVERS = "receivers";
|
public static final String RECEIVERS = "receivers";
|
||||||
|
|
||||||
|
public static final String SELECTIONS = "selections";
|
||||||
public static final String SENDER = "sender";
|
public static final String SENDER = "sender";
|
||||||
public static final String SERVICE_WITH_ID = "service ({id})";
|
public static final String SERVICE_WITH_ID = "service ({id})";
|
||||||
public static final String SESSION = "session";
|
public static final String SESSION = "session";
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
import Times from "./routes/time/Index.svelte";
|
import Times from "./routes/time/Index.svelte";
|
||||||
import User from "./routes/user/User.svelte";
|
import User from "./routes/user/User.svelte";
|
||||||
import ViewDoc from "./routes/document/View.svelte";
|
import ViewDoc from "./routes/document/View.svelte";
|
||||||
|
import ViewPoll from "./routes/poll/View.svelte";
|
||||||
import ViewPrj from "./routes/project/View.svelte";
|
import ViewPrj from "./routes/project/View.svelte";
|
||||||
import ViewTask from "./routes/task/View.svelte";
|
import ViewTask from "./routes/task/View.svelte";
|
||||||
import WikiIndex from "./routes/wiki/Index.svelte";
|
import WikiIndex from "./routes/wiki/Index.svelte";
|
||||||
@@ -101,6 +102,7 @@
|
|||||||
<Route path="/poll" component={PollList} />
|
<Route path="/poll" component={PollList} />
|
||||||
<Route path="/poll/:id/edit" component={EditPoll} />
|
<Route path="/poll/:id/edit" component={EditPoll} />
|
||||||
<Route path="/poll/:id/evaluate" component={EvalPoll} />
|
<Route path="/poll/:id/evaluate" component={EvalPoll} />
|
||||||
|
<Route path="/poll/:id/view" component={ViewPoll} />
|
||||||
<Route path="/project" component={ProjectList} />
|
<Route path="/project" component={ProjectList} />
|
||||||
<Route path="/project/add" component={ProjectAdd} />
|
<Route path="/project/add" component={ProjectAdd} />
|
||||||
<Route path="/project/:project_id/add_task" component={AddTask} />
|
<Route path="/project/:project_id/add_task" component={AddTask} />
|
||||||
|
|||||||
@@ -40,7 +40,12 @@
|
|||||||
} else error(res);
|
} else error(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function open(poll){
|
||||||
|
router.navigate(`/poll/${poll.id}/view`);
|
||||||
|
}
|
||||||
|
|
||||||
function share(poll){
|
function share(poll){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(load);
|
onMount(load);
|
||||||
@@ -61,9 +66,9 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{#each polls as poll}
|
{#each polls as poll}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{poll.name}</td>
|
<td onclick={e => open(poll)}>{poll.name}</td>
|
||||||
<td>{@html poll.description.rendered}</td>
|
<td>{@html poll.description.rendered}</td>
|
||||||
<td>{poll.owner.name}</td>
|
<td onclick={e => open(poll)}>{poll.owner.name}</td>
|
||||||
<td>
|
<td>
|
||||||
{#if user.id == poll.owner.id || poll.shares[user.id].permission == 2}
|
{#if user.id == poll.owner.id || poll.shares[user.id].permission == 2}
|
||||||
<button onclick={e => edit(poll)}>{t('edit')}</button>
|
<button onclick={e => edit(poll)}>{t('edit')}</button>
|
||||||
|
|||||||
103
frontend/src/routes/poll/View.svelte
Normal file
103
frontend/src/routes/poll/View.svelte
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { api, get, post } from '../../urls.svelte';
|
||||||
|
import { error, yikes } from '../../warn.svelte';
|
||||||
|
import { user } from '../../user.svelte';
|
||||||
|
|
||||||
|
let { id } = $props();
|
||||||
|
import { t } from '../../translations.svelte';
|
||||||
|
let poll = $state(null);
|
||||||
|
let selection = $state({});
|
||||||
|
let editor = user ? { name: user.name, user_id : user.id } : { name : '', user_id : -1 };
|
||||||
|
|
||||||
|
async function load(){
|
||||||
|
let url = api('poll/'+id);
|
||||||
|
let res = await get(url);
|
||||||
|
if (res.ok){
|
||||||
|
poll = await res.json();
|
||||||
|
yikes();
|
||||||
|
} else error(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save(ev){
|
||||||
|
console.log({ev,editor,selection});
|
||||||
|
let url = api(`poll/${id}/select`);
|
||||||
|
let res = await post(url,{editor,selection});
|
||||||
|
if (res.ok) {
|
||||||
|
yikes();
|
||||||
|
} else error(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(option,weight){
|
||||||
|
selection[option.id] = +weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(load);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
table td:nth-child(n+2) {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.radio {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{t('User')}</legend>
|
||||||
|
{#if user}
|
||||||
|
<div>{t('logged in as: {user}',{user:user.name})}</div>
|
||||||
|
{:else}
|
||||||
|
<label>
|
||||||
|
<input type="text" bind:value={editor.name} />
|
||||||
|
{t('Your name')}
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
|
</fieldset>
|
||||||
|
{#if poll}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{t('poll')}: {poll.name}</legend>
|
||||||
|
<div class="description">
|
||||||
|
{@html poll.description.rendered}
|
||||||
|
</div>
|
||||||
|
<table class="poll">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>{t('option')}</td>
|
||||||
|
{#each Object.entries(poll.weights) as [weight,name]}
|
||||||
|
<td class="weight">
|
||||||
|
{weight}
|
||||||
|
<span class="description">
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each poll.options as option}
|
||||||
|
<tr>
|
||||||
|
<td class="option">
|
||||||
|
{option.name}
|
||||||
|
<span class="description">
|
||||||
|
{@html option.description.rendered}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
{#each Object.entries(poll.weights) as [weight,name]}
|
||||||
|
<td class="radio" onclick={e => select(option,weight)} title={t('click to select')} >
|
||||||
|
{#if selection[option.id] == weight}
|
||||||
|
X
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{#if Object.keys(selection).length}
|
||||||
|
<button onclick={save}>{t('save')}</button>
|
||||||
|
{/if}
|
||||||
|
</fieldset>
|
||||||
|
<div class="warn">TODO: add notes</div>
|
||||||
|
{/if}
|
||||||
@@ -3,8 +3,10 @@ package de.srsoftware.umbrella.poll;
|
|||||||
|
|
||||||
import de.srsoftware.umbrella.core.model.Poll;
|
import de.srsoftware.umbrella.core.model.Poll;
|
||||||
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface PollDb {
|
public interface PollDb {
|
||||||
Collection<Poll> listPolls(UmbrellaUser user);
|
Collection<Poll> listPolls(UmbrellaUser user);
|
||||||
@@ -12,4 +14,7 @@ public interface PollDb {
|
|||||||
Poll loadPoll(String id);
|
Poll loadPoll(String id);
|
||||||
|
|
||||||
Poll save(Poll poll);
|
Poll save(Poll poll);
|
||||||
|
|
||||||
|
void saveSelection(Poll poll, Map<Integer,Integer> optionsToWeights, String guestName);
|
||||||
|
void saveSelection(Poll poll, Map<Integer,Integer> optionsToWeights, UmbrellaUser user);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import static de.srsoftware.umbrella.core.constants.Field.ID;
|
|||||||
import static de.srsoftware.umbrella.core.constants.Path.*;
|
import static de.srsoftware.umbrella.core.constants.Path.*;
|
||||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
|
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
|
||||||
import static de.srsoftware.umbrella.poll.Constants.CONFIG_DATABASE;
|
import static de.srsoftware.umbrella.poll.Constants.CONFIG_DATABASE;
|
||||||
|
import static java.text.MessageFormat.format;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import de.srsoftware.configuration.Configuration;
|
import de.srsoftware.configuration.Configuration;
|
||||||
@@ -25,6 +26,7 @@ import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -81,12 +83,11 @@ public class PollModule extends BaseHandler implements PollService {
|
|||||||
addCors(ex);
|
addCors(ex);
|
||||||
try {
|
try {
|
||||||
Optional<Token> token = SessionToken.from(ex).map(Token::of);
|
Optional<Token> token = SessionToken.from(ex).map(Token::of);
|
||||||
var user = userService().loadUser(token);
|
var user = userService().loadUser(token).orElse(null );
|
||||||
if (user.isEmpty()) return unauthorized(ex);
|
|
||||||
var head = path.pop();
|
var head = path.pop();
|
||||||
return switch (head) {
|
return switch (head) {
|
||||||
case null -> postNewPoll(ex,user.get());
|
case null -> postNewPoll(ex,user);
|
||||||
default -> postToPoll(ex,user.get(),head, path);
|
default -> postToPoll(ex, user, head, path);
|
||||||
};
|
};
|
||||||
} catch (UmbrellaException e){
|
} catch (UmbrellaException e){
|
||||||
return send(ex,e);
|
return send(ex,e);
|
||||||
@@ -185,6 +186,7 @@ public class PollModule extends BaseHandler implements PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean postNewPoll(HttpExchange ex, UmbrellaUser user) throws IOException {
|
private boolean postNewPoll(HttpExchange ex, UmbrellaUser user) throws IOException {
|
||||||
|
if (user == null) return unauthorized(ex);
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
if (!json.has(Field.NAME)) throw missingField(Field.NAME);
|
if (!json.has(Field.NAME)) throw missingField(Field.NAME);
|
||||||
var name = json.getString(Field.NAME);
|
var name = json.getString(Field.NAME);
|
||||||
@@ -193,6 +195,7 @@ public class PollModule extends BaseHandler implements PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean postToPoll(HttpExchange ex, UmbrellaUser user, String id, Path path) throws IOException {
|
private boolean postToPoll(HttpExchange ex, UmbrellaUser user, String id, Path path) throws IOException {
|
||||||
|
if (user == null) return unauthorized(ex);
|
||||||
var poll = pollDb.loadPoll(id);
|
var poll = pollDb.loadPoll(id);
|
||||||
var permitted = user.equals(poll.owner());
|
var permitted = user.equals(poll.owner());
|
||||||
if (!permitted) {
|
if (!permitted) {
|
||||||
@@ -202,10 +205,33 @@ public class PollModule extends BaseHandler implements PollService {
|
|||||||
var head = path.pop();
|
var head = path.pop();
|
||||||
return switch (head){
|
return switch (head){
|
||||||
case OPTION -> postOption(ex, poll);
|
case OPTION -> postOption(ex, poll);
|
||||||
|
case SELECT -> postSelection(ex, poll, user);
|
||||||
case null, default -> notFound(ex);
|
case null, default -> notFound(ex);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean postSelection(HttpExchange ex, Poll poll, UmbrellaUser user) throws IOException {
|
||||||
|
var json = json(ex);
|
||||||
|
if (!json.has(Field.SELECTION)) throw missingField(Field.SELECTION);
|
||||||
|
if (!(json.get(Field.SELECTION) instanceof JSONObject job)) throw invalidField(Field.SELECTION,JSON);
|
||||||
|
var map = new HashMap<Integer,Integer>();
|
||||||
|
for (var key : job.keySet()){
|
||||||
|
var optionId = Integer.parseInt(key);
|
||||||
|
if (!(job.get(key) instanceof Integer weight)) throw invalidField(Field.WEIGHT,Text.NUMBER);
|
||||||
|
map.put(optionId,weight);
|
||||||
|
}
|
||||||
|
if (user != null) {
|
||||||
|
pollDb.saveSelection(poll, map, user);
|
||||||
|
} else {
|
||||||
|
if (!json.has(Field.EDITOR)) throw missingField(Field.EDITOR);
|
||||||
|
if (!(json.get(Field.EDITOR) instanceof JSONObject editor)) throw invalidField(Field.EDITOR,JSON);
|
||||||
|
if (!editor.has(Field.NAME)) throw missingField(format("{0}.{1}}",Field.EDITOR,Field.NAME));
|
||||||
|
if (!(editor.get(Field.NAME) instanceof String name)) throw invalidField(format("{0}.{1}",Field.EDITOR,Field.NAME),Text.STRING);
|
||||||
|
pollDb.saveSelection(poll, map, name);
|
||||||
|
}
|
||||||
|
return notFound(ex);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean postOption(HttpExchange ex, Poll poll) throws IOException {
|
private boolean postOption(HttpExchange ex, Poll poll) throws IOException {
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
if (!json.has(Field.NAME) || !(json.get(Field.NAME) instanceof String name)) throw missingField(Field.NAME);
|
if (!json.has(Field.NAME) || !(json.get(Field.NAME) instanceof String name)) throw missingField(Field.NAME);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import static java.text.MessageFormat.format;
|
|||||||
import de.srsoftware.umbrella.core.BaseDb;
|
import de.srsoftware.umbrella.core.BaseDb;
|
||||||
import de.srsoftware.umbrella.core.constants.Field;
|
import de.srsoftware.umbrella.core.constants.Field;
|
||||||
import de.srsoftware.umbrella.core.constants.Text;
|
import de.srsoftware.umbrella.core.constants.Text;
|
||||||
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import de.srsoftware.umbrella.core.model.Poll;
|
import de.srsoftware.umbrella.core.model.Poll;
|
||||||
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
||||||
|
|
||||||
@@ -206,6 +207,30 @@ public class SqliteDb extends BaseDb implements PollDb {
|
|||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveSelection(Poll poll, Map<Integer, Integer> optionsToWeights, String guestName) {
|
||||||
|
saveSelection(poll.id(), optionsToWeights, guestName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveSelection(Poll poll, Map<Integer, Integer> optionsToWeights, UmbrellaUser user) {
|
||||||
|
saveSelection(poll.id(), optionsToWeights, user.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveSelection(String pollId, Map<Integer, Integer> optionsToWeights, Object editor) {
|
||||||
|
var query = insertInto(TABLE_SELECTIONS,POLL_ID,OPTION_ID,USER_ID,WEIGHT);
|
||||||
|
for (var entry : optionsToWeights.entrySet()){
|
||||||
|
var optionId = entry.getKey();
|
||||||
|
var weight = entry.getValue();
|
||||||
|
query.values(pollId,optionId,editor,weight);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
query.execute(db).close();
|
||||||
|
} catch (SQLException sqle){
|
||||||
|
throw UmbrellaException.failedToStoreObject(Text.SELECTIONS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Poll update(Poll poll) {
|
private Poll update(Poll poll) {
|
||||||
if (poll.isDirty(NAME, DESCRIPTION)) try {
|
if (poll.isDirty(NAME, DESCRIPTION)) try {
|
||||||
replaceInto(TABLE_POLLS,ID,Field.USER_ID,NAME,DESCRIPTION).values(poll.id(),poll.owner().id(),poll.name(),poll.description()).execute(db);
|
replaceInto(TABLE_POLLS,ID,Field.USER_ID,NAME,DESCRIPTION).values(poll.id(),poll.owner().id(),poll.name(),poll.description()).execute(db);
|
||||||
|
|||||||
@@ -196,6 +196,7 @@
|
|||||||
"locality": "Ort",
|
"locality": "Ort",
|
||||||
"location": "Ort",
|
"location": "Ort",
|
||||||
"locations": "Orte",
|
"locations": "Orte",
|
||||||
|
"logged in as: {user}": "Anegemeldet als: {user}",
|
||||||
"login" : "Anmeldung",
|
"login" : "Anmeldung",
|
||||||
"login service": "Login-Service",
|
"login service": "Login-Service",
|
||||||
"login_services": "Login-Services",
|
"login_services": "Login-Services",
|
||||||
|
|||||||
@@ -196,6 +196,7 @@
|
|||||||
"locality": "locality",
|
"locality": "locality",
|
||||||
"location": "location",
|
"location": "location",
|
||||||
"locations": "locations",
|
"locations": "locations",
|
||||||
|
"logged in as: {user}": "logged in as: {user}",
|
||||||
"login" : "login",
|
"login" : "login",
|
||||||
"login service": "login service",
|
"login service": "login service",
|
||||||
"login_services": "login service",
|
"login_services": "login service",
|
||||||
|
|||||||
Reference in New Issue
Block a user