/* Monolith calendar widget. * - by Richard W.M. Jones * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: ml_calendar_notes.c,v 1.6 2003/02/22 15:34:26 rich Exp $ */ #include "config.h" #include #include #include #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ml_calendar_lib.h" #include "ml_calendar_notes.h" static void repaint (void *, ml_session, const char *, io_handle); struct ml_widget_operations calendar_notes_ops = { repaint: repaint }; struct ml_calendar_notes { struct ml_widget_operations *ops; pool pool; /* Pool for allocations. */ ml_session session; /* Current session. */ const char *conninfo; /* Database connection. */ ml_calendar calendar; /* Parent calendar. */ int yyyy, mm, dd; /* Current date. */ ml_table_layout top; /* Top-level layout. */ ml_button add; /* Add a note. */ ml_vertical_layout tbl; /* Table of notes. */ /* These widgets are used in the add note form. */ ml_form_select f_resid; /* Calendar selected. */ vector f_resids; /* List of calendars. */ ml_form_text f_subject; /* Title. */ ml_form_textarea f_body; /* Notes. */ /* The above fields are also used on the edit note form, plus these: */ int eventid; /* Event ID we are editing. */ ml_form_submit edit, delete; /* Which button was pressed. */ }; /* This is the parameter to the note_button_press function. */ struct note_button_data { int eventid; ml_calendar_notes w; }; static void update_notes (ml_calendar_notes w); static void add_note_button (ml_session session, void *vw); static void add_note (ml_session session, void *vw); static void note_button_press (ml_session session, void *vdata); static void edit_note (ml_session session, void *vw); ml_calendar_notes new_ml_calendar_notes (pool pool, ml_session session, const char *conninfo, ml_calendar calendar) { ml_calendar_notes w = pmalloc (pool, sizeof *w); w->ops = &calendar_notes_ops; w->pool = pool; w->session = session; w->conninfo = conninfo; w->calendar = calendar; /* The parent calendar must call ml_calendar_month_set_date to set these * before using this widget. */ w->yyyy = w->mm = w->dd = -1; /* Create the top-level layout and add buttons to it. */ w->top = new_ml_table_layout (pool, 2, 1); ml_widget_set_property (w->top, "class", "ml_calendar_notes"); w->add = new_ml_button (pool, "Add note"); ml_button_set_callback (w->add, add_note_button, session, w); ml_button_set_popup (w->add, "ml_calendar_notes_add_event"); ml_button_set_popup_size (w->add, 640, 300); ml_table_layout_pack (w->top, w->add, 1, 0); ml_table_layout_set_align (w->top, 1, 0, "left"); /* Create the notes area, unpopulated to start with. */ w->tbl = new_ml_vertical_layout (pool); ml_widget_set_property (w->tbl, "class", "ml_calendar_notes"); ml_table_layout_pack (w->top, w->tbl, 0, 0); ml_table_layout_set_align (w->top, 0, 0, "left"); return w; } void ml_calendar_notes_set_date (ml_calendar_notes w, int yyyy, int mm, int dd) { /* Update the date. */ w->yyyy = yyyy; w->mm = mm; w->dd = dd; update_notes (w); } /* Update the notes area from the database. */ static void update_notes (ml_calendar_notes w) { db_handle dbh; st_handle sth; const vector resids = _ml_calendar_get_calendars (w->calendar); int eventid, resid; const char *subject, *body; ml_button b; struct note_button_data *data; ml_vertical_layout_clear (w->tbl); dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS); /* Read the notes from the database. */ sth = st_prepare_cached (dbh, "select id, resid, subject, body from ml_calendar_events " " where resid in (@) " " and length = '1 day' " " and start_time = date (? || '/' || ? || '/' || ? || ' 00:00:00') " " order by 1", DBI_VECTOR_INT, DBI_INT, DBI_INT, DBI_INT); st_execute (sth, resids, w->yyyy, w->mm, w->dd); st_bind (sth, 0, eventid, DBI_INT); st_bind (sth, 1, resid, DBI_INT); st_bind (sth, 2, subject, DBI_STRING); st_bind (sth, 3, body, DBI_STRING); while (st_fetch (sth)) { data = pmalloc (w->pool, sizeof *data); data->eventid = eventid; data->w = w; b = new_ml_button (w->pool, subject); ml_widget_set_property (b, "button.style", "link"); ml_widget_set_property (b, "color", _ml_calendar_colour (resid)); ml_widget_set_property (b, "title", body); ml_button_set_callback (b, note_button_press, w->session, data); ml_button_set_popup (b, "ml_calendar_notes_edit_event"); ml_button_set_popup_size (b, 640, 300); ml_vertical_layout_push_back (w->tbl, b); } put_db_handle (dbh); } static void add_note_button (ml_session session, void *vw) { ml_calendar_notes w = (ml_calendar_notes) vw; ml_window win; ml_form form; ml_form_layout tbl; ml_form_submit submit; db_handle dbh; st_handle sth; int resid; const char *resname; win = new_ml_window (w->session, w->pool); /* Create the form. */ form = new_ml_form (w->pool); tbl = new_ml_form_layout (w->pool); /* Set the callback on the form. */ ml_form_set_callback (form, add_note, session, w); ml_widget_set_property (form, "method", "GET"); /* Pull out the list of calendar names from the database and populate * the drop-down menu with it. */ dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS); w->f_resid = new_ml_form_select (w->pool, form); w->f_resids = new_vector (w->pool, int); sth = st_prepare_cached (dbh, "select c.resid, r.name from ml_calendar c, ml_resources r " "where c.resid in (@) and c.resid = r.resid order by 2", DBI_VECTOR_INT); st_execute (sth, _ml_calendar_get_calendars (w->calendar)); st_bind (sth, 0, resid, DBI_INT); st_bind (sth, 1, resname, DBI_STRING); while (st_fetch (sth)) { vector_push_back (w->f_resids, resid); ml_form_select_push_back (w->f_resid, resname); /* XXX Colours. */ } ml_form_layout_pack (tbl, "Calendar: ", w->f_resid); put_db_handle (dbh); /* Subject/title. */ w->f_subject = new_ml_form_text (w->pool, form); ml_widget_set_property (w->f_subject, "form.text.size", 50); ml_form_layout_pack (tbl, "Title: ", w->f_subject); /* Body. */ w->f_body = new_ml_form_textarea (w->pool, form, 5, 50); ml_form_layout_pack (tbl, "Notes: ", w->f_body); /* Submit button. */ /* XXX Cancel button. */ submit = new_ml_form_submit (w->pool, form, "Save"); ml_form_layout_pack (tbl, 0, submit); /* Pack everything together. */ ml_form_pack (form, tbl); ml_window_pack (win, form); } static void add_note (ml_session session, void *vw) { ml_calendar_notes w = (ml_calendar_notes) vw; db_handle dbh; st_handle sth; int userid, i, resid; const char *subject, *body; /* Only logged-in users can post. */ /* XXX Resource-level security - later. */ userid = ml_session_userid (session); if (!userid) { ml_error_window (w->pool, session, "You must be logged in to add events to the calendar.", ML_DIALOG_CLOSE_BUTTON); return; } /* Verify that the user has at least given a subject line. If not, just * return, which redisplays the form. */ subject = ml_form_input_get_value (w->f_subject); if (!subject || strlen (subject) == 0) return; /* Verify the remaining fields are in range. */ i = ml_form_select_get_selection (w->f_resid); if (i < 0 || i >= vector_size (w->f_resids)) return; /* Pull the other fields from the form. */ body = ml_form_input_get_value (w->f_body); vector_get (w->f_resids, i, resid); /* Save it in the database. */ dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS); sth = st_prepare_cached (dbh, "insert into ml_calendar_events (resid, start_time, length, subject, " "body, author, original_ip) " "values (?, ?, '1 day', ?, ?, ?, ?)", DBI_INT, DBI_STRING, DBI_STRING, DBI_STRING, DBI_INT, DBI_STRING); st_execute (sth, resid, psprintf (w->pool, "%04d/%02d/%02d 00:00:00", w->yyyy, w->mm, w->dd), subject, body, userid, ml_session_get_peernamestr (session)); /* Commit change to the database. */ db_commit (dbh); put_db_handle (dbh); /* Print confirmation page. */ ml_ok_window (w->pool, session, "That note was added to the calendar.", ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER); update_notes (w); } static void note_button_press (ml_session session, void *vdata) { struct note_button_data *data = (struct note_button_data *) vdata; ml_calendar_notes w = data->w; ml_window win; ml_form form; ml_form_layout tbl; ml_flow_layout buttons; db_handle dbh; st_handle sth; int resid, t; const char *resname, *subject, *body; dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS); /* Pull out the current values of the fields from the database. */ sth = st_prepare_cached (dbh, "select resid, subject, body " " from ml_calendar_events " " where id = ? and resid in (@)", DBI_INT, DBI_VECTOR_INT); st_execute (sth, data->eventid, _ml_calendar_get_calendars (w->calendar)); st_bind (sth, 0, resid, DBI_INT); st_bind (sth, 1, subject, DBI_STRING); st_bind (sth, 2, body, DBI_STRING); if (!st_fetch (sth)) { ml_error_window (w->pool, session, "I cannot find that calendar event. Perhaps it has " "been deleted by another user, or it is in a calendar " "which you can no longer see.", ML_DIALOG_CLOSE_BUTTON); return; } /* Save the event ID in the widget structure. We need it in edit_event. */ w->eventid = data->eventid; win = new_ml_window (w->session, w->pool); /* Create the form. */ form = new_ml_form (w->pool); tbl = new_ml_form_layout (w->pool); /* Set the callback on the form. */ ml_form_set_callback (form, edit_note, session, w); ml_widget_set_property (form, "method", "GET"); /* Pull out the list of calendar names from the database and populate * the drop-down menu with it. */ w->f_resid = new_ml_form_select (w->pool, form); w->f_resids = new_vector (w->pool, int); sth = st_prepare_cached (dbh, "select c.resid, r.name from ml_calendar c, ml_resources r " "where c.resid in (@) and c.resid = r.resid order by 2", DBI_VECTOR_INT); st_execute (sth, _ml_calendar_get_calendars (w->calendar)); st_bind (sth, 0, t, DBI_INT); st_bind (sth, 1, resname, DBI_STRING); while (st_fetch (sth)) { vector_push_back (w->f_resids, resid); ml_form_select_push_back (w->f_resid, resname); /* XXX Colours. */ if (t == resid) ml_form_select_set_selection (w->f_resid, ml_form_select_size (w->f_resid) - 1); } ml_form_layout_pack (tbl, "Calendar: ", w->f_resid); put_db_handle (dbh); /* Subject/title. */ w->f_subject = new_ml_form_text (w->pool, form); ml_widget_set_property (w->f_subject, "form.text.size", 50); if (subject) ml_form_input_set_value (w->f_subject, subject); ml_form_layout_pack (tbl, "Title: ", w->f_subject); /* Body. */ w->f_body = new_ml_form_textarea (w->pool, form, 5, 50); if (body) ml_form_input_set_value (w->f_body, body); ml_form_layout_pack (tbl, "Notes: ", w->f_body); /* Submit buttons. */ buttons = new_ml_flow_layout (w->pool); w->edit = new_ml_form_submit (w->pool, form, "Edit"); ml_flow_layout_pack (buttons, w->edit); w->delete = new_ml_form_submit (w->pool, form, "Delete"); ml_flow_layout_pack (buttons, w->delete); ml_form_layout_pack (tbl, 0, buttons); /* Pack everything together. */ ml_form_pack (form, tbl); ml_window_pack (win, form); } static void edit_note (ml_session session, void *vw) { ml_calendar_notes w = (ml_calendar_notes) vw; int userid; int edit_pressed, delete_pressed; const char *s, *subject, *body; int i, resid; db_handle dbh; st_handle sth; /* Only logged-in users can post. */ /* XXX Resource-level security - later. */ userid = ml_session_userid (session); if (!userid) { ml_error_window (w->pool, session, "You must be logged in to change " "events in the calendar.", ML_DIALOG_CLOSE_BUTTON); return; } dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS); /* Which submit button was pressed? */ s = ml_form_input_get_value (w->edit); edit_pressed = s && strlen (s) > 0; s = ml_form_input_get_value (w->delete); delete_pressed = s && strlen (s) > 0; assert ((edit_pressed || delete_pressed) && !(edit_pressed && delete_pressed)); /* Delete button pressed? */ if (delete_pressed) { sth = st_prepare_cached (dbh, "delete from ml_calendar_events " "where id = ? and resid in (@)", DBI_INT, DBI_VECTOR_INT); st_execute (sth, w->eventid, _ml_calendar_get_calendars (w->calendar)); /* Commit change to the database. */ db_commit (dbh); put_db_handle (dbh); /* Print confirmation page. */ ml_ok_window (w->pool, session, "That event was deleted.", ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER); update_notes (w); return; } /* Verify that the user has at least given a subject line. If not, just * return, which redisplays the form. */ subject = ml_form_input_get_value (w->f_subject); if (!subject || strlen (subject) == 0) return; /* Verify the remaining fields are in range. */ i = ml_form_select_get_selection (w->f_resid); if (i < 0 || i >= vector_size (w->f_resids)) return; /* Pull the other fields from the form. */ body = ml_form_input_get_value (w->f_body); vector_get (w->f_resids, i, resid); /* Update the database. */ sth = st_prepare_cached (dbh, "update ml_calendar_events " "set resid = ?, subject = ?, body = ?, " "last_modified_date = current_timestamp " "where id = ? and resid in (@)", DBI_INT, DBI_STRING, DBI_STRING, DBI_INT, DBI_VECTOR_INT); st_execute (sth, resid, subject, body, w->eventid, _ml_calendar_get_calendars (w->calendar)); /* Commit change to the database. */ db_commit (dbh); put_db_handle (dbh); /* Print confirmation page. */ ml_ok_window (w->pool, session, "That event was edited.", ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER); update_notes (w); } static void repaint (void *vw, ml_session session, const char *windowid, io_handle io) { ml_calendar_notes w = (ml_calendar_notes) vw; ml_widget_repaint (w->top, session, windowid, io); }