plugins/calendar plugins/libcalendaring plugins/tasklist
Thomas Brüderli
bruederli at kolabsys.com
Wed Jan 14 09:28:53 CET 2015
plugins/calendar/calendar.php | 26 ++++
plugins/calendar/calendar_ui.js | 108 ++++++++++++++----
plugins/calendar/drivers/calendar_driver.php | 15 ++
plugins/calendar/drivers/kolab/kolab_calendar.php | 38 ++++++
plugins/calendar/drivers/kolab/kolab_driver.php | 23 +++
plugins/calendar/localization/en_US.inc | 2
plugins/calendar/skins/larry/calendar.css | 31 +++++
plugins/calendar/skins/larry/templates/calendar.html | 5
plugins/calendar/skins/larry/templates/eventedit.html | 5
plugins/libcalendaring/libcalendaring.js | 33 +++++
plugins/tasklist/tasklist.js | 50 ++------
11 files changed, 270 insertions(+), 66 deletions(-)
New commits:
commit b02e2c3b8f9ef6a63eba80f84432b33a00da95d1
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Jan 14 09:27:48 2015 +0100
- Store relation to message when creating event from email (#4161)
- Move common functions to libcalendaring
- Assign dialog button classes directly
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 547be1e..f84d07a 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1636,6 +1636,15 @@ class calendar extends rcube_plugin
$event['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']);
}
+ // convert link URIs references into structs
+ if (array_key_exists('links', $event)) {
+ foreach ((array)$event['links'] as $i => $link) {
+ if (strpos($link, 'imap://') === 0 && ($msgref = $this->driver->get_message_reference($link))) {
+ $event['links'][$i] = $msgref;
+ }
+ }
+ }
+
// check for organizer in attendees list
$organizer = null;
foreach ((array)$event['attendees'] as $i => $attendee) {
@@ -1824,6 +1833,13 @@ class calendar extends rcube_plugin
$event['attachments'] = $attachments;
+ // convert link references into simple URIs
+ if (array_key_exists('links', $event)) {
+ $event['links'] = array_map(function($link) {
+ return is_array($link) ? $link['uri'] : strval($link);
+ }, (array)$event['links']);
+ }
+
// check for organizer in attendees
if ($action == 'new' || $action == 'edit') {
if (!$event['attendees'])
@@ -2932,9 +2948,15 @@ class calendar extends rcube_plugin
if ($message->headers) {
$event['title'] = trim($message->subject);
$event['description'] = trim($message->first_text_part());
-
+
+ $this->load_driver();
+
+ // add a reference to the email message
+ if ($msgref = $this->driver->get_message_reference($message->headers, $mbox)) {
+ $event['links'] = array($msgref);
+ }
// copy mail attachments to event
- if ($message->attachments) {
+ else if ($message->attachments) {
$eventid = 'cal:';
if (!is_array($_SESSION[self::SESSION_KEY]) || $_SESSION[self::SESSION_KEY]['id'] != $eventid) {
$_SESSION[self::SESSION_KEY] = array();
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index a2dc39c..45a537f 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -187,6 +187,7 @@ function rcube_calendar_ui(settings)
var fromunixtime = this.fromunixtime;
var parseISO8601 = this.parseISO8601;
var date2servertime = this.date2ISO8601;
+ var render_message_links = this.render_message_links;
/*** private methods ***/
@@ -471,6 +472,13 @@ function rcube_calendar_ui(settings)
// fetch attachments, some drivers doesn't set 'attachments' prop of the event?
}
+ // build attachments list
+ $('#event-links').hide();
+ if ($.isArray(event.links) && event.links.length) {
+ render_message_links(event.links || [], $('#event-links').children('.event-text'), false, 'calendar');
+ $('#event-links').show();
+ }
+
// list event attendees
if (calendar.attendees && event.attendees) {
// sort resources to the end
@@ -546,20 +554,30 @@ function rcube_calendar_ui(settings)
$('#event-rsvp .itip-reply-comment textarea').hide().val('');
}
- var buttons = {};
+ var buttons = [];
if (!temp && calendar.editable && event.editable !== false) {
- buttons[rcmail.gettext('edit', 'calendar')] = function() {
- event_edit_dialog('edit', event);
- };
- buttons[rcmail.gettext('delete', 'calendar')] = function() {
- me.delete_event(event);
- $dialog.dialog('close');
- };
+ buttons.push({
+ text: rcmail.gettext('edit', 'calendar'),
+ click: function() {
+ event_edit_dialog('edit', event);
+ }
+ });
+ buttons.push({
+ text: rcmail.gettext('delete', 'calendar'),
+ 'class': 'delete',
+ click: function() {
+ me.delete_event(event);
+ $dialog.dialog('close');
+ }
+ });
}
else {
- buttons[rcmail.gettext('close', 'calendar')] = function(){
- $dialog.dialog('close');
- };
+ buttons.push({
+ text: rcmail.gettext('close', 'calendar'),
+ click: function(){
+ $dialog.dialog('close');
+ }
+ });
}
// open jquery UI dialog
@@ -712,6 +730,14 @@ function rcube_calendar_ui(settings)
$('<option>').attr('value', event.categories).text(event.categories).appendTo(categories).prop('selected', true);
}
+ if ($.isArray(event.links) && event.links.length) {
+ render_message_links(event.links, $('#edit-event-links .event-text'), true, 'calendar');
+ $('#edit-event-links').show();
+ }
+ else {
+ $('#edit-event-links').hide();
+ }
+
// show warning if editing a recurring event
if (event.id && event.recurrence) {
var sel = event.thisandfuture ? 'future' : (event.isexception ? 'current' : 'all');
@@ -778,10 +804,13 @@ function rcube_calendar_ui(settings)
};
// init dialog buttons
- var buttons = {};
+ var buttons = [];
// save action
- buttons[rcmail.gettext('save', 'calendar')] = function() {
+ buttons.push({
+ text: rcmail.gettext('save', 'calendar'),
+ 'class': 'mainaction',
+ click: function() {
var start = parse_datetime(allday.checked ? '12:00' : starttime.val(), startdate.val());
var end = parse_datetime(allday.checked ? '13:00' : endtime.val(), enddate.val());
@@ -809,6 +838,7 @@ function rcube_calendar_ui(settings)
recurrence: me.serialize_recurrence(endtime.val()),
valarms: me.serialize_alarms('#edit-alarms'),
attendees: event_attendees,
+ links: me.selected_event.links,
deleted_attachments: rcmail.env.deleted_attachments,
attachments: []
};
@@ -865,18 +895,26 @@ function rcube_calendar_ui(settings)
update_event(action, data);
$dialog.dialog("close");
- };
+ } // end click:
+ });
if (event.id) {
- buttons[rcmail.gettext('delete', 'calendar')] = function() {
- me.delete_event(event);
- $dialog.dialog('close');
- };
+ buttons.push({
+ text: rcmail.gettext('delete', 'calendar'),
+ 'class': 'delete',
+ click: function() {
+ me.delete_event(event);
+ $dialog.dialog('close');
+ }
+ });
}
- buttons[rcmail.gettext('cancel', 'calendar')] = function() {
- $dialog.dialog("close");
- };
+ buttons.push({
+ text: rcmail.gettext('cancel', 'calendar'),
+ click: function() {
+ $dialog.dialog("close");
+ }
+ });
// show/hide tabs according to calendar's feature support
$('#edit-tab-attendees')[(calendar.attendees?'show':'hide')]();
@@ -900,7 +938,6 @@ function rcube_calendar_ui(settings)
title: rcmail.gettext((action == 'edit' ? 'edit_event' : 'new_event'), 'calendar'),
open: function() {
editform.attr('aria-hidden', 'false');
- $dialog.parent().find('.ui-dialog-buttonset .ui-button').first().addClass('mainaction');
},
close: function() {
editform.hide().attr('aria-hidden', 'true').appendTo(document.body);
@@ -2414,7 +2451,18 @@ function rcube_calendar_ui(settings)
})
$.each(listitems, function(idx, item) { mylist.append(item); });
}
-
+
+ // remove the link reference matching the given uri
+ function remove_link(elem)
+ {
+ var $elem = $(elem), uri = $elem.attr('data-uri');
+
+ me.selected_event.links = $.grep(me.selected_event.links, function(link) { return link.uri != uri; });
+
+ // remove UI list item
+ $elem.hide().closest('li').addClass('deleted');
+ }
+
// post the given event data to server
var update_event = function(action, data, add)
{
@@ -4054,6 +4102,20 @@ function rcube_calendar_ui(settings)
return false;
})
+ // register click handler for message links
+ $('#edit-event-links, #event-links').on('click', 'li a.messagelink', function(e) {
+ rcmail.open_window(this.href);
+ if (!rcube_event.is_keyboard(e) && this.blur)
+ this.blur();
+ return false;
+ });
+
+ // register click handler for message delete buttons
+ $('#edit-event-links').on('click', 'li a.delete', function(e) {
+ remove_link(e.target);
+ return false;
+ });
+
$('#agenda-listrange').change(function(e){
settings['agenda_range'] = parseInt($(this).val());
fc.fullCalendar('option', 'listRange', settings['agenda_range']).fullCalendar('render');
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index b6624ce..4ec53c4 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -378,6 +378,21 @@ abstract class calendar_driver
public function get_attachment_body($id, $event) { }
/**
+ * Build a struct representing the given message reference
+ *
+ * @param object|string $uri_or_headers rcube_message_header instance holding the message headers
+ * or an URI from a stored link referencing a mail message.
+ * @param string $folder IMAP folder the message resides in
+ *
+ * @return array An struct referencing the given IMAP message
+ */
+ public function get_message_reference($uri_or_headers, $folder = null)
+ {
+ // to be implemented by the derived classes
+ return false;
+ }
+
+ /**
* List availabale categories
* The default implementation reads them from config/user prefs
*/
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 2cc3c0b..b5c0844 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -391,6 +391,10 @@ class kolab_calendar extends kolab_storage_folder_api
if (!is_array($event))
return false;
+ // email links are stored separately
+ $links = $event['links'];
+ unset($event['links']);
+
//generate new event from RC input
$object = $this->_from_rcube_event($event);
$saved = $this->storage->save($object, 'event');
@@ -404,6 +408,9 @@ class kolab_calendar extends kolab_storage_folder_api
$saved = false;
}
else {
+ // save links in configuration.relation object
+ $this->save_links($event['uid'], $links);
+
$event['id'] = $event['uid'];
$this->events = array($event['uid'] => $this->_to_rcube_event($object));
}
@@ -425,6 +432,10 @@ class kolab_calendar extends kolab_storage_folder_api
if (!$old || PEAR::isError($old))
return false;
+ // email links are stored separately
+ $links = $event['links'];
+ unset($event['links']);
+
$object = $this->_from_rcube_event($event, $old);
$saved = $this->storage->save($object, 'event', $event['id']);
@@ -436,6 +447,9 @@ class kolab_calendar extends kolab_storage_folder_api
true, false);
}
else {
+ // save links in configuration.relation object
+ $this->save_links($event['uid'], $links);
+
$updated = true;
$this->events[$event['id']] = $this->_to_rcube_event($object);
@@ -491,6 +505,29 @@ class kolab_calendar extends kolab_storage_folder_api
return false;
}
+ /**
+ * Find messages linked with an event
+ */
+ protected function get_links($uid)
+ {
+ $storage = kolab_storage_config::get_instance();
+ return $storage->get_object_links($uid);
+ }
+
+ /**
+ *
+ */
+ protected function save_links($uid, $links)
+ {
+ // make sure we have a valid array
+ if (empty($links)) {
+ $links = array();
+ }
+
+ $storage = kolab_storage_config::get_instance();
+ $remove = array_diff($storage->get_object_links($uid), $links);
+ return $storage->save_object_links($uid, $links, $remove);
+ }
/**
* Create instances of a recurring event
@@ -635,6 +672,7 @@ class kolab_calendar extends kolab_storage_folder_api
{
$record['id'] = $record['uid'];
$record['calendar'] = $this->id;
+ $record['links'] = $this->get_links($record['uid']);
return kolab_driver::to_rcube_event($record);
}
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 94e1e41..8720dd8 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -772,7 +772,7 @@ class kolab_driver extends calendar_driver
break;
}
}
-
+
if ($success && $this->freebusy_trigger)
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
@@ -980,7 +980,7 @@ class kolab_driver extends calendar_driver
$success = $storage->update_event($event);
break;
}
-
+
if ($success && $this->freebusy_trigger)
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
@@ -1216,6 +1216,24 @@ class kolab_driver extends calendar_driver
}
/**
+ * Build a struct representing the given message reference
+ *
+ * @see calendar_driver::get_message_reference()
+ */
+ public function get_message_reference($uri_or_headers, $folder = null)
+ {
+ if (is_object($uri_or_headers)) {
+ $uri_or_headers = kolab_storage_config::get_message_uri($uri_or_headers, $folder);
+ }
+
+ if (is_string($uri_or_headers)) {
+ return kolab_storage_config::get_message_reference($uri_or_headers, 'event');
+ }
+
+ return false;
+ }
+
+ /**
* List availabale categories
* The default implementation reads them from config/user prefs
*/
@@ -1403,7 +1421,6 @@ class kolab_driver extends calendar_driver
return $record;
}
-
/**
* Provide a list of revisions for the given event
*
diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc
index b63b930..ebea976 100644
--- a/plugins/calendar/localization/en_US.inc
+++ b/plugins/calendar/localization/en_US.inc
@@ -87,6 +87,7 @@ $labels['sensitivity'] = 'Privacy';
$labels['public'] = 'public';
$labels['private'] = 'private';
$labels['confidential'] = 'confidential';
+$labels['links'] = 'Reference';
$labels['alarms'] = 'Reminder';
$labels['comment'] = 'Comment';
$labels['created'] = 'Created';
@@ -95,6 +96,7 @@ $labels['unknown'] = 'Unknown';
$labels['eventoptions'] = 'Options';
$labels['generated'] = 'generated at';
$labels['eventhistory'] = 'History';
+$labels['removelink'] = 'Remove email reference';
$labels['printdescriptions'] = 'Print descriptions';
$labels['parentcalendar'] = 'Insert inside';
$labels['searchearlierdates'] = '« Search for earlier events';
diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
index b9b87b0..b38b312 100644
--- a/plugins/calendar/skins/larry/calendar.css
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -958,6 +958,37 @@ div.form-section,
text-overflow: ellipsis;
}
+#event-links .attachmentslist {
+ display: inline-block;
+}
+
+#event-links label,
+#edit-event-links label {
+ float: left;
+ margin-top: 0.3em;
+ padding-right: 0.75em;
+}
+
+#edit-event-links .event-text {
+ margin-left: 8em;
+ min-height: 22px;
+}
+
+#edit-event-links .attachmentslist li.message a.messagelink,
+#event-links .attachmentslist li.message a.messagelink {
+ padding: 0 0 0 24px;
+}
+
+#edit-event-links .attachmentslist li a.delete {
+ top: 0;
+ background-position: -6px -378px;
+}
+
+#edit-event-links .attachmentslist li.deleted a.messagelink,
+#edit-event-links .attachmentslist li.deleted a.messagelink:hover {
+ text-decoration: line-through;
+}
+
#eventedit .formtable td.label {
min-width: 6em;
}
diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html
index 40a9873..eecb24e 100644
--- a/plugins/calendar/skins/larry/templates/calendar.html
+++ b/plugins/calendar/skins/larry/templates/calendar.html
@@ -135,6 +135,11 @@
<label><roundcube:label name="calendar.sensitivity" /></label>
<span class="event-text"></span>
</div>
+ <div class="event-section" id="event-links">
+ <label><roundcube:label name="calendar.links" /></label>
+ <span class="event-text"></span>
+ <br style="clear:left">
+ </div>
<div class="event-section" id="event-attachments">
<label><roundcube:label name="attachments" /></label>
<div class="event-text"></div>
diff --git a/plugins/calendar/skins/larry/templates/eventedit.html b/plugins/calendar/skins/larry/templates/eventedit.html
index 28cfc7f..4d0585b 100644
--- a/plugins/calendar/skins/larry/templates/eventedit.html
+++ b/plugins/calendar/skins/larry/templates/eventedit.html
@@ -70,6 +70,11 @@
<label for="edit-sensitivity"><roundcube:label name="calendar.sensitivity" /></label>
<roundcube:object name="plugin.sensitivity_select" id="edit-sensitivity" />
</div>
+ <div class="event-section" id="edit-event-links">
+ <label><roundcube:label name="calendar.links" /></label>
+ <div class="event-text"></div>
+ <br style="clear:left">
+ </div>
</div>
<!-- recurrence settings -->
<div id="event-panel-recurrence">
diff --git a/plugins/libcalendaring/libcalendaring.js b/plugins/libcalendaring/libcalendaring.js
index d04a024..f8f2a55 100644
--- a/plugins/libcalendaring/libcalendaring.js
+++ b/plugins/libcalendaring/libcalendaring.js
@@ -788,6 +788,39 @@ function rcube_libcalendaring(settings)
}
};
+
+ // Render message reference links to the given container
+ this.render_message_links = function(links, container, edit, plugin)
+ {
+ var ul = $('<ul>').addClass('attachmentslist');
+
+ $.each(links, function(i, link) {
+ if (!link.mailurl)
+ return true; // continue
+
+ var li = $('<li>').addClass('link')
+ .addClass('message eml')
+ .append($('<a>')
+ .attr('href', link.mailurl)
+ .addClass('messagelink')
+ .text(link.subject || link.uri)
+ )
+ .appendTo(ul);
+
+ // add icon to remove the link
+ if (edit) {
+ $('<a>')
+ .attr('href', '#delete')
+ .attr('title', rcmail.gettext('removelink', plugin))
+ .attr('data-uri', link.uri)
+ .addClass('delete')
+ .text(rcmail.gettext('delete'))
+ .appendTo(li);
+ }
+ });
+
+ container.empty().append(ul);
+ }
}
////// static methods
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index ed1972e..acb7505 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -129,6 +129,7 @@ function rcube_tasklist_ui(settings)
var parse_datetime = this.parse_datetime;
var date2unixtime = this.date2unixtime;
var fromunixtime = this.fromunixtime;
+ var render_message_links = this.render_message_links;
/**
* initialize the tasks UI
@@ -597,6 +598,12 @@ function rcube_tasklist_ui(settings)
return false;
});
+ // register click handler for message delete buttons
+ $('#taskedit-links').on('click', 'li a.delete', function(e) {
+ remove_link(e.target);
+ return false;
+ });
+
// handle global document clicks: close popup menus
$(document.body).click(clear_popups);
@@ -1840,7 +1847,7 @@ function rcube_tasklist_ui(settings)
// build attachments list
$('#task-links').hide();
if ($.isArray(rec.links) && rec.links.length) {
- task_show_links(rec.links || [], $('#task-links').children('.task-text'));
+ render_message_links(rec.links || [], $('#task-links').children('.task-text'), false, 'tasklist');
$('#task-links').show();
}
@@ -2052,7 +2059,7 @@ function rcube_tasklist_ui(settings)
me.set_alarms_edit('#taskedit-alarms', action != 'new' && rec.valarms ? rec.valarms : []);
if ($.isArray(rec.links) && rec.links.length) {
- task_show_links(rec.links, $('#taskedit-links .task-text'), true);
+ render_message_links(rec.links, $('#taskedit-links .task-text'), true, 'tasklist');
$('#taskedit-links').show();
}
else {
@@ -2358,48 +2365,15 @@ function rcube_tasklist_ui(settings)
/**
*
*/
- function task_show_links(links, container, edit)
+ function remove_link(elem)
{
- var dellink, ul = $('<ul>').addClass('attachmentslist');
-
- $.each(links, function(i, link) {
- var li = $('<li>').addClass('link')
- .addClass('message eml')
- .append($('<a>')
- .attr('href', link.mailurl)
- .addClass('messagelink')
- .text(link.subject || link.uri)
- )
- .appendTo(ul);
-
- // add icon to remove the link
- if (edit) {
- $('<a>')
- .attr('href', '#delete')
- .attr('title', rcmail.gettext('removelink','tasklist'))
- .addClass('delete')
- .text(rcmail.gettext('delete'))
- .click({ uri:link.uri }, function(e) {
- remove_link(this, e.data.uri);
- return false;
- })
- .appendTo(li);
- }
- });
-
- container.empty().append(ul);
- }
+ var $elem = $(elem), uri = $elem.attr('data-uri');
- /**
- *
- */
- function remove_link(elem, uri)
- {
// remove the link item matching the given uri
me.selected_task.links = $.grep(me.selected_task.links, function(link) { return link.uri != uri; });
// remove UI list item
- $(elem).hide().closest('li').addClass('deleted');
+ $elem.hide().closest('li').addClass('deleted');
}
/**
More information about the commits
mailing list