implemented saving of new events. next: updating
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -103,7 +103,11 @@ public class BaseAppointment implements Appointment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Appointment clone(long newId) {
|
public Appointment clone(long newId) {
|
||||||
return new BaseAppointment(newId, title, description, start, end, location, slug).coords(coords).addLinks(links).add(attachments);
|
return new BaseAppointment(newId, title, description, start, end, location, slug) //
|
||||||
|
.coords(coords)
|
||||||
|
.addLinks(links)
|
||||||
|
.add(attachments)
|
||||||
|
.tags(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import static de.srsoftware.cal.db.Fields.ALL;
|
|||||||
import static de.srsoftware.tools.Optionals.*;
|
import static de.srsoftware.tools.Optionals.*;
|
||||||
import static de.srsoftware.tools.Result.transform;
|
import static de.srsoftware.tools.Result.transform;
|
||||||
import static de.srsoftware.tools.jdbc.Condition.*;
|
import static de.srsoftware.tools.jdbc.Condition.*;
|
||||||
import static de.srsoftware.tools.jdbc.Query.MARK;
|
import static de.srsoftware.tools.jdbc.Query.*;
|
||||||
import static java.lang.System.Logger.Level.*;
|
import static java.lang.System.Logger.Level.*;
|
||||||
|
|
||||||
import de.srsoftware.cal.BaseAppointment;
|
import de.srsoftware.cal.BaseAppointment;
|
||||||
@@ -44,7 +44,7 @@ public class MariaDB implements Database {
|
|||||||
|
|
||||||
private void applyUpdates() throws SQLException {
|
private void applyUpdates() throws SQLException {
|
||||||
LOG.log(INFO, "Checking for updates…");
|
LOG.log(INFO, "Checking for updates…");
|
||||||
var rs = Query.select("value").from("config").where("keyname", equal("dbversion")).exec(connection);
|
var rs = select("value").from("config").where("keyname", equal("dbversion")).exec(connection);
|
||||||
var version = 0;
|
var version = 0;
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
version = rs.getInt("value");
|
version = rs.getInt("value");
|
||||||
@@ -68,7 +68,7 @@ public class MariaDB implements Database {
|
|||||||
connection.prepareStatement(ADD_SLUG).execute();
|
connection.prepareStatement(ADD_SLUG).execute();
|
||||||
var slugMap = new HashMap<Long, String>();
|
var slugMap = new HashMap<Long, String>();
|
||||||
LOG.log(DEBUG, "Reading existing appointments…");
|
LOG.log(DEBUG, "Reading existing appointments…");
|
||||||
var rs = Query.select(ALL).from("appointments").exec(connection);
|
var rs = select(ALL).from("appointments").exec(connection);
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
var id = rs.getLong(AID);
|
var id = rs.getLong(AID);
|
||||||
var location = nullable(nullIfEmpty(rs.getString("location")));
|
var location = nullable(nullIfEmpty(rs.getString("location")));
|
||||||
@@ -84,13 +84,13 @@ public class MariaDB implements Database {
|
|||||||
}
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
LOG.log(DEBUG, "Creating slugs…");
|
LOG.log(DEBUG, "Creating slugs…");
|
||||||
var query = Query.updateIgnore("appointments").set("slug").where(AID, equal(MARK)).prepare(connection);
|
var query = updateIgnore("appointments").set("slug").where(AID, equal(MARK)).prepare(connection);
|
||||||
for (var entry : slugMap.entrySet()) {
|
for (var entry : slugMap.entrySet()) {
|
||||||
query.apply(entry.getValue(), entry.getKey());
|
query.apply(entry.getValue(), entry.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.log(DEBUG, "Writing new db version marker…");
|
LOG.log(DEBUG, "Writing new db version marker…");
|
||||||
Query.update("config").set("value").where("keyname", equal("dbversion")).prepare(connection).apply(2);
|
update("config").set("value").where("keyname", equal("dbversion")).prepare(connection).apply(2);
|
||||||
connection.setAutoCommit(true);
|
connection.setAutoCommit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +102,7 @@ public class MariaDB implements Database {
|
|||||||
@Override
|
@Override
|
||||||
public Result<Appointment> add(Appointment appointment) {
|
public Result<Appointment> add(Appointment appointment) {
|
||||||
try {
|
try {
|
||||||
ResultSet keys = Query //
|
ResultSet keys = insertInto(APPOINTMENTS, TITLE, DESCRIPTION, START, END, LOCATION, COORDS, SLUG) //
|
||||||
.insertInto(APPOINTMENTS, TITLE, DESCRIPTION, START, END, LOCATION, COORDS, SLUG)
|
|
||||||
.values(appointment.title(), appointment.description(), appointment.start(), appointment.end().orElse(null), appointment.location(), appointment.coords().orElse(null), appointment.slug())
|
.values(appointment.title(), appointment.description(), appointment.start(), appointment.end().orElse(null), appointment.location(), appointment.coords().orElse(null), appointment.slug())
|
||||||
.execute(connection)
|
.execute(connection)
|
||||||
.getGeneratedKeys();
|
.getGeneratedKeys();
|
||||||
@@ -111,36 +110,72 @@ public class MariaDB implements Database {
|
|||||||
if (keys.next()) saved = appointment.clone(keys.getLong(1));
|
if (keys.next()) saved = appointment.clone(keys.getLong(1));
|
||||||
keys.close();
|
keys.close();
|
||||||
if (saved == null) return Error.of("Insert query did not return appointment id!");
|
if (saved == null) return Error.of("Insert query did not return appointment id!");
|
||||||
var attachments = saved.attachments();
|
|
||||||
Query.InsertQuery assignQuery = null;
|
|
||||||
|
|
||||||
for (var attachment : attachments){
|
{ // link to attachments
|
||||||
if (assignQuery == null) assignQuery = Query.insertInto(APPOINTMENT_ATTACHMENTS,AID,UID,MIME);
|
var attachments = saved.attachments();
|
||||||
var urlId = getOrCreateUrl(attachment.url());
|
InsertQuery assignQuery = null;
|
||||||
if (urlId.isPresent()) assignQuery.values(saved.id(), urlId.get(),attachment.mime());
|
for (var attachment : attachments) {
|
||||||
|
var urlId = getOrCreateUrl(attachment.url());
|
||||||
|
if (assignQuery == null) assignQuery = insertInto(APPOINTMENT_ATTACHMENTS, AID, UID, MIME);
|
||||||
|
if (urlId.isPresent()) assignQuery.values(saved.id(), urlId.get(), attachment.mime());
|
||||||
|
}
|
||||||
|
if (assignQuery != null) assignQuery.execute(connection);
|
||||||
}
|
}
|
||||||
if (assignQuery != null) assignQuery.execute(connection);
|
|
||||||
|
|
||||||
|
{ // link to links
|
||||||
|
var links = saved.urls();
|
||||||
|
InsertQuery assignQuery = null;
|
||||||
|
for (var link : links) {
|
||||||
|
var urlId = getOrCreateUrl(link.url());
|
||||||
|
if (assignQuery == null) assignQuery = insertInto(APPOINTMENT_URLS, AID, UID, DESCRIPTION);
|
||||||
|
if (urlId.isPresent()) assignQuery.values(saved.id(), urlId.get(), link.desciption());
|
||||||
|
}
|
||||||
|
if (assignQuery != null) assignQuery.execute(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tags = saved.tags();
|
||||||
|
InsertQuery assignQuery = null;
|
||||||
|
for (var tag : tags) {
|
||||||
|
var tagId = getOrCreateTag(tag);
|
||||||
|
if (assignQuery == null) assignQuery = insertInto(APPOINTMENT_TAGS, AID, TID);
|
||||||
|
if (tagId.isPresent()) assignQuery.values(saved.id(), tagId.get());
|
||||||
|
}
|
||||||
|
if (assignQuery != null) assignQuery.execute(connection);
|
||||||
|
}
|
||||||
return Payload.of(saved);
|
return Payload.of(saved);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
LOG.log(ERROR,"Failed to store appointment",e);
|
LOG.log(ERROR, "Failed to store appointment", e);
|
||||||
return Error.of("Failed to store appointment", e);
|
return Error.of("Failed to store appointment", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Long> getOrCreateUrl(URL url) throws SQLException {
|
private Optional<Long> getOrCreateUrl(URL url) throws SQLException {
|
||||||
var rs = Query.select(UID).from(URLS).where(URL,equal(url.toString())).exec(connection);
|
var rs = select(UID).from(URLS).where(URL, equal(url.toString())).exec(connection);
|
||||||
Long uid = null;
|
Long uid = null;
|
||||||
if (rs.next()) uid = rs.getLong(1);
|
if (rs.next()) uid = rs.getLong(1);
|
||||||
rs.close();
|
rs.close();
|
||||||
if (uid == null){
|
if (uid == null) {
|
||||||
rs = Query.insertInto(URLS,URL).values(url.toString()).execute(connection).getGeneratedKeys();
|
rs = insertInto(URLS, URL).values(url.toString()).execute(connection).getGeneratedKeys();
|
||||||
if (rs.next()) uid = rs.getLong(1);
|
if (rs.next()) uid = rs.getLong(1);
|
||||||
rs.close();
|
rs.close();
|
||||||
}
|
}
|
||||||
return nullable(uid);
|
return nullable(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<Long> getOrCreateTag(String tag) throws SQLException {
|
||||||
|
var rs = select(TID).from(TAGS).where(KEYWORD, equal(tag)).exec(connection);
|
||||||
|
Long tid = null;
|
||||||
|
if (rs.next()) tid = rs.getLong(1);
|
||||||
|
rs.close();
|
||||||
|
if (tid == null) {
|
||||||
|
rs = insertInto(TAGS, KEYWORD).values(tag).execute(connection).getGeneratedKeys();
|
||||||
|
if (rs.next()) tid = rs.getLong(1);
|
||||||
|
rs.close();
|
||||||
|
}
|
||||||
|
return nullable(tid);
|
||||||
|
}
|
||||||
|
|
||||||
public static Database connect(String jdbc, String user, String pass) throws SQLException {
|
public static Database connect(String jdbc, String user, String pass) throws SQLException {
|
||||||
return new MariaDB(DriverManager.getConnection(jdbc, user, pass));
|
return new MariaDB(DriverManager.getConnection(jdbc, user, pass));
|
||||||
}
|
}
|
||||||
@@ -149,7 +184,7 @@ public class MariaDB implements Database {
|
|||||||
public Result<List<String>> findTags(String infix) {
|
public Result<List<String>> findTags(String infix) {
|
||||||
try {
|
try {
|
||||||
List<String> results = new ArrayList<>();
|
List<String> results = new ArrayList<>();
|
||||||
var rs = Query.select(KEYWORD).from(TAGS).where(KEYWORD, like("%%%s%%".formatted(infix))).sort(KEYWORD).exec(connection);
|
var rs = select(KEYWORD).from(TAGS).where(KEYWORD, like("%%%s%%".formatted(infix))).sort(KEYWORD).exec(connection);
|
||||||
while (rs.next()) results.add(rs.getString(KEYWORD));
|
while (rs.next()) results.add(rs.getString(KEYWORD));
|
||||||
rs.close();
|
rs.close();
|
||||||
return Payload.of(results);
|
return Payload.of(results);
|
||||||
@@ -161,7 +196,7 @@ public class MariaDB implements Database {
|
|||||||
@Override
|
@Override
|
||||||
public List<Appointment> list(Integer count, Integer offset) throws SQLException {
|
public List<Appointment> list(Integer count, Integer offset) throws SQLException {
|
||||||
var list = new ArrayList<Appointment>();
|
var list = new ArrayList<Appointment>();
|
||||||
var results = Query.select(ALL).from(APPOINTMENTS).sort("start").exec(connection);
|
var results = select(ALL).from(APPOINTMENTS).sort("start").exec(connection);
|
||||||
while (results.next()) createAppointmentOf(results).optional().ifPresent(list::add);
|
while (results.next()) createAppointmentOf(results).optional().ifPresent(list::add);
|
||||||
results.close();
|
results.close();
|
||||||
return list;
|
return list;
|
||||||
@@ -170,7 +205,7 @@ public class MariaDB implements Database {
|
|||||||
@Override
|
@Override
|
||||||
public Result<Appointment> loadEvent(long id) {
|
public Result<Appointment> loadEvent(long id) {
|
||||||
try {
|
try {
|
||||||
var rs = Query.select(ALL).from(APPOINTMENTS).where(AID, equal(id)).exec(connection);
|
var rs = select(ALL).from(APPOINTMENTS).where(AID, equal(id)).exec(connection);
|
||||||
Result<Appointment> result = rs.next() ? createAppointmentOf(rs).map(MariaDB::loadExtra) : Error.format("Failed to find appointment with id %s", id);
|
Result<Appointment> result = rs.next() ? createAppointmentOf(rs).map(MariaDB::loadExtra) : Error.format("Failed to find appointment with id %s", id);
|
||||||
rs.close();
|
rs.close();
|
||||||
return result;
|
return result;
|
||||||
@@ -182,7 +217,7 @@ public class MariaDB implements Database {
|
|||||||
@Override
|
@Override
|
||||||
public Result<Appointment> loadEvent(String slug) {
|
public Result<Appointment> loadEvent(String slug) {
|
||||||
try {
|
try {
|
||||||
var rs = Query.select(ALL).from(APPOINTMENTS).where(SLUG, equal(slug)).exec(connection);
|
var rs = select(ALL).from(APPOINTMENTS).where(SLUG, equal(slug)).exec(connection);
|
||||||
Result<Appointment> result = rs.next() ? createAppointmentOf(rs).map(MariaDB::loadExtra) : Error.format("Failed to find appointment with slug %s", slug);
|
Result<Appointment> result = rs.next() ? createAppointmentOf(rs).map(MariaDB::loadExtra) : Error.format("Failed to find appointment with slug %s", slug);
|
||||||
rs.close();
|
rs.close();
|
||||||
return result;
|
return result;
|
||||||
@@ -200,7 +235,7 @@ public class MariaDB implements Database {
|
|||||||
BaseAppointment event = res.optional().get();
|
BaseAppointment event = res.optional().get();
|
||||||
var id = event.id();
|
var id = event.id();
|
||||||
try {
|
try {
|
||||||
var rs = Query.select(KEYWORD).from(APPOINTMENT_TAGS).leftJoin(TID, "tags", TID).where(AID, equal(id)).exec(connection);
|
var rs = select(KEYWORD).from(APPOINTMENT_TAGS).leftJoin(TID, "tags", TID).where(AID, equal(id)).exec(connection);
|
||||||
while (rs.next()) event.tags(rs.getString(1));
|
while (rs.next()) event.tags(rs.getString(1));
|
||||||
rs.close();
|
rs.close();
|
||||||
return Payload.of(event);
|
return Payload.of(event);
|
||||||
@@ -214,7 +249,7 @@ public class MariaDB implements Database {
|
|||||||
BaseAppointment event = res.optional().get();
|
BaseAppointment event = res.optional().get();
|
||||||
var id = event.id();
|
var id = event.id();
|
||||||
try {
|
try {
|
||||||
var rs = Query.select(URL, DESCRIPTION).from(APPOINTMENT_URLS).leftJoin(UID, URLS, UID).where(AID, equal(id)).exec(connection);
|
var rs = select(URL, DESCRIPTION).from(APPOINTMENT_URLS).leftJoin(UID, URLS, UID).where(AID, equal(id)).exec(connection);
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
var u = rs.getString(URL);
|
var u = rs.getString(URL);
|
||||||
try {
|
try {
|
||||||
@@ -237,7 +272,7 @@ public class MariaDB implements Database {
|
|||||||
BaseAppointment event = res.optional().get();
|
BaseAppointment event = res.optional().get();
|
||||||
var id = event.id();
|
var id = event.id();
|
||||||
try {
|
try {
|
||||||
var rs = Query.select(URL, MIME).from(APPOINTMENT_ATTACHMENTS).leftJoin(UID, URLS, UID).where(AID, equal(id)).exec(connection);
|
var rs = select(URL, MIME).from(APPOINTMENT_ATTACHMENTS).leftJoin(UID, URLS, UID).where(AID, equal(id)).exec(connection);
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
var u = rs.getString(URL);
|
var u = rs.getString(URL);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public class ApiHandler extends PathHandler {
|
|||||||
private static final Logger LOG = getLogger(ApiHandler.class.getSimpleName());
|
private static final Logger LOG = getLogger(ApiHandler.class.getSimpleName());
|
||||||
private static final String ATTACHMENTS = "attachments";
|
private static final String ATTACHMENTS = "attachments";
|
||||||
private static final String LINKS = "links";
|
private static final String LINKS = "links";
|
||||||
|
private static final String TAGS = "tags";
|
||||||
private final Database db;
|
private final Database db;
|
||||||
|
|
||||||
public ApiHandler(Database db) {
|
public ApiHandler(Database db) {
|
||||||
@@ -102,6 +103,7 @@ public class ApiHandler extends PathHandler {
|
|||||||
if (o instanceof JSONObject j) toLink(j).optional().ifPresent(event::addLinks);
|
if (o instanceof JSONObject j) toLink(j).optional().ifPresent(event::addLinks);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (json.has(TAGS)) json.getJSONArray(TAGS).forEach(o -> event.tags(o.toString()));
|
||||||
var res = db.add(event).map(ApiHandler::toJson);
|
var res = db.add(event).map(ApiHandler::toJson);
|
||||||
return sendContent(ex, res);
|
return sendContent(ex, res);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function(event){
|
document.addEventListener("DOMContentLoaded", function(event){
|
||||||
console.log("page loaded…");
|
|
||||||
loadCurrentEvents();
|
loadCurrentEvents();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -135,4 +135,20 @@ table#eventlist{
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 15px;
|
top: 15px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
display: inline;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.error span {
|
||||||
|
color: red;
|
||||||
|
background: wheat;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.highlight{
|
||||||
|
background: wheat;
|
||||||
}
|
}
|
||||||
@@ -139,6 +139,25 @@ async function slug(start,location){
|
|||||||
return btoa(String.fromCharCode(...new Uint8Array(hash)));
|
return btoa(String.fromCharCode(...new Uint8Array(hash)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showError(message){
|
||||||
|
var span = document.createElement('div');
|
||||||
|
span.setAttribute('class','error');
|
||||||
|
span.innerHTML = "<span>Error: "+message+"<span>";
|
||||||
|
element('taglist').appendChild(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSave(response){
|
||||||
|
if (response.ok){
|
||||||
|
var json = await response.json();
|
||||||
|
var url = window.location.href.replace('/static/edit','')
|
||||||
|
if (json.id) url+='?highlight='+json.id;
|
||||||
|
window.location.href = url; // TODO: add highlight on new event
|
||||||
|
} else {
|
||||||
|
var json = await response.json();
|
||||||
|
if (json.error) showError(json.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function saveEvent(){
|
async function saveEvent(){
|
||||||
element('save').toggleAttribute("disabled");
|
element('save').toggleAttribute("disabled");
|
||||||
|
|
||||||
@@ -163,5 +182,5 @@ async function saveEvent(){
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type' : 'appication/json'
|
'Content-Type' : 'appication/json'
|
||||||
}
|
}
|
||||||
});
|
}).then(handleSave);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
var start = null;
|
var start = null;
|
||||||
var end = null;
|
var end = null;
|
||||||
var tags = new Set();
|
var tags = new Set();
|
||||||
|
var highlight = null;
|
||||||
|
|
||||||
function addCell(row,content,id){
|
function addCell(row,content,id){
|
||||||
var a = document.createElement('a');
|
var a = document.createElement('a');
|
||||||
@@ -14,6 +15,7 @@ function addCell(row,content,id){
|
|||||||
function addRow(json){
|
function addRow(json){
|
||||||
var table = document.getElementById('eventlist');
|
var table = document.getElementById('eventlist');
|
||||||
var row = table.insertRow(1);
|
var row = table.insertRow(1);
|
||||||
|
row.id = json.id;
|
||||||
if (json.tags){
|
if (json.tags){
|
||||||
var tagList = json.tags.join(' ');
|
var tagList = json.tags.join(' ');
|
||||||
row.setAttribute('class',tagList);
|
row.setAttribute('class',tagList);
|
||||||
@@ -76,10 +78,17 @@ async function handleEvents(response){
|
|||||||
var json = await response.json();
|
var json = await response.json();
|
||||||
json.forEach(addRow);
|
json.forEach(addRow);
|
||||||
updateTagVisibility();
|
updateTagVisibility();
|
||||||
|
if (highlight){
|
||||||
|
var row = element(highlight);
|
||||||
|
row.classList.add('highlight');
|
||||||
|
row.scrollIntoView({behavior: 'smooth',block: 'center'});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadCurrentEvents(){
|
function loadCurrentEvents(){
|
||||||
|
let params = new URLSearchParams(location.search);
|
||||||
|
highlight = params.get('highlight');
|
||||||
if (start == null){
|
if (start == null){
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
var year = now.getFullYear();
|
var year = now.getFullYear();
|
||||||
|
|||||||
Reference in New Issue
Block a user