1 /* Monolith calendar widget.
2 * - by Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: ml_calendar_notes.c,v 1.6 2003/02/22 15:34:26 rich Exp $
41 #include <ml_widget.h>
42 #include <ml_table_layout.h>
43 #include <ml_flow_layout.h>
44 #include <ml_vertical_layout.h>
45 #include <ml_form_layout.h>
46 #include <ml_button.h>
47 #include <ml_text_label.h>
48 #include <ml_dialog.h>
50 #include <ml_form_text.h>
51 #include <ml_form_textarea.h>
52 #include <ml_form_select.h>
53 #include <ml_form_submit.h>
55 #include "ml_calendar_lib.h"
56 #include "ml_calendar_notes.h"
58 static void repaint (void *, ml_session, const char *, io_handle);
60 struct ml_widget_operations calendar_notes_ops =
65 struct ml_calendar_notes
67 struct ml_widget_operations *ops;
68 pool pool; /* Pool for allocations. */
69 ml_session session; /* Current session. */
70 const char *conninfo; /* Database connection. */
71 ml_calendar calendar; /* Parent calendar. */
72 int yyyy, mm, dd; /* Current date. */
74 ml_table_layout top; /* Top-level layout. */
75 ml_button add; /* Add a note. */
76 ml_vertical_layout tbl; /* Table of notes. */
78 /* These widgets are used in the add note form. */
79 ml_form_select f_resid; /* Calendar selected. */
80 vector f_resids; /* List of calendars. */
81 ml_form_text f_subject; /* Title. */
82 ml_form_textarea f_body; /* Notes. */
84 /* The above fields are also used on the edit note form, plus these: */
85 int eventid; /* Event ID we are editing. */
86 ml_form_submit edit, delete; /* Which button was pressed. */
89 /* This is the parameter to the note_button_press function. */
90 struct note_button_data
96 static void update_notes (ml_calendar_notes w);
97 static void add_note_button (ml_session session, void *vw);
98 static void add_note (ml_session session, void *vw);
99 static void note_button_press (ml_session session, void *vdata);
100 static void edit_note (ml_session session, void *vw);
103 new_ml_calendar_notes (pool pool, ml_session session, const char *conninfo,
104 ml_calendar calendar)
106 ml_calendar_notes w = pmalloc (pool, sizeof *w);
108 w->ops = &calendar_notes_ops;
110 w->session = session;
111 w->conninfo = conninfo;
112 w->calendar = calendar;
114 /* The parent calendar must call ml_calendar_month_set_date to set these
115 * before using this widget.
117 w->yyyy = w->mm = w->dd = -1;
119 /* Create the top-level layout and add buttons to it. */
120 w->top = new_ml_table_layout (pool, 2, 1);
121 ml_widget_set_property (w->top, "class", "ml_calendar_notes");
122 w->add = new_ml_button (pool, "Add note");
123 ml_button_set_callback (w->add, add_note_button, session, w);
124 ml_button_set_popup (w->add, "ml_calendar_notes_add_event");
125 ml_button_set_popup_size (w->add, 640, 300);
126 ml_table_layout_pack (w->top, w->add, 1, 0);
127 ml_table_layout_set_align (w->top, 1, 0, "left");
129 /* Create the notes area, unpopulated to start with. */
130 w->tbl = new_ml_vertical_layout (pool);
131 ml_widget_set_property (w->tbl, "class", "ml_calendar_notes");
132 ml_table_layout_pack (w->top, w->tbl, 0, 0);
133 ml_table_layout_set_align (w->top, 0, 0, "left");
139 ml_calendar_notes_set_date (ml_calendar_notes w, int yyyy, int mm, int dd)
141 /* Update the date. */
149 /* Update the notes area from the database. */
151 update_notes (ml_calendar_notes w)
155 const vector resids = _ml_calendar_get_calendars (w->calendar);
157 const char *subject, *body;
159 struct note_button_data *data;
161 ml_vertical_layout_clear (w->tbl);
163 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
165 /* Read the notes from the database. */
166 sth = st_prepare_cached
168 "select id, resid, subject, body from ml_calendar_events "
169 " where resid in (@) "
170 " and length = '1 day' "
171 " and start_time = date (? || '/' || ? || '/' || ? || ' 00:00:00') "
173 DBI_VECTOR_INT, DBI_INT, DBI_INT, DBI_INT);
174 st_execute (sth, resids, w->yyyy, w->mm, w->dd);
176 st_bind (sth, 0, eventid, DBI_INT);
177 st_bind (sth, 1, resid, DBI_INT);
178 st_bind (sth, 2, subject, DBI_STRING);
179 st_bind (sth, 3, body, DBI_STRING);
181 while (st_fetch (sth))
183 data = pmalloc (w->pool, sizeof *data);
184 data->eventid = eventid;
187 b = new_ml_button (w->pool, subject);
188 ml_widget_set_property (b, "button.style", "link");
189 ml_widget_set_property (b, "color", _ml_calendar_colour (resid));
190 ml_widget_set_property (b, "title", body);
191 ml_button_set_callback (b, note_button_press, w->session, data);
192 ml_button_set_popup (b, "ml_calendar_notes_edit_event");
193 ml_button_set_popup_size (b, 640, 300);
195 ml_vertical_layout_push_back (w->tbl, b);
202 add_note_button (ml_session session, void *vw)
204 ml_calendar_notes w = (ml_calendar_notes) vw;
208 ml_form_submit submit;
214 win = new_ml_window (w->session, w->pool);
216 /* Create the form. */
217 form = new_ml_form (w->pool);
218 tbl = new_ml_form_layout (w->pool);
220 /* Set the callback on the form. */
221 ml_form_set_callback (form, add_note, session, w);
222 ml_widget_set_property (form, "method", "GET");
224 /* Pull out the list of calendar names from the database and populate
225 * the drop-down menu with it.
227 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
229 w->f_resid = new_ml_form_select (w->pool, form);
230 w->f_resids = new_vector (w->pool, int);
232 sth = st_prepare_cached
234 "select c.resid, r.name from ml_calendar c, ml_resources r "
235 "where c.resid in (@) and c.resid = r.resid order by 2",
237 st_execute (sth, _ml_calendar_get_calendars (w->calendar));
239 st_bind (sth, 0, resid, DBI_INT);
240 st_bind (sth, 1, resname, DBI_STRING);
242 while (st_fetch (sth))
244 vector_push_back (w->f_resids, resid);
245 ml_form_select_push_back (w->f_resid, resname);
248 ml_form_layout_pack (tbl, "Calendar: ", w->f_resid);
253 w->f_subject = new_ml_form_text (w->pool, form);
254 ml_widget_set_property (w->f_subject, "form.text.size", 50);
255 ml_form_layout_pack (tbl, "Title: ", w->f_subject);
258 w->f_body = new_ml_form_textarea (w->pool, form, 5, 50);
259 ml_form_layout_pack (tbl, "Notes: ", w->f_body);
262 /* XXX Cancel button. */
263 submit = new_ml_form_submit (w->pool, form, "Save");
264 ml_form_layout_pack (tbl, 0, submit);
266 /* Pack everything together. */
267 ml_form_pack (form, tbl);
268 ml_window_pack (win, form);
272 add_note (ml_session session, void *vw)
274 ml_calendar_notes w = (ml_calendar_notes) vw;
277 int userid, i, resid;
278 const char *subject, *body;
280 /* Only logged-in users can post. */
281 /* XXX Resource-level security - later. */
282 userid = ml_session_userid (session);
285 ml_error_window (w->pool, session,
286 "You must be logged in to add events to the calendar.",
287 ML_DIALOG_CLOSE_BUTTON);
291 /* Verify that the user has at least given a subject line. If not, just
292 * return, which redisplays the form.
294 subject = ml_form_input_get_value (w->f_subject);
295 if (!subject || strlen (subject) == 0)
298 /* Verify the remaining fields are in range. */
299 i = ml_form_select_get_selection (w->f_resid);
300 if (i < 0 || i >= vector_size (w->f_resids))
303 /* Pull the other fields from the form. */
304 body = ml_form_input_get_value (w->f_body);
305 vector_get (w->f_resids, i, resid);
307 /* Save it in the database. */
308 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
310 sth = st_prepare_cached
312 "insert into ml_calendar_events (resid, start_time, length, subject, "
313 "body, author, original_ip) "
314 "values (?, ?, '1 day', ?, ?, ?, ?)",
315 DBI_INT, DBI_STRING, DBI_STRING, DBI_STRING, DBI_INT, DBI_STRING);
318 psprintf (w->pool, "%04d/%02d/%02d 00:00:00",
319 w->yyyy, w->mm, w->dd),
322 ml_session_get_peernamestr (session));
324 /* Commit change to the database. */
328 /* Print confirmation page. */
329 ml_ok_window (w->pool, session,
330 "That note was added to the calendar.",
331 ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
336 note_button_press (ml_session session, void *vdata)
338 struct note_button_data *data = (struct note_button_data *) vdata;
339 ml_calendar_notes w = data->w;
343 ml_flow_layout buttons;
347 const char *resname, *subject, *body;
349 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
351 /* Pull out the current values of the fields from the database. */
352 sth = st_prepare_cached
354 "select resid, subject, body "
355 " from ml_calendar_events "
356 " where id = ? and resid in (@)", DBI_INT, DBI_VECTOR_INT);
357 st_execute (sth, data->eventid, _ml_calendar_get_calendars (w->calendar));
359 st_bind (sth, 0, resid, DBI_INT);
360 st_bind (sth, 1, subject, DBI_STRING);
361 st_bind (sth, 2, body, DBI_STRING);
365 ml_error_window (w->pool, session,
366 "I cannot find that calendar event. Perhaps it has "
367 "been deleted by another user, or it is in a calendar "
368 "which you can no longer see.",
369 ML_DIALOG_CLOSE_BUTTON);
373 /* Save the event ID in the widget structure. We need it in edit_event. */
374 w->eventid = data->eventid;
376 win = new_ml_window (w->session, w->pool);
378 /* Create the form. */
379 form = new_ml_form (w->pool);
380 tbl = new_ml_form_layout (w->pool);
382 /* Set the callback on the form. */
383 ml_form_set_callback (form, edit_note, session, w);
384 ml_widget_set_property (form, "method", "GET");
386 /* Pull out the list of calendar names from the database and populate
387 * the drop-down menu with it.
389 w->f_resid = new_ml_form_select (w->pool, form);
390 w->f_resids = new_vector (w->pool, int);
392 sth = st_prepare_cached
394 "select c.resid, r.name from ml_calendar c, ml_resources r "
395 "where c.resid in (@) and c.resid = r.resid order by 2",
397 st_execute (sth, _ml_calendar_get_calendars (w->calendar));
399 st_bind (sth, 0, t, DBI_INT);
400 st_bind (sth, 1, resname, DBI_STRING);
402 while (st_fetch (sth))
404 vector_push_back (w->f_resids, resid);
405 ml_form_select_push_back (w->f_resid, resname);
408 ml_form_select_set_selection (w->f_resid,
409 ml_form_select_size (w->f_resid) - 1);
411 ml_form_layout_pack (tbl, "Calendar: ", w->f_resid);
416 w->f_subject = new_ml_form_text (w->pool, form);
417 ml_widget_set_property (w->f_subject, "form.text.size", 50);
419 ml_form_input_set_value (w->f_subject, subject);
420 ml_form_layout_pack (tbl, "Title: ", w->f_subject);
423 w->f_body = new_ml_form_textarea (w->pool, form, 5, 50);
425 ml_form_input_set_value (w->f_body, body);
426 ml_form_layout_pack (tbl, "Notes: ", w->f_body);
428 /* Submit buttons. */
429 buttons = new_ml_flow_layout (w->pool);
430 w->edit = new_ml_form_submit (w->pool, form, "Edit");
431 ml_flow_layout_pack (buttons, w->edit);
432 w->delete = new_ml_form_submit (w->pool, form, "Delete");
433 ml_flow_layout_pack (buttons, w->delete);
435 ml_form_layout_pack (tbl, 0, buttons);
437 /* Pack everything together. */
438 ml_form_pack (form, tbl);
439 ml_window_pack (win, form);
443 edit_note (ml_session session, void *vw)
445 ml_calendar_notes w = (ml_calendar_notes) vw;
447 int edit_pressed, delete_pressed;
448 const char *s, *subject, *body;
453 /* Only logged-in users can post. */
454 /* XXX Resource-level security - later. */
455 userid = ml_session_userid (session);
458 ml_error_window (w->pool, session,
459 "You must be logged in to change "
460 "events in the calendar.",
461 ML_DIALOG_CLOSE_BUTTON);
465 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
467 /* Which submit button was pressed? */
468 s = ml_form_input_get_value (w->edit);
469 edit_pressed = s && strlen (s) > 0;
471 s = ml_form_input_get_value (w->delete);
472 delete_pressed = s && strlen (s) > 0;
474 assert ((edit_pressed || delete_pressed) &&
475 !(edit_pressed && delete_pressed));
477 /* Delete button pressed? */
480 sth = st_prepare_cached
482 "delete from ml_calendar_events "
483 "where id = ? and resid in (@)",
484 DBI_INT, DBI_VECTOR_INT);
485 st_execute (sth, w->eventid, _ml_calendar_get_calendars (w->calendar));
487 /* Commit change to the database. */
491 /* Print confirmation page. */
492 ml_ok_window (w->pool, session,
493 "That event was deleted.",
494 ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
499 /* Verify that the user has at least given a subject line. If not, just
500 * return, which redisplays the form.
502 subject = ml_form_input_get_value (w->f_subject);
503 if (!subject || strlen (subject) == 0)
506 /* Verify the remaining fields are in range. */
507 i = ml_form_select_get_selection (w->f_resid);
508 if (i < 0 || i >= vector_size (w->f_resids))
511 /* Pull the other fields from the form. */
512 body = ml_form_input_get_value (w->f_body);
513 vector_get (w->f_resids, i, resid);
515 /* Update the database. */
516 sth = st_prepare_cached
518 "update ml_calendar_events "
519 "set resid = ?, subject = ?, body = ?, "
520 "last_modified_date = current_timestamp "
521 "where id = ? and resid in (@)",
522 DBI_INT, DBI_STRING, DBI_STRING, DBI_INT, DBI_VECTOR_INT);
526 w->eventid, _ml_calendar_get_calendars (w->calendar));
528 /* Commit change to the database. */
532 /* Print confirmation page. */
533 ml_ok_window (w->pool, session,
534 "That event was edited.",
535 ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
540 repaint (void *vw, ml_session session, const char *windowid, io_handle io)
542 ml_calendar_notes w = (ml_calendar_notes) vw;
544 ml_widget_repaint (w->top, session, windowid, io);