diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 19aa4bdb..e4c3b291 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1652,11 +1652,11 @@ class calendar extends rcube_plugin */ private function notify_attendees($event, $old, $action = 'edit') { - if ($action == 'remove') { + if ($action == 'remove' || ($event['status'] == 'CANCELLED' && $old['status'] != $event['status'])) { $event['cancelled'] = true; $is_cancelled = true; } - + $itip = $this->load_itip(); $emails = $this->get_user_emails(); diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index e8678564..92a17653 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -325,6 +325,11 @@ function rcube_calendar_ui(settings) $('#event-priority').show().children('.event-text').html(Q(event.priority+' '+priolabels[event.priority])); } + if (event.status) { + var status_lc = String(event.status).toLowerCase(); + $('#event-status').show().children('.event-text').html(Q(rcmail.gettext(status_lc,'calendar'))); + $dialog.addClass('status-'+status_lc); + } if (event.sensitivity && event.sensitivity != 'public') { $('#event-sensitivity').show().children('.event-text').html(Q(sensitivitylabels[event.sensitivity])); $dialog.addClass('sensitivity-'+event.sensitivity); @@ -491,6 +496,7 @@ function rcube_calendar_ui(settings) var vurl = $('#edit-url').val(event.vurl || ''); var categories = $('#edit-categories').val(event.categories); var calendars = $('#edit-calendar').val(event.calendar); + var eventstatus = $('#edit-event-status').val(event.status); var freebusy = $('#edit-free-busy').val(event.free_busy); var priority = $('#edit-priority').val(event.priority); var sensitivity = $('#edit-sensitivity').val(event.sensitivity); @@ -682,6 +688,7 @@ function rcube_calendar_ui(settings) free_busy: freebusy.val(), priority: priority.val(), sensitivity: sensitivity.val(), + status: eventstatus.val(), recurrence: '', alarms: '', attendees: event_attendees, @@ -2146,6 +2153,9 @@ function rcube_calendar_ui(settings) if (event.alarms) element.find('div.fc-event-time').append(''); } + if (event.status) { + element.addClass('cal-event-status-' + String(event.status).toLowerCase()); + } }; diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php index d883f18c..6769cfdc 100644 --- a/plugins/calendar/drivers/calendar_driver.php +++ b/plugins/calendar/drivers/calendar_driver.php @@ -52,6 +52,7 @@ * 'recurrence_id' => 'ID of the recurrence group', // usually the ID of the starting event * 'categories' => 'Event category', * 'free_busy' => 'free|busy|outofoffice|tentative', // Show time as + * 'status' => 'TENTATIVE|CONFIRMED|CANCELLED', // event status according to RFC 2445 * 'priority' => 0-9, // Event priority (0=undefined, 1=highest, 9=lowest) * 'sensitivity' => 'public|private|confidential', // Event sensitivity * 'alarms' => '-15M:DISPLAY', // DEPRECATED Reminder settings inspired by valarm definition (e.g. display alert 15 minutes before event) diff --git a/plugins/calendar/drivers/database/SQL/mysql.initial.sql b/plugins/calendar/drivers/database/SQL/mysql.initial.sql index 945d429b..c45b3f2a 100644 --- a/plugins/calendar/drivers/database/SQL/mysql.initial.sql +++ b/plugins/calendar/drivers/database/SQL/mysql.initial.sql @@ -44,6 +44,7 @@ CREATE TABLE IF NOT EXISTS `events` ( `free_busy` tinyint(1) NOT NULL DEFAULT '0', `priority` tinyint(1) NOT NULL DEFAULT '0', `sensitivity` tinyint(1) NOT NULL DEFAULT '0', + `status` varchar(32) NOT NULL DEFAULT '', `alarms` varchar(255) DEFAULT NULL, `attendees` text DEFAULT NULL, `notifyat` datetime DEFAULT NULL, diff --git a/plugins/calendar/drivers/database/SQL/mysql/2014040900.sql b/plugins/calendar/drivers/database/SQL/mysql/2014040900.sql new file mode 100644 index 00000000..814e10d4 --- /dev/null +++ b/plugins/calendar/drivers/database/SQL/mysql/2014040900.sql @@ -0,0 +1,3 @@ +-- MySQL database updates since version 1.0 + +ALTER TABLE `events` ADD `status` VARCHAR(32) NOT NULL AFTER `sensitivity`; diff --git a/plugins/calendar/drivers/database/SQL/postgres.initial.sql b/plugins/calendar/drivers/database/SQL/postgres.initial.sql index 00a91c20..007bbf29 100644 --- a/plugins/calendar/drivers/database/SQL/postgres.initial.sql +++ b/plugins/calendar/drivers/database/SQL/postgres.initial.sql @@ -60,6 +60,7 @@ CREATE TABLE events ( free_busy smallint NOT NULL DEFAULT 0, priority smallint NOT NULL DEFAULT 0, sensitivity smallint NOT NULL DEFAULT 0, + status character varying(32) NOT NULL, alarms varchar(255) DEFAULT NULL, attendees text DEFAULT NULL, notifyat timestamp without time zone DEFAULT NULL, diff --git a/plugins/calendar/drivers/database/SQL/postgres/2014040900.sql b/plugins/calendar/drivers/database/SQL/postgres/2014040900.sql new file mode 100644 index 00000000..310744c9 --- /dev/null +++ b/plugins/calendar/drivers/database/SQL/postgres/2014040900.sql @@ -0,0 +1,3 @@ +-- Postgres database updates since version 1.0 + +ALTER TABLE events ADD status character varying(32) NOT NULL; diff --git a/plugins/calendar/drivers/database/SQL/sqlite.initial.sql b/plugins/calendar/drivers/database/SQL/sqlite.initial.sql index 6e0643b1..3d359073 100644 --- a/plugins/calendar/drivers/database/SQL/sqlite.initial.sql +++ b/plugins/calendar/drivers/database/SQL/sqlite.initial.sql @@ -43,6 +43,7 @@ CREATE TABLE events ( free_busy tinyint(1) NOT NULL default '0', priority tinyint(1) NOT NULL default '0', sensitivity tinyint(1) NOT NULL default '0', + status varchar(32) NOT NULL default '', alarms varchar(255) default NULL, attendees text default NULL, notifyat datetime default NULL, diff --git a/plugins/calendar/drivers/database/SQL/sqlite/2014040900.sql b/plugins/calendar/drivers/database/SQL/sqlite/2014040900.sql new file mode 100644 index 00000000..ff8ed173 --- /dev/null +++ b/plugins/calendar/drivers/database/SQL/sqlite/2014040900.sql @@ -0,0 +1,67 @@ +-- SQLite database updates since version 0.9-beta + +-- ALTER TABLE events ADD url varchar(255) NOT NULL AFTER categories; + +CREATE TABLE temp_events ( + event_id integer NOT NULL PRIMARY KEY, + calendar_id integer NOT NULL default '0', + recurrence_id integer NOT NULL default '0', + uid varchar(255) NOT NULL default '', + created datetime NOT NULL default '1000-01-01 00:00:00', + changed datetime NOT NULL default '1000-01-01 00:00:00', + sequence integer NOT NULL default '0', + start datetime NOT NULL default '1000-01-01 00:00:00', + end datetime NOT NULL default '1000-01-01 00:00:00', + recurrence varchar(255) default NULL, + title varchar(255) NOT NULL, + description text NOT NULL, + location varchar(255) NOT NULL default '', + categories varchar(255) NOT NULL default '', + url varchar(255) NOT NULL default '', + all_day tinyint(1) NOT NULL default '0', + free_busy tinyint(1) NOT NULL default '0', + priority tinyint(1) NOT NULL default '0', + sensitivity tinyint(1) NOT NULL default '0', + alarms varchar(255) default NULL, + attendees text default NULL, + notifyat datetime default NULL +); + +INSERT INTO temp_events (event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, url, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat) + SELECT event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, url, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat + FROM events; + +DROP TABLE events; + +CREATE TABLE events ( + event_id integer NOT NULL PRIMARY KEY, + calendar_id integer NOT NULL default '0', + recurrence_id integer NOT NULL default '0', + uid varchar(255) NOT NULL default '', + created datetime NOT NULL default '1000-01-01 00:00:00', + changed datetime NOT NULL default '1000-01-01 00:00:00', + sequence integer NOT NULL default '0', + start datetime NOT NULL default '1000-01-01 00:00:00', + end datetime NOT NULL default '1000-01-01 00:00:00', + recurrence varchar(255) default NULL, + title varchar(255) NOT NULL, + description text NOT NULL, + location varchar(255) NOT NULL default '', + categories varchar(255) NOT NULL default '', + url varchar(255) NOT NULL default '', + all_day tinyint(1) NOT NULL default '0', + free_busy tinyint(1) NOT NULL default '0', + priority tinyint(1) NOT NULL default '0', + sensitivity tinyint(1) NOT NULL default '0', + status varchar(32) NOT NULL default '', + alarms varchar(255) default NULL, + attendees text default NULL, + notifyat datetime default NULL, + CONSTRAINT fk_events_calendar_id FOREIGN KEY (calendar_id) + REFERENCES calendars(calendar_id) +); + +INSERT INTO events (event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, url, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat) + SELECT event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, url, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat + FROM temp_events; + diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php index b50b23bb..77e49518 100644 --- a/plugins/calendar/drivers/database/database_driver.php +++ b/plugins/calendar/drivers/database/database_driver.php @@ -271,8 +271,8 @@ class database_driver extends calendar_driver $this->rc->db->query(sprintf( "INSERT INTO " . $this->db_events . " - (calendar_id, created, changed, uid, %s, %s, all_day, recurrence, title, description, location, categories, url, free_busy, priority, sensitivity, attendees, alarms, notifyat) - VALUES (?, %s, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + (calendar_id, created, changed, uid, %s, %s, all_day, recurrence, title, description, location, categories, url, free_busy, priority, sensitivity, status, attendees, alarms, notifyat) + VALUES (?, %s, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $this->rc->db->quote_identifier('start'), $this->rc->db->quote_identifier('end'), $this->rc->db->now(), @@ -292,6 +292,7 @@ class database_driver extends calendar_driver intval($event['free_busy']), intval($event['priority']), intval($event['sensitivity']), + strval($event['status']), $event['attendees'], $event['alarms'], $event['notifyat'] @@ -450,7 +451,11 @@ class database_driver extends calendar_driver $event['_recurrence'] = rtrim($rrule, ';'); $event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]); $event['sensitivity'] = intval($this->sensitivity_map[strtolower($event['sensitivity'])]); - + + if ($event['free_busy'] == 'tentative') { + $event['status'] = 'TENTATIVE'; + } + if (isset($event['allday'])) { $event['all_day'] = $event['allday'] ? 1 : 0; } @@ -499,13 +504,13 @@ class database_driver extends calendar_driver { $event = $this->_save_preprocess($event); $sql_set = array(); - $set_cols = array('start', 'end', 'all_day', 'recurrence_id', 'sequence', 'title', 'description', 'location', 'categories', 'url', 'free_busy', 'priority', 'sensitivity', 'attendees', 'alarms', 'notifyat'); + $set_cols = array('start', 'end', 'all_day', 'recurrence_id', 'sequence', 'title', 'description', 'location', 'categories', 'url', 'free_busy', 'priority', 'sensitivity', 'status', 'attendees', 'alarms', 'notifyat'); foreach ($set_cols as $col) { if (is_object($event[$col]) && is_a($event[$col], 'DateTime')) $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($event[$col]->format(self::DB_DATE_FORMAT)); else if (is_array($event[$col])) $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote(join(',', $event[$col])); - else if (isset($event[$col])) + else if (array_key_exists($col, $event)) $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($event[$col]); } @@ -581,11 +586,11 @@ class database_driver extends calendar_driver $next_start->setTimezone($this->server_timezone); $next_end = clone $next_start; $next_end->add($duration); - $notify_at = $this->_get_notification(array('alarms' => $event['alarms'], 'start' => $next_start, 'end' => $next_end)); + $notify_at = $this->_get_notification(array('alarms' => $event['alarms'], 'start' => $next_start, 'end' => $next_end, 'status' => $event['status'])); $query = $this->rc->db->query(sprintf( "INSERT INTO " . $this->db_events . " - (calendar_id, recurrence_id, created, changed, uid, %s, %s, all_day, recurrence, title, description, location, categories, url, free_busy, priority, sensitivity, alarms, attendees, notifyat) - SELECT calendar_id, ?, %s, %s, uid, ?, ?, all_day, recurrence, title, description, location, categories, url, free_busy, priority, sensitivity, alarms, attendees, ? + (calendar_id, recurrence_id, created, changed, uid, %s, %s, all_day, recurrence, title, description, location, categories, url, free_busy, priority, sensitivity, status, alarms, attendees, notifyat) + SELECT calendar_id, ?, %s, %s, uid, ?, ?, all_day, recurrence, title, description, location, categories, url, free_busy, priority, sensitivity, status, alarms, attendees, ? FROM " . $this->db_events . " WHERE event_id=? AND calendar_id IN (" . $this->calendar_ids . ")", $this->rc->db->quote_identifier('start'), $this->rc->db->quote_identifier('end'), diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php index 129d30f4..46fbed90 100644 --- a/plugins/calendar/drivers/kolab/kolab_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_calendar.php @@ -597,6 +597,10 @@ class kolab_calendar if (is_array($record['categories'])) $record['categories'] = $record['categories'][0]; + // the cancelled flag transltes into status=CANCELLED + if ($record['cancelled']) + $record['status'] = 'CANCELLED'; + // The web client only supports DISPLAY type of alarms if (!empty($record['alarms'])) $record['alarms'] = preg_replace('/:[A-Z]+$/', ':DISPLAY', $record['alarms']); diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php index 00306859..2f544999 100644 --- a/plugins/calendar/lib/calendar_ui.php +++ b/plugins/calendar/lib/calendar_ui.php @@ -74,6 +74,7 @@ class calendar_ui $this->cal->register_handler('plugin.calendar_select', array($this, 'calendar_select')); $this->cal->register_handler('plugin.identity_select', array($this, 'identity_select')); $this->cal->register_handler('plugin.category_select', array($this, 'category_select')); + $this->cal->register_handler('plugin.status_select', array($this, 'status_select')); $this->cal->register_handler('plugin.freebusy_select', array($this, 'freebusy_select')); $this->cal->register_handler('plugin.priority_select', array($this, 'priority_select')); $this->cal->register_handler('plugin.sensitivity_select', array($this, 'sensitivity_select')); @@ -303,6 +304,20 @@ class calendar_ui return $select->show(null); } + /** + * Render a HTML select box for status property + */ + function status_select($attrib = array()) + { + $attrib['name'] = 'status'; + $select = new html_select($attrib); + $select->add('---', ''); + $select->add($this->cal->gettext('confirmed'), 'CONFIRMED'); + $select->add($this->cal->gettext('cancelled'), 'CANCELLED'); + //$select->add($this->cal->gettext('tentative'), 'TENTATIVE'); + return $select->show(null); + } + /** * Render a HTML select box for free/busy/out-of-office property */ diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc index b243c24b..3df791cd 100644 --- a/plugins/calendar/localization/en_US.inc +++ b/plugins/calendar/localization/en_US.inc @@ -60,6 +60,9 @@ $labels['free'] = 'Free'; $labels['busy'] = 'Busy'; $labels['outofoffice'] = 'Out of Office'; $labels['tentative'] = 'Tentative'; +$labels['status'] = 'Status'; +$labels['confirmed'] = 'Confirmed'; +$labels['cancelled'] = 'Cancelled'; $labels['priority'] = 'Priority'; $labels['sensitivity'] = 'Privacy'; $labels['public'] = 'public'; diff --git a/plugins/calendar/skins/classic/calendar.css b/plugins/calendar/skins/classic/calendar.css index 87ef2da0..937a7b65 100644 --- a/plugins/calendar/skins/classic/calendar.css +++ b/plugins/calendar/skins/classic/calendar.css @@ -446,6 +446,10 @@ a.miniColors-trigger { margin: 0 -0.2em; } +#eventshow.status-cancelled { + background: url(images/badge_cancelled.png) top right no-repeat; +} + #eventshow.sensitivity-private { background: url(images/badge_private.png) top right no-repeat; } @@ -1257,6 +1261,10 @@ span.spacer { font-weight: bold; } +.cal-event-status-cancelled .fc-event-title { + text-decoration: line-through; +} + .fc-event-hori .fc-event-title { font-weight: normal; white-space: nowrap; diff --git a/plugins/calendar/skins/classic/images/badge_cancelled.png b/plugins/calendar/skins/classic/images/badge_cancelled.png new file mode 100644 index 00000000..b89029e0 Binary files /dev/null and b/plugins/calendar/skins/classic/images/badge_cancelled.png differ diff --git a/plugins/calendar/skins/classic/templates/eventedit.html b/plugins/calendar/skins/classic/templates/eventedit.html index 3bc4a488..7e7170d2 100644 --- a/plugins/calendar/skins/classic/templates/eventedit.html +++ b/plugins/calendar/skins/classic/templates/eventedit.html @@ -52,6 +52,10 @@ +
+ + +
diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css index 46f0b3c7..25ad82cf 100644 --- a/plugins/calendar/skins/larry/calendar.css +++ b/plugins/calendar/skins/larry/calendar.css @@ -536,6 +536,10 @@ a.miniColors-trigger { margin: 0 -0.2em; } +#eventshow.status-cancelled { + background: url(images/badge_cancelled.png) top right no-repeat; +} + #eventshow.sensitivity-private { background: url(images/badge_private.png) top right no-repeat; } @@ -1395,6 +1399,10 @@ a.dropdown-link:after { font-weight: bold; } +.cal-event-status-cancelled .fc-event-title { + text-decoration: line-through; +} + .fc-event-hori .fc-event-title { font-weight: normal; white-space: nowrap; diff --git a/plugins/calendar/skins/larry/images/badge_cancelled.png b/plugins/calendar/skins/larry/images/badge_cancelled.png new file mode 100644 index 00000000..b89029e0 Binary files /dev/null and b/plugins/calendar/skins/larry/images/badge_cancelled.png differ diff --git a/plugins/calendar/skins/larry/images/badge_confidential.png b/plugins/calendar/skins/larry/images/badge_confidential.png index e12e788e..5b0bd7c3 100644 Binary files a/plugins/calendar/skins/larry/images/badge_confidential.png and b/plugins/calendar/skins/larry/images/badge_confidential.png differ diff --git a/plugins/calendar/skins/larry/images/badge_private.png b/plugins/calendar/skins/larry/images/badge_private.png index acf32070..37ce2067 100644 Binary files a/plugins/calendar/skins/larry/images/badge_private.png and b/plugins/calendar/skins/larry/images/badge_private.png differ diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html index 6895faff..debe7ec4 100644 --- a/plugins/calendar/skins/larry/templates/calendar.html +++ b/plugins/calendar/skins/larry/templates/calendar.html @@ -91,6 +91,10 @@
+
+ + +
diff --git a/plugins/calendar/skins/larry/templates/eventedit.html b/plugins/calendar/skins/larry/templates/eventedit.html index 95be8eab..85b3a771 100644 --- a/plugins/calendar/skins/larry/templates/eventedit.html +++ b/plugins/calendar/skins/larry/templates/eventedit.html @@ -48,6 +48,10 @@
+
+ + +
diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php index d33df671..edc0ddee 100644 --- a/plugins/libcalendaring/libcalendaring.php +++ b/plugins/libcalendaring/libcalendaring.php @@ -400,7 +400,7 @@ class libcalendaring extends rcube_plugin */ public static function get_next_alarm($rec, $type = 'event') { - if (!$rec['alarms']) + if (!$rec['alarms'] || $rec['cancelled'] || $rec['status'] == 'CANCELLED') return null; if ($type == 'task') {