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