6 commits - plugins/kolab_notes plugins/libkolab plugins/tasklist

Thomas Brüderli bruederli at kolabsys.com
Mon Oct 13 15:34:19 CEST 2014


 plugins/kolab_notes/kolab_notes.php                      |   79 -----------
 plugins/kolab_notes/kolab_notes_ui.php                   |    2 
 plugins/kolab_notes/localization/en_US.inc               |    1 
 plugins/kolab_notes/notes.js                             |   34 +++-
 plugins/kolab_notes/skins/larry/notes.css                |   12 +
 plugins/libkolab/lib/kolab_format_configuration.php      |    4 
 plugins/libkolab/lib/kolab_storage_config.php            |  106 ++++++++++++++-
 plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php |   72 +++++++++-
 plugins/tasklist/drivers/tasklist_driver.php             |   14 +
 plugins/tasklist/localization/en_US.inc                  |    2 
 plugins/tasklist/skins/larry/sprites.png                 |binary
 plugins/tasklist/skins/larry/tasklist.css                |   50 +++++++
 plugins/tasklist/skins/larry/templates/mainview.html     |    4 
 plugins/tasklist/skins/larry/templates/taskedit.html     |    5 
 plugins/tasklist/tasklist.js                             |   68 +++++++++
 plugins/tasklist/tasklist.php                            |   61 ++++++++
 16 files changed, 427 insertions(+), 87 deletions(-)

New commits:
commit ff139fe6627102033dc54c2ab7894b85e028e452
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Mon Oct 13 15:34:10 2014 +0200

    Allow to delete email message links from notes

diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php
index 369f1c5..4def121 100644
--- a/plugins/kolab_notes/kolab_notes.php
+++ b/plugins/kolab_notes/kolab_notes.php
@@ -998,6 +998,9 @@ class kolab_notes extends rcube_plugin
 
     private function save_links($uid, $links)
     {
+        if (empty($links)) {
+            $links = array();
+        }
         $config = kolab_storage_config::get_instance();
         $remove = array_diff($config->get_object_links($uid), $links);
         return $config->save_object_links($uid, $links, $remove);
diff --git a/plugins/kolab_notes/kolab_notes_ui.php b/plugins/kolab_notes/kolab_notes_ui.php
index 1726980..bb13813 100644
--- a/plugins/kolab_notes/kolab_notes_ui.php
+++ b/plugins/kolab_notes/kolab_notes_ui.php
@@ -102,7 +102,7 @@ class kolab_notes_ui
 
         $this->rc->output->set_env('kolab_notes_settings', $settings);
 
-        $this->rc->output->add_label('save','cancel');
+        $this->rc->output->add_label('save','cancel','delete');
     }
 
     public function folders($attrib)
diff --git a/plugins/kolab_notes/localization/en_US.inc b/plugins/kolab_notes/localization/en_US.inc
index 939946c..19d4139 100644
--- a/plugins/kolab_notes/localization/en_US.inc
+++ b/plugins/kolab_notes/localization/en_US.inc
@@ -34,6 +34,7 @@ $labels['listsearchresults'] = 'Additional notebooks';
 $labels['nrnotebooksfound'] = '$nr notebooks found';
 $labels['nonotebooksfound'] = 'No notebooks found';
 $labels['removelist'] = 'Remove from list';
+$labels['removelink'] = 'Remove email reference';
 
 $labels['savingdata'] = 'Saving data...';
 $labels['recordnotfound'] = 'Record not found';
diff --git a/plugins/kolab_notes/notes.js b/plugins/kolab_notes/notes.js
index 09813fe..5a9e05f 100644
--- a/plugins/kolab_notes/notes.js
+++ b/plugins/kolab_notes/notes.js
@@ -842,16 +842,19 @@ function rcube_kolab_notes_ui(settings)
                         .text(link.subject || link.uri)
                     )
                     .appendTo(attachmentslist);
-/*
+
                 if (!readonly && !data._from_mail) {
                     $('<a>')
                         .attr('href', '#delete')
-                        .attr('title', rcmail.gettext('delete'))
+                        .attr('title', rcmail.gettext('removelink', 'kolab_notes'))
                         .addClass('delete')
                         .html(rcmail.gettext('delete'))
+                        .click({ uri:link.uri }, function(e) {
+                            remove_link(this, e.data.uri);
+                            return false;
+                        })
                         .appendTo(li);
                 }
-*/
             });
         }
 
@@ -939,6 +942,19 @@ function rcube_kolab_notes_ui(settings)
     }
 
     /**
+     *
+     */
+    function remove_link(elem, uri)
+    {
+        // remove the link item matching the given uri
+        me.selected_note.links = $.grep(me.selected_note.links, function(link) { return link.uri != uri; });
+        me.selected_note._links_removed = true
+
+        // remove UI list item
+        $(elem).hide().closest('li').addClass('deleted');
+    }
+
+    /**
      * Open a new window to print the currently selected note
      */
     function print_note()
@@ -1134,10 +1150,6 @@ function rcube_kolab_notes_ui(settings)
 
         var savedata = get_save_data();
 
-        // copy links as they're yet immutable
-        if (me.selected_note.links)
-            savedata.links = me.selected_note.links;
-
         // add reference to old list if changed
         if (me.selected_note.list && savedata.list != me.selected_note.list) {
             savedata._fromlist = me.selected_note.list;
@@ -1175,6 +1187,11 @@ function rcube_kolab_notes_ui(settings)
             tags: []
         };
 
+        // copy links
+        if ($.isArray(me.selected_note.links)) {
+            savedata.links = me.selected_note.links;
+        }
+
         // collect tags
         $('.tagedit-list input[type="hidden"]', rcmail.gui_objects.noteviewtitle).each(function(i, elem){
             if (elem.value)
@@ -1203,7 +1220,8 @@ function rcube_kolab_notes_ui(settings)
         return savedata.title != me.selected_note.title
             || savedata.description != me.selected_note.description
             || savedata.tags.join(',') != (me.selected_note.tags || []).join(',')
-            || savedata.list != me.selected_note.list;
+            || savedata.list != me.selected_note.list
+            || me.selected_note._links_removed;
     }
 
     /**
diff --git a/plugins/kolab_notes/skins/larry/notes.css b/plugins/kolab_notes/skins/larry/notes.css
index 24daa9d..1937da6 100644
--- a/plugins/kolab_notes/skins/larry/notes.css
+++ b/plugins/kolab_notes/skins/larry/notes.css
@@ -530,14 +530,26 @@
 }
 
 .notesview .attachmentslist li.message.eml {
+	position: relative;
 	display: inline-block;
 	background-image: url(sprites.png);
 	background-position: -6px -128px;
 	margin-right: 1em;
+	padding-right: 28px;
 }
 
 .notesview .attachmentslist li.message a.messagelink {
 	padding-left: 24px;
+	padding-right: 0;
+}
+
+.notesview .attachmentslist li.message a.delete {
+	background-position: -6px -378px;
+}
+
+.notesview .attachmentslist li.deleted a.messagelink,
+.notesview .attachmentslist li.deleted a.messagelink:hover {
+	text-decoration: line-through;
 }
 
 ul.toolbarmenu li .appendnote span.icon {


commit 44c2639cbeb9bf612d178515c4db2bff0235a38d
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Mon Oct 13 15:33:39 2014 +0200

    Save email message a task is created from as relation (#3439); display the reference in the task details and edit dialogs

diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
index 817cfdf..e44b2c6 100644
--- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
+++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
@@ -820,6 +820,59 @@ class tasklist_kolab_driver extends tasklist_driver
     }
 
     /**
+     * Find messages linked with a task record
+     */
+    private function get_links($uid)
+    {
+        $config = kolab_storage_config::get_instance();
+        return array_map(array($this, '_convert_message_uri'), $config->get_object_links($uid));
+    }
+
+    /**
+     *
+     */
+    private function save_links($uid, $links)
+    {
+        // make sure we have a valid array
+        if (empty($links)) {
+            $links = array();
+        }
+
+        // convert the given (simplified) message links into absolute IMAP URIs
+        $links = array_map(function($link) {
+            $url = parse_url(substr($link, 8));
+            parse_str($url['query'], $linkref);
+
+            $path = explode('/', $url['path']);
+            $linkref['uid'] = array_pop($path);
+            $linkref['folder'] = join('/', array_map('rawurldecode', $path));
+
+            return kolab_storage_config::build_member_url($linkref);
+        }, $links);
+
+        $config = kolab_storage_config::get_instance();
+        $remove = array_diff($config->get_object_links($uid), $links);
+        return $config->save_object_links($uid, $links, $remove);
+    }
+
+    /**
+     * Simplify the given message URI by converting the mailbox
+     * part into a relative IMAP path valid for the current user.
+     */
+    protected function _convert_message_uri($uri)
+    {
+        if (strpos($uri, 'imap:///') === 0) {
+            $linkref = kolab_storage_config::parse_member_url($uri);
+
+            return 'imap:///' . implode('/', array_map('rawurlencode', explode('/', $linkref['folder']))) .
+                '/' . $linkref['uid'] .
+                '?' . http_build_query($linkref['params'], '', '&');
+        }
+
+        return $uri;
+    }
+
+    /**
      * Convert from Kolab_Format to internal representation
      */
     private function _to_rcube_task($record)
@@ -839,6 +892,7 @@ class tasklist_kolab_driver extends tasklist_driver
             'organizer' => $record['organizer'],
             'sequence' => $record['sequence'],
             'tags' => $record['tags'],
+            'links' => $this->get_links($record['uid']),
         );
 
         // convert from DateTime to internal date format
@@ -1013,9 +1067,10 @@ class tasklist_kolab_driver extends tasklist_driver
         if (!$list_id || !($folder = $this->get_folder($list_id)))
             return false;
 
-        // tags are stored separately
+        // email links and tags are stored separately
+        $links = $task['links'];
         $tags = $task['tags'];
-        unset($task['tags']);
+        unset($task['tags'], $task['links']);
 
         // moved from another folder
         if ($task['_fromlist'] && ($fromfolder = $this->get_folder($task['_fromlist']))) {
@@ -1049,6 +1104,8 @@ class tasklist_kolab_driver extends tasklist_driver
             $saved = false;
         }
         else {
+            // save links in configuration.relation object
+            $this->save_links($object['uid'], $links);
             // save tags in configuration.relation object
             $this->save_tags($object['uid'], $tags);
 
@@ -1170,6 +1227,17 @@ class tasklist_kolab_driver extends tasklist_driver
     }
 
     /**
+     * Build a URI representing the given message reference
+     *
+     * @see tasklist_driver::get_message_uri()
+     */
+    public function get_message_uri($headers, $folder)
+    {
+        $uri = kolab_storage_config::get_message_uri($headers, $folder);
+        return $this->_convert_message_uri($uri);
+    }
+
+    /**
      * 
      */
     public function tasklist_edit_form($action, $list, $fieldprop)
diff --git a/plugins/tasklist/drivers/tasklist_driver.php b/plugins/tasklist/drivers/tasklist_driver.php
index cdaa76b..2102d21 100644
--- a/plugins/tasklist/drivers/tasklist_driver.php
+++ b/plugins/tasklist/drivers/tasklist_driver.php
@@ -288,6 +288,20 @@ abstract class tasklist_driver
     public function get_attachment_body($id, $task) { }
 
     /**
+     * Build a URI representing the given message reference
+     *
+     * @param object rcube_message_header Instance holding the message headers
+     * @param string IMAP folder the message resides in
+     *
+     * @return string An URI referencing the given IMAP message
+     */
+    public function get_message_uri($headers, $folder)
+    {
+        // to be implemented by the derived classes
+        return false;
+    }
+
+    /**
      * Helper method to determine whether the given task is considered "complete"
      *
      * @param array  $task  Hash array with event properties:
diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc
index 7275995..4563999 100644
--- a/plugins/tasklist/localization/en_US.inc
+++ b/plugins/tasklist/localization/en_US.inc
@@ -37,6 +37,7 @@ $labels['start'] = 'Start';
 $labels['starttime'] = 'Start time';
 $labels['alarms'] = 'Reminder';
 $labels['repeat'] = 'Repeat';
+$labels['links'] = 'References';
 $labels['status'] = 'Status';
 $labels['status-needs-action'] = 'Needs action';
 $labels['status-in-process'] = 'In process';
@@ -61,6 +62,7 @@ $labels['mytasks'] = 'My tasks';
 $labels['mytaskstitle'] = 'Tasks assigned to you';
 $labels['nodate'] = 'no date';
 $labels['removetag'] = 'Remove';
+$labels['removelink'] = 'Remove email reference';
 $labels['auto'] = 'Auto';
 
 $labels['taskdetails'] = 'Details';
diff --git a/plugins/tasklist/skins/larry/sprites.png b/plugins/tasklist/skins/larry/sprites.png
index 7a9c89f..3552b00 100644
Binary files a/plugins/tasklist/skins/larry/sprites.png and b/plugins/tasklist/skins/larry/sprites.png differ
diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css
index aafc9e8..bf53e6b 100644
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -1083,6 +1083,56 @@ label.block {
 	-o-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9);
 }
 
+#task-links {
+	margin-top: 0;
+	margin-bottom: 0.2em;
+}
+
+#task-links label {
+	vertical-align: top;
+	margin-top: 0.3em;
+}
+
+#task-links .attachmentslist {
+	display: inline-block;
+}
+
+#task-links .attachmentslist li {
+	display: inline-block;
+	margin-right: 1em;
+}
+
+#taskedit-links .attachmentslist li.message.eml,
+#task-links .attachmentslist li.message.eml {
+	background-image: url(sprites.png);
+	background-position: -2px -388px;
+}
+
+#taskedit-links .attachmentslist li.message a.messagelink,
+#task-links .attachmentslist li.message a.messagelink {
+	padding: 0 0 0 24px;
+}
+
+#taskedit-links .attachmentslist li.deleted a.messagelink,
+#taskedit-links .attachmentslist li.deleted a.messagelink:hover {
+	text-decoration: line-through;
+}
+
+#taskedit-links label {
+	float: left;
+	margin-top: 0.3em;
+}
+
+#taskedit-links .task-text {
+	margin-left: 8em;
+	min-height: 22px;
+}
+
+#taskedit-links .attachmentslist li a.delete {
+	top: 0;
+	background-position: -6px -378px;
+}
+
 #task-attachments .attachmentslist li {
 	float: left;
 	margin-right: 1em;
diff --git a/plugins/tasklist/skins/larry/templates/mainview.html b/plugins/tasklist/skins/larry/templates/mainview.html
index f3fd595..4d3b9c1 100644
--- a/plugins/tasklist/skins/larry/templates/mainview.html
+++ b/plugins/tasklist/skins/larry/templates/mainview.html
@@ -211,6 +211,10 @@
 		<label><roundcube:label name="tasklist.status" /></label>
 		<span class="task-text"></span>
 	</div>
+	<div id="task-links" class="form-section">
+		<label><roundcube:label name="tasklist.links" /></label>
+		<span class="task-text"></span>
+	</div>
 	<div id="task-attachments" class="form-section">
 		<label><roundcube:label name="attachments" /></label>
 		<div class="task-text"></div>
diff --git a/plugins/tasklist/skins/larry/templates/taskedit.html b/plugins/tasklist/skins/larry/templates/taskedit.html
index e44c5d8..8155668 100644
--- a/plugins/tasklist/skins/larry/templates/taskedit.html
+++ b/plugins/tasklist/skins/larry/templates/taskedit.html
@@ -54,6 +54,11 @@
 				<label for="taskedit-tasklist"><roundcube:label name="tasklist.list" /></label>
 				<roundcube:object name="plugin.tasklist_select" id="taskedit-tasklist" />
 			</div>
+			<div class="form-section" id="taskedit-links">
+				<label><roundcube:label name="tasklist.links" /></label>
+				<div class="task-text"></div>
+				<br style="clear:left">
+			</div>
 		</div>
 		<!-- recurrence settings -->
 		<div id="taskedit-panel-recurrence">
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 8a92887..3cadd57 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -558,6 +558,12 @@ function rcube_tasklist_ui(settings)
             }
         });
 
+        // register click handler for message links
+        $('#task-links, #taskedit-links').on('click', 'li a.messagelink', function(e) {
+            rcmail.open_window(this.href);
+            return false;
+        });
+
         // handle global document clicks: close popup menus
         $(document.body).click(clear_popups);
 
@@ -1778,6 +1784,13 @@ 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'));
+            $('#task-links').show();
+        }
+
         // list task attendees
         if (list.attendees && rec.attendees) {
 /*
@@ -1984,6 +1997,14 @@ function rcube_tasklist_ui(settings)
         // set alarm(s)
         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);
+            $('#taskedit-links').show();
+        }
+        else {
+            $('#taskedit-links').hide();
+        }
+
         // set recurrence
         me.set_recurrence_edit(rec);
 
@@ -2271,6 +2292,53 @@ function rcube_tasklist_ui(settings)
     /**
      *
      */
+    function task_show_links(links, container, edit)
+    {
+        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);
+    }
+
+    /**
+     *
+     */
+    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');
+    }
+
+    /**
+     *
+     */
     function add_childtask(id)
     {
         if (rcmail.busy)
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index 5824527..9acded6 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -564,6 +564,11 @@ class tasklist extends rcube_plugin
 
         $rec['attachments'] = $attachments;
 
+        // convert link references into simple URIs
+        if (array_key_exists('links', $rec)) {
+            $rec['links'] = array_map(function($link) { return is_array($link) ? $link['uri'] : strval($link); }, (array)$rec['links']);
+        }
+
         // convert invalid data
         if (isset($rec['attendees']) && !is_array($rec['attendees']))
             $rec['attendees'] = array();
@@ -1045,6 +1050,15 @@ class tasklist extends rcube_plugin
             $rec['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']);
         }
 
+        // convert link URIs references into structs
+        if (array_key_exists('links', $rec)) {
+            foreach ((array)$rec['links'] as $i => $link) {
+                if (strpos($link, 'imap://') === 0) {
+                    $rec['links'][$i] = $this->get_message_reference($link);
+                }
+            }
+        }
+
         if (!is_array($rec['tags']))
             $rec['tags'] = (array)$rec['tags'];
         sort($rec['tags'], SORT_LOCALE_STRING);
@@ -1163,7 +1177,7 @@ class tasklist extends rcube_plugin
         $this->rc->output->set_env('autocomplete_threads', (int)$this->rc->config->get('autocomplete_threads', 0));
         $this->rc->output->set_env('autocomplete_max', (int)$this->rc->config->get('autocomplete_max', 15));
         $this->rc->output->set_env('autocomplete_min_length', $this->rc->config->get('autocomplete_min_length'));
-        $this->rc->output->add_label('autocompletechars', 'autocompletemore', 'libcalendaring.expandattendeegroup', 'libcalendaring.expandattendeegroupnodata');
+        $this->rc->output->add_label('autocompletechars', 'autocompletemore', 'delete', 'libcalendaring.expandattendeegroup', 'libcalendaring.expandattendeegroupnodata');
 
         $this->rc->output->set_pagetitle($this->gettext('navtitle'));
         $this->rc->output->send('tasklist.mainview');
@@ -1335,8 +1349,12 @@ class tasklist extends rcube_plugin
 
             $this->load_driver();
 
+            // add a reference to the email message
+            if ($msguri = $this->driver->get_message_uri($message->headers, $mbox)) {
+                $task['links'] = array($this->get_message_reference($msguri));
+            }
             // copy mail attachments to task
-            if ($message->attachments && $this->driver->attachments) {
+            else if ($message->attachments && $this->driver->attachments) {
                 if (!is_array($_SESSION[self::SESSION_KEY]) || $_SESSION[self::SESSION_KEY]['id'] != $task['id']) {
                     $_SESSION[self::SESSION_KEY] = array();
                     $_SESSION[self::SESSION_KEY]['id'] = $task['id'];
@@ -1489,6 +1507,45 @@ class tasklist extends rcube_plugin
     }
 
     /**
+     * Resolve the email message reference from the given URI
+     */
+    public function get_message_reference($uri, $resolve = false)
+    {
+        if (strpos($uri, 'imap:///') === 0) {
+            $url = parse_url(substr($uri, 8));
+            parse_str($url['query'], $params);
+
+            $path = explode('/', $url['path']);
+            $uid  = array_pop($path);
+            $folder = join('/', array_map('rawurldecode', $path));
+        }
+
+        if ($folder && $uid) {
+            // TODO: check if folder/uid still references an existing message
+            // TODO: validate message or resovle the new URI using the message-id parameter
+
+            $linkref = array(
+                'folder'  => $folder,
+                'uid'     => $uid,
+                'subject' => $params['subject'],
+                'uri'     => $uri,
+                'mailurl' => $this->rc->url(array(
+                    'task'   => 'mail',
+                    'action' => 'show',
+                    'mbox'   => $folder,
+                    'uid'    => $uid,
+                    'rel'    => 'task',
+                ))
+            );
+        }
+        else {
+            $linkref = array();
+        }
+
+        return $linkref;
+    }
+
+    /**
      * Import the full payload from a mail message attachment
      */
     public function mail_import_attachment()


commit ef7f457c025545df77996f948398d52544b11b26
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Mon Oct 13 14:55:50 2014 +0200

    Bugfixes for recent refactoring

diff --git a/plugins/libkolab/lib/kolab_storage_config.php b/plugins/libkolab/lib/kolab_storage_config.php
index 22fb90c..915e25c 100644
--- a/plugins/libkolab/lib/kolab_storage_config.php
+++ b/plugins/libkolab/lib/kolab_storage_config.php
@@ -722,7 +722,7 @@ class kolab_storage_config
         }
 
         // create a new relation
-        if (!$done) {
+        if (!$done && !empty($links)) {
             $relation = array(
                 'members'  => array_merge($links, array($object_uri)),
                 'category' => 'generic',
@@ -775,7 +775,7 @@ class kolab_storage_config
             // derive message identifier from URI
             $member_id = md5($uri);
         }
-        array('member', '=', $member_id);
+        $filter[] = array('member', '=', $member_id);
 
         // get UIDs of assigned notes
         foreach ($this->get_objects($filter, $default) as $relation) {


commit 6f0ef60f7a15162bdb7052c73bbb2fd515ae773c
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Mon Oct 13 14:48:59 2014 +0200

    Add fall-back to the full uri for searching relations in cache

diff --git a/plugins/libkolab/lib/kolab_format_configuration.php b/plugins/libkolab/lib/kolab_format_configuration.php
index 17b46a7..4506ed3 100644
--- a/plugins/libkolab/lib/kolab_format_configuration.php
+++ b/plugins/libkolab/lib/kolab_format_configuration.php
@@ -241,6 +241,10 @@ class kolab_format_configuration extends kolab_format
             else if (!empty($member['params']['message-id'])) {
                 $words[] = $member['params']['message-id'];
             }
+            else {
+                // derive message identifier from URI
+                $words[] = md5($url);
+            }
         }
 
         return $words;
diff --git a/plugins/libkolab/lib/kolab_storage_config.php b/plugins/libkolab/lib/kolab_storage_config.php
index e04ccd6..22fb90c 100644
--- a/plugins/libkolab/lib/kolab_storage_config.php
+++ b/plugins/libkolab/lib/kolab_storage_config.php
@@ -351,6 +351,8 @@ class kolab_storage_config
                 'params' => $params,
             );
         }
+
+        return false;
     }
 
     /**
@@ -765,10 +767,16 @@ class kolab_storage_config
         $filter  = array(
             array('type', '=', 'relation'),
             array('category', '=', 'generic'),
-            // @TODO: what if Message-Id (and Date) does not exist?
-            array('member', '=', $message->get('message-id', false)),
         );
 
+        // query by message-id
+        $member_id = $message->get('message-id', false);
+        if (empty($member_id)) {
+            // derive message identifier from URI
+            $member_id = md5($uri);
+        }
+        array('member', '=', $member_id);
+
         // get UIDs of assigned notes
         foreach ($this->get_objects($filter, $default) as $relation) {
             // we don't need to update members if the URI is found


commit 63b69871fee37777f03e17a916501f1e13f3f028
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Mon Oct 13 13:55:45 2014 +0200

    Fix typo

diff --git a/plugins/libkolab/lib/kolab_storage_config.php b/plugins/libkolab/lib/kolab_storage_config.php
index 35d0d62..e04ccd6 100644
--- a/plugins/libkolab/lib/kolab_storage_config.php
+++ b/plugins/libkolab/lib/kolab_storage_config.php
@@ -699,7 +699,7 @@ class kolab_storage_config
 
             // make sure the object_uri is still a member
             if (!in_array($object_uri, $members)) {
-                $members[$obejct_uri];
+                $members[$object_uri];
             }
 
             // remove relation if no other members remain


commit a807768c329badbbc3559959acf7a38ca430cab0
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Mon Oct 13 12:38:57 2014 +0200

    Move email link/relation handling functions to libkolab for common use

diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php
index 259f26d..369f1c5 100644
--- a/plugins/kolab_notes/kolab_notes.php
+++ b/plugins/kolab_notes/kolab_notes.php
@@ -998,46 +998,9 @@ class kolab_notes extends rcube_plugin
 
     private function save_links($uid, $links)
     {
-        $config    = kolab_storage_config::get_instance();
-        $search    = kolab_storage_config::build_member_url($uid);
-        $relations = $this->get_relations($uid);
-
-        // @TODO: here we support only one-way relations, i.e.
-        // such relation can contain only note and mail members
-        // So, when we remove a note member the whole relation
-        // will be removed
-
-        foreach ($relations as $relation) {
-            if (empty($links)) {
-                $config->delete($relation['uid']);
-            }
-            else {
-                // make relation members up-to-date
-                kolab_storage_config::resolve_members($relation);
-
-                // assign all links to one relation, others will be removed
-                $members = array_merge($links, array($search));
-                $diff1   = array_diff($members, $relation['members']);
-                $diff1   = array_diff($relation['members'], $members);
-
-                if (count($diff1) || count($diff2)) {
-                    $relation['members'] = $members;
-                    $config->save($relation, 'relation');
-                }
-
-                $links = null;
-            }
-        }
-
-        // create a new relation
-        if (!empty($links)) {
-            $relation = array(
-                'members'  => array_merge($links, array($search)),
-                'category' => 'generic',
-            );
-
-            $config->save($relation, 'relation');
-        }
+        $config = kolab_storage_config::get_instance();
+        $remove = array_diff($config->get_object_links($uid), $links);
+        return $config->save_object_links($uid, $links, $remove);
     }
 
     /**
@@ -1045,23 +1008,8 @@ class kolab_notes extends rcube_plugin
      */
     private function get_links($uid)
     {
-        $result = array();
-        $search = kolab_storage_config::build_member_url($uid);
-
-        foreach ($this->get_relations($uid) as $relation) {
-            if (in_array($search, (array) $relation['members'])) {
-                // make relation members up-to-date
-                kolab_storage_config::resolve_members($relation);
-
-                foreach ($relation['members'] as $member) {
-                    if ($member != $search) {
-                        $result[] = $member;
-                    }
-                }
-            }
-        }
-
-        return array_unique($result);
+        $config = kolab_storage_config::get_instance();
+        return $config->get_object_links($uid);
     }
 
     /**
@@ -1092,22 +1040,6 @@ class kolab_notes extends rcube_plugin
     }
 
     /**
-     * Find relation objects referring to specified note
-     */
-    private function get_relations($uid)
-    {
-        $config      = kolab_storage_config::get_instance();
-        $default     = true;
-        $filter      = array(
-            array('type', '=', 'relation'),
-            array('category', '=', 'generic'),
-            array('member', '=', $uid),
-        );
-
-        return $config->get_objects($filter, $default, 100);
-    }
-
-    /**
      * Resolve the email message reference from the given URI
      */
     public function get_message_reference($uri, $resolve = false)
diff --git a/plugins/libkolab/lib/kolab_storage_config.php b/plugins/libkolab/lib/kolab_storage_config.php
index 8a0fab5..35d0d62 100644
--- a/plugins/libkolab/lib/kolab_storage_config.php
+++ b/plugins/libkolab/lib/kolab_storage_config.php
@@ -654,6 +654,100 @@ class kolab_storage_config
     }
 
     /**
+     * Find objects linked with the given groupware object through a relation
+     *
+     * @param string Object UUID
+     * @param array List of related URIs
+     */
+    public function get_object_links($uid)
+    {
+        $links = array();
+        $object_uri = self::build_member_url($uid);
+
+        foreach ($this->get_relations_for_member($uid) as $relation) {
+            if (in_array($object_uri, (array) $relation['members'])) {
+                // make relation members up-to-date
+                kolab_storage_config::resolve_members($relation);
+
+                foreach ($relation['members'] as $member) {
+                    if ($member != $object_uri) {
+                        $links[] = $member;
+                    }
+                }
+            }
+        }
+
+        return array_unique($links);
+    }
+
+    /**
+     *
+     */
+    public function save_object_links($uid, $links, $remove = array())
+    {
+        $object_uri = self::build_member_url($uid);
+        $relations = $this->get_relations_for_member($uid);
+        $done = false;
+
+        foreach ($relations as $relation) {
+            // make relation members up-to-date
+            kolab_storage_config::resolve_members($relation);
+
+            // remove and add links
+            $members = array_diff($relation['members'], (array)$remove);
+            $members = array_unique(array_merge($members, $links));
+
+            // make sure the object_uri is still a member
+            if (!in_array($object_uri, $members)) {
+                $members[$obejct_uri];
+            }
+
+            // remove relation if no other members remain
+            if (count($members) <= 1) {
+                $done = $this->delete($relation['uid']);
+            }
+            // update relation object if members changed
+            else if (count(array_diff($members, $relation['members'])) || count(array_diff($relation['members'], $members))) {
+                $relation['members'] = $members;
+                $done = $this->save($relation, 'relation');
+                $links = array();
+            }
+            // no changes, we're happy
+            else {
+                $done = true;
+                $links = array();
+            }
+        }
+
+        // create a new relation
+        if (!$done) {
+            $relation = array(
+                'members'  => array_merge($links, array($object_uri)),
+                'category' => 'generic',
+            );
+
+            $ret = $this->save($relation, 'relation');
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Find relation objects referring to specified note
+     */
+    public function get_relations_for_member($uid, $reltype = 'generic')
+    {
+        $default = true;
+        $filter  = array(
+            array('type', '=', 'relation'),
+            array('category', '=', $reltype),
+            array('member', '=', $uid),
+        );
+
+        return $this->get_objects($filter, $default, 100);
+    }
+
+    /**
      * Find kolab objects assigned to specified e-mail message
      *
      * @param rcube_message $message E-mail message




More information about the commits mailing list