implemented updating of option names. next: update option description

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2026-02-23 14:58:47 +01:00
parent 0eab5619d1
commit e5ab655787
6 changed files with 96 additions and 15 deletions
@@ -81,6 +81,7 @@ public class Text {
public static final String WIKI_PAGES = "wiki pages"; public static final String WIKI_PAGES = "wiki pages";
public static final String UNIT_PRICE = "unit price"; public static final String UNIT_PRICE = "unit price";
public static final String UNKNOWN_FIELD = "unknown field: {id}";
public static final String USER = "user"; public static final String USER = "user";
public static final String USERS = "users"; public static final String USERS = "users";
@@ -19,7 +19,8 @@ public record Poll(String id, Owner owner, String name, String description, bool
public static class Option implements Mappable{ public static class Option implements Mappable{
private int id; private int id;
Integer status; Integer status;
private final String description, name; private String description;
private String name;
private final Set<String> dirtyFields = new HashSet<>(); private final Set<String> dirtyFields = new HashSet<>();
public Option(int id, String name, String description, Integer status) { public Option(int id, String name, String description, Integer status) {
@@ -33,6 +34,12 @@ public record Poll(String id, Owner owner, String name, String description, bool
return description; return description;
} }
public Option description(String newVal){
description = newVal;
dirtyFields.add(DESCRIPTION);
return this;
}
public int id(){ public int id(){
return id; return id;
} }
@@ -55,6 +62,12 @@ public record Poll(String id, Owner owner, String name, String description, bool
return name; return name;
} }
public Option name(String newVal){
name = newVal;
dirtyFields.add(NAME);
return this;
}
public static Option of(ResultSet rs) throws SQLException { public static Option of(ResultSet rs) throws SQLException {
var id = rs.getInt(ID); var id = rs.getInt(ID);
var name = rs.getString(NAME); var name = rs.getString(NAME);
+1 -1
View File
@@ -1,4 +1,4 @@
<script> <script>
import { activeField } from './field_sync.svelte.js'; import { activeField } from './field_sync.svelte.js';
import { t } from '../translations.svelte.js'; import { t } from '../translations.svelte.js';
+14 -4
View File
@@ -5,7 +5,7 @@
import LineEditor from '../../Components/LineEditor.svelte'; import LineEditor from '../../Components/LineEditor.svelte';
import MarkdownEditor from '../../Components/MarkdownEditor.svelte'; import MarkdownEditor from '../../Components/MarkdownEditor.svelte';
import { api, get, post } from '../../urls.svelte'; import { api, get, patch, post } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte'; import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte'; import { t } from '../../translations.svelte';
import { user } from '../../user.svelte.js'; import { user } from '../../user.svelte.js';
@@ -23,18 +23,28 @@
} else error(res); } else error(res);
} }
async function patchOption(option, field, newVal){
let url = api(`poll/${id}/option/${option.id}`);
let data = {}
data[field] = newVal;
let res = await patch(url,data);
if (res.ok) {
yikes();
const json = await res.json();
} else error(res);
}
async function save_new_option(){ async function save_new_option(){
let url = api('poll/'+id+'/option'); let url = api('poll/'+id+'/option');
let res = await post(url,new_option); let res = await post(url,new_option);
if (res.ok){ if (res.ok){
yikes(); yikes();
const json = await res.json(); const json = await res.json();
console.log(json);
poll.options = json.options; poll.options = json.options;
} else error(res); } else error(res);
} }
onMount(load); onMount(load);
</script> </script>
@@ -63,7 +73,7 @@
{#each poll.options as option} {#each poll.options as option}
<tr> <tr>
<td> <td>
<input type="text" value={option.name} /> <LineEditor editable={true} value={option.name} onSet={name => patchOption(option,'name',name)} />
</td> </td>
<td> <td>
<MarkdownEditor bind:value={option.description} /> <MarkdownEditor bind:value={option.description} />
@@ -3,10 +3,11 @@ package de.srsoftware.umbrella.poll;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect; import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.ModuleRegistry.userService; import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
import static de.srsoftware.umbrella.core.constants.Field.FIELD;
import static de.srsoftware.umbrella.core.constants.Field.ID;
import static de.srsoftware.umbrella.core.constants.Path.LIST; import static de.srsoftware.umbrella.core.constants.Path.LIST;
import static de.srsoftware.umbrella.core.constants.Path.OPTION; import static de.srsoftware.umbrella.core.constants.Path.OPTION;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingField;
import static de.srsoftware.umbrella.poll.Constants.CONFIG_DATABASE; import static de.srsoftware.umbrella.poll.Constants.CONFIG_DATABASE;
import static java.lang.System.Logger.Level.WARNING; import static java.lang.System.Logger.Level.WARNING;
@@ -57,6 +58,23 @@ public class PollModule extends BaseHandler implements PollService {
} }
} }
@Override
public boolean doPatch(Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
Optional<Token> token = SessionToken.from(ex).map(Token::of);
var user = userService().loadUser(token);
if (user.isEmpty()) return unauthorized(ex);
var head = path.pop();
return switch (head) {
case null -> super.doPatch(path,ex);
default -> patchPoll(ex,user.get(),head, path);
};
} catch (UmbrellaException e){
return send(ex,e);
}
}
@Override @Override
public boolean doPost(Path path, HttpExchange ex) throws IOException { public boolean doPost(Path path, HttpExchange ex) throws IOException {
addCors(ex); addCors(ex);
@@ -66,7 +84,7 @@ public class PollModule extends BaseHandler implements PollService {
if (user.isEmpty()) return unauthorized(ex); if (user.isEmpty()) return unauthorized(ex);
var head = path.pop(); var head = path.pop();
return switch (head) { return switch (head) {
case null -> super.doGet(path,ex); case null -> super.doPost(path,ex);
default -> postToPoll(ex,user.get(),head, path); default -> postToPoll(ex,user.get(),head, path);
}; };
} catch (UmbrellaException e){ } catch (UmbrellaException e){
@@ -86,6 +104,46 @@ public class PollModule extends BaseHandler implements PollService {
return sendContent(ex,list); return sendContent(ex,list);
} }
private boolean patchPoll(HttpExchange ex, UmbrellaUser user, String id, Path path) throws IOException {
var poll = pollDb.loadPoll(id);
var permitted = user.equals(poll.owner());
if (!permitted) {
var permission = poll.shares().get(user);
if (permission == null || permission < 2) throw forbidden(Text.NOT_ALLOWED_TO_EDIT, Field.OBJECT,Text.POLL);
}
var head = path.pop();
return switch (head){
case OPTION -> patchPollOptions(ex, path, poll);
default -> notFound(ex);
};
}
private boolean patchPollOptions(HttpExchange ex, Path path, Poll poll) throws IOException {
try {
if (path.empty()) throw missingField(ID);
var optionId = Integer.parseInt(path.pop());
var option = poll.options().stream().filter(o -> o.id()==optionId).findFirst().orElse(null);
if (option == null) throw failedToLoadObject(Text.OPTION);
var json = json(ex);
for (var key : json.keySet()){
switch (key) {
case ID:
break;
case Field.NAME:
option.name(json.getString(key)); break;
case Field.DESCRIPTION:
option.description(json.getString(key)); break;
default:
throw UmbrellaException.badRequest(Text.UNKNOWN_FIELD,FIELD,key);
}
}
return sendContent(ex, pollDb.save(poll));
} catch (NumberFormatException nfe){
throw invalidField(ID,Text.NUMBER).causedBy(nfe);
}
}
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 {
var poll = pollDb.loadPoll(id); var poll = pollDb.loadPoll(id);
var permitted = user.equals(poll.owner()); var permitted = user.equals(poll.owner());
@@ -188,9 +188,8 @@ public class SqliteDb extends BaseDb implements PollDb {
} }
private Poll.Option update(String pollId, Poll.Option option) throws SQLException { private Poll.Option update(String pollId, Poll.Option option) throws SQLException {
updateIgnore(TABLE_OPTIONS) replaceInto(TABLE_OPTIONS,POLL_ID, ID, NAME, DESCRIPTION, STATUS)
.set(NAME, DESCRIPTION, STATUS).where(ID, equal(pollId)).prepare(db) .values(pollId, option.id(), option.name(), option.description(), option.status()).execute(db);
.apply(option.name(), option.description(), option.status()).execute();
return option; return option;
} }