Browse Source

implemented saving of new events. next: updating

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
main
Stephan Richter 9 months ago
parent
commit
2e1f1c9697
  1. 6
      de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/BaseAppointment.java
  2. 73
      de.srsoftware.cal.db/src/main/java/de/srsoftware/cal/db/MariaDB.java
  3. 2
      de.srsoftware.cal.web/src/main/java/de/srsoftware/cal/ApiHandler.java
  4. 1
      de.srsoftware.cal.web/src/main/resources/index.html
  5. 16
      de.srsoftware.cal.web/src/main/resources/occ.css
  6. 21
      de.srsoftware.cal.web/src/main/resources/script/edit.js
  7. 9
      de.srsoftware.cal.web/src/main/resources/script/index.js

6
de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/BaseAppointment.java

@ -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

73
de.srsoftware.cal.db/src/main/java/de/srsoftware/cal/db/MariaDB.java

@ -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,16 +110,39 @@ 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;
{ // link to attachments
var attachments = saved.attachments();
InsertQuery assignQuery = null;
for (var attachment : attachments) { for (var attachment : attachments) {
if (assignQuery == null) assignQuery = Query.insertInto(APPOINTMENT_ATTACHMENTS,AID,UID,MIME);
var urlId = getOrCreateUrl(attachment.url()); 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 (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);
@ -129,18 +151,31 @@ public class MariaDB implements Database {
} }
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 {

2
de.srsoftware.cal.web/src/main/java/de/srsoftware/cal/ApiHandler.java

@ -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);
} }

1
de.srsoftware.cal.web/src/main/resources/index.html

@ -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>

16
de.srsoftware.cal.web/src/main/resources/occ.css

@ -136,3 +136,19 @@ table#eventlist{
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;
}

21
de.srsoftware.cal.web/src/main/resources/script/edit.js

@ -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);
} }

9
de.srsoftware.cal.web/src/main/resources/script/index.js

@ -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();

Loading…
Cancel
Save