plugins/kolab_notes plugins/kolab_tags plugins/libkolab

Aleksander Machniak machniak at kolabsys.com
Fri Aug 1 10:04:39 CEST 2014


 plugins/kolab_notes/kolab_notes.php           |  250 ++++++++++++++++++--------
 plugins/kolab_notes/notes.js                  |    4 
 plugins/kolab_tags/lib/kolab_tags_engine.php  |  105 ----------
 plugins/libkolab/lib/kolab_storage_config.php |  131 +++++++++++++
 4 files changed, 313 insertions(+), 177 deletions(-)

New commits:
commit 79ebe9e2277af9ad29541994bd83dfc9fd7bc2be
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Aug 1 03:58:09 2014 -0400

    Refactored links to email messages using libkolabxml relation objects (#3091)

diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php
index 4c816a2..6dbfa17 100644
--- a/plugins/kolab_notes/kolab_notes.php
+++ b/plugins/kolab_notes/kolab_notes.php
@@ -488,7 +488,7 @@ class kolab_notes extends rcube_plugin
     public function note_record()
     {
         $data = $this->get_note(array(
-            'uid' => rcube_utils::get_input_value('_id', RCUBE_INPUT_GPC),
+            'uid'  => rcube_utils::get_input_value('_id', RCUBE_INPUT_GPC),
             'list' => rcube_utils::get_input_value('_list', RCUBE_INPUT_GPC),
         ));
 
@@ -562,12 +562,10 @@ class kolab_notes extends rcube_plugin
         }
 
         // resolve message links
-        if (is_array($note['links'])) {
-            $me = $this;
-            $note['links'] = array_map(function($link) use ($me, $resolve){
+        $me = $this;
+        $note['links'] = array_map(function($link) use ($me, $resolve) {
                 return $me->get_message_reference($link, $resolve) ?: array('uri' => $link);
-            }, $note['links']);
-        }
+            }, $this->get_links($note['uid']));
 
         return $note;
     }
@@ -622,7 +620,7 @@ class kolab_notes extends rcube_plugin
         else {
             $this->rc->output->show_message('errorsaving', 'error');
         }
- 
+
         // unlock client
         $this->rc->output->command('plugin.unlock_saving');
 
@@ -666,6 +664,11 @@ class kolab_notes extends rcube_plugin
 
         // generate new note object from input
         $object = $this->_write_preprocess($note, $old);
+
+        // email links are handled separately
+        $links = $object['links'];
+        unset($object['links']);
+
         $saved = $folder->save($object, 'note', $note['uid']);
 
         if (!$saved) {
@@ -677,7 +680,10 @@ class kolab_notes extends rcube_plugin
             $saved = false;
         }
         else {
-            $note = $object;
+            // save links in configuration.relation object
+            $this->save_links($object['uid'], $links);
+
+            $note         = $object;
             $note['list'] = $list_id;
 
             // cache this in memory for later read
@@ -716,10 +722,17 @@ class kolab_notes extends rcube_plugin
         $this->_read_lists();
 
         $list_id = $note['list'];
-        if (!$list_id || !($folder = $this->get_folder($list_id)))
+        if (!$list_id || !($folder = $this->get_folder($list_id))) {
             return false;
+        }
+
+        $status = $folder->delete($note['uid'], $force);
 
-        return $folder->delete($note['uid'], $force);
+        if ($status) {
+            $this->save_links($note['uid'], null);
+        }
+
+        return $status;
     }
 
     /**
@@ -873,23 +886,12 @@ class kolab_notes extends rcube_plugin
     }
 
     /**
-     * Lookup backend storage and find notes tagged with the given message-ID
+     * Lookup backend storage and find notes associated with the given message
      */
     public function mail_message_load($p)
     {
-        $this->message = $p['object'];
-        $this->message_notes = array();
-
-        // TODO: only query for notes if message was flagged with $KolabNotes ?
-
-        $message_id = trim($p['object']->headers->messageID, '<> ');
-        if ($message_id && $p['object']->uid) {
-            $query = array(array('tags','=','ref:' . $message_id));
-            foreach (kolab_storage::select($query, 'note') as $record) {
-                $record['list'] = kolab_storage::folder_id($record['_mailbox']);
-                $this->message_notes[] = $record;
-            }
-        }
+        $this->message       = $p['object'];
+        $this->message_notes = $this->get_message_notes($this->message->headers, $this->message->folder);
     }
 
     /**
@@ -964,76 +966,180 @@ class kolab_notes extends rcube_plugin
         return $message->getMessage();
     }
 
+    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']);
+                $this->relations = null; // clear in-memory cache
+            }
+            else {
+                // 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');
+                    $this->relations = null; // clear in-memory cache
+                }
+
+                $links = null;
+            }
+        }
+
+        // create a new relation
+        if (!empty($links)) {
+            $relation = array(
+                'members'  => array_merge($links, array($search)),
+                'category' => 'generic',
+            );
+
+            $config->save($relation, 'relation');
+            $this->relations = null; // clear in-memory cache
+        }
+    }
+
     /**
-     * Build a URI representing the given message reference
+     * Find messages assigned to specified note
      */
-    private function get_message_uri($headers, $folder)
+    private function get_links($uid)
     {
-        return sprintf('imap://%s/%s?message-id=%s&subject=%s',
-            $headers->folder ?: $folder,
-            $headers->uid,
-            urlencode($headers->messageID),
-            urlencode($headers->get('subject'))
-        );
+        $result = array();
+        $search = kolab_storage_config::build_member_url($uid);
+
+        foreach ($this->get_relations($uid) as $relation) {
+            if (in_array($search, (array) $relation['members'])) {
+                foreach ($relation['members'] as $member) {
+                    if ($member != $search) {
+                        $result[] = $member;
+                    }
+                }
+            }
+        }
+
+        return array_unique($result);
     }
 
     /**
-     * Extract message reference components from the given URI
+     * Find notes assigned to specified message
      */
-    private function parse_message_uri($uri)
+    private function get_message_notes($message, $folder)
     {
-        $url = parse_url($uri);
-        if ($url['scheme'] == 'imap') {
-            parse_str($url['query'], $param);
-            $linkref = array(
-                'uri' => $uri,
-                'message_id' => $param['message-id'] ?: urldecode($url['fragment']),
-                'subject' => $param['subject'],
-            );
+        $result = array();
+        $uids   = array();
+
+        // TODO: only query for notes if message was flagged with $KolabNotes ?
+
+        // get UIDs of assigned notes
+        foreach ($this->get_relations() as $relation) {
+            foreach ($relation['members'] as $member) {
+                $member = kolab_storage_config::parse_member_url($member);
+                if ($member['folder'] == $folder && $member['uid'] == $message->uid) {
+                    reset($relation['members']);
+                    // find note UID(s)
+                    foreach ($relation['members'] as $member) {
+                        if (strpos($member, 'urn:uuid:') === 0) {
+                            $uids[] = substr($member, 9);
+                        }
+                    }
 
-            $path = explode('/', $url['host'] . $url['path']);
-            $linkref['msguid'] = array_pop($path);
-            $linkref['folder'] = join('/', $path);
+                    continue 2;
+                }
+            }
+        }
 
-            return $linkref;
+        // get Note objects
+        if (!empty($uids)) {
+            $query = array(array('uid', '=', $uids));
+            foreach (kolab_storage::select($query, 'note') as $record) {
+                $record['list'] = kolab_storage::folder_id($record['_mailbox']);
+                $result[] = $record;
+            }
         }
 
-        return false;
+        return $result;
     }
 
     /**
-     * Resolve the email message reference from the given URI
+     * Find relation objects referring to specified note
      */
-    public function get_message_reference($uri, $resolve = false)
+    private function get_relations($uid = null)
     {
-        if ($linkref = $this->parse_message_uri($uri)) {
-            // fetch message subject
-            if ($resolve || empty($linkref['subject'])) {
-                $imap = $this->rc->get_storage();
-                if (!($message = $imap->get_message_headers($linrkef['msguid'], $linkref['folder']))) {
-                    // try to find message using the message_id fragment
-                    $index = $imap->search_once($imap->list_folders_subscribed('', '*', 'mail', null, true), 'HEADER MESSAGE-ID ' . $linkref['message_id']);
-                    if ($index->count()) {
-                        $uid = reset($index->get());
-                        $folder = $index->get_parameters('MAILBOX');
-                        if ($message = $imap->get_message_headers($uid, $folder)) {
-                            // replace metadata
-                            $linkref['subject'] = $message->get('subject');
-                            $linkref['msguid'] = $message->uid;
-                            $linkref['folder'] = $message->folder;
-                            $linkref['uri'] = $this->get_message_uri($message, $folder);
-                        }
-                    }
-                }
+        if (!isset($this->relations)) {
+            $config      = kolab_storage_config::get_instance();
+            $filter      = array(array('type', '=', 'relation'));
+            $default     = true;
+            $data_filter = array('category' => 'generic');
+
+            $this->relations = $config->get_objects($filter, $default, $data_filter);
+        }
+
+        if ($uid === null) {
+            return $this->relations;
+        }
+
+        $result = array();
+        $search = kolab_storage_config::build_member_url($uid);
+
+        foreach ($this->relations as $relation) {
+            if (in_array($search, (array) $relation['members'])) {
+                $result[] = $relation;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Build a URI representing the given message reference
+     */
+    private function get_message_uri($headers, $folder)
+    {
+        $params = array(
+            'folder' => $headers->folder ?: $folder,
+            'uid'    => $headers->uid,
+        );
+
+        if (($messageid = $headers->get('message-id', false)) && ($date = $headers->get('date', false))) {
+            $params['message-id'] = $messageid;
+            $params['date']       = $date;
+
+            if ($subject = $headers->get('subject')) {
+                $params['subject'] = $subject;
             }
+        }
 
+        return kolab_storage_config::build_member_url($params);
+    }
+
+    /**
+     * Resolve the email message reference from the given URI
+     */
+    public function get_message_reference($uri, $resolve = false)
+    {
+        if ($linkref = kolab_storage_config::parse_member_url($uri)) {
+            $linkref['subject'] = $linkref['params']['subject'];
+            $linkref['uri']     = $uri;
             $linkref['mailurl'] = $this->rc->url(array(
-                'task' => 'mail',
+                'task'   => 'mail',
                 'action' => 'show',
-                'mbox' => $linkref['folder'],
-                'uid' => $linkref['msguid'],
-                'rel' => 'note',
+                'mbox'   => $linkref['folder'],
+                'uid'    => $linkref['uid'],
+                'rel'    => 'note',
             ));
+
+            unset($linkref['params']);
         }
 
         return $linkref;
diff --git a/plugins/kolab_notes/notes.js b/plugins/kolab_notes/notes.js
index 8f59c19..7edded5 100644
--- a/plugins/kolab_notes/notes.js
+++ b/plugins/kolab_notes/notes.js
@@ -811,13 +811,13 @@ function rcube_kolab_notes_ui(settings)
         });
 
         if (data.links) {
-            $.each(data.links, function(i, link){
+            $.each(data.links, function(i, link) {
                 var li = $('<li>').addClass('link')
                     .addClass('message eml')
                     .append($('<a>')
                         .attr('href', link.mailurl)
                         .addClass('messagelink')
-                        .html(Q(link.subject || link.message_id || link.uri))
+                        .text(link.subject || link.uri)
                     )
                     .appendTo(attachmentslist);
 /*
diff --git a/plugins/kolab_tags/lib/kolab_tags_engine.php b/plugins/kolab_tags/lib/kolab_tags_engine.php
index 684041b..1551726 100644
--- a/plugins/kolab_tags/lib/kolab_tags_engine.php
+++ b/plugins/kolab_tags/lib/kolab_tags_engine.php
@@ -552,57 +552,7 @@ class kolab_tags_engine
      */
     protected function parse_member_url($url)
     {
-        // Look for IMAP URI:
-        // imap:///(user/username@domain|shared)/<folder>/<UID>?<search_params>
-        if (strpos($url, 'imap:///') === 0) {
-            $rcube   = rcube::get_instance();
-            $storage = $rcube->get_storage();
-
-            // parse_url does not work with imap:/// prefix
-            $url   = parse_url(substr($url, 8));
-            $path  = explode('/', $url['path']);
-            parse_str($url['query'], $params);
-
-            $uid  = array_pop($path);
-            $ns   = array_shift($path);
-            $path = array_map('rawurldecode', $path);
-
-            // resolve folder name
-            if ($ns == 'shared') {
-                $folder = implode('/', $path);
-                // Note: this assumes there's only one shared namespace root
-                if ($ns = $storage->get_namespace('shared')) {
-                    if ($prefix = $ns[0][0]) {
-                        $folder = $prefix . '/' . $folder;
-                    }
-                }
-            }
-            else if ($ns == 'user') {
-                $username = array_shift($path);
-                $folder   = implode('/', $path);
-
-                if ($username != $rcube->get_user_name()) {
-                    // Note: this assumes there's only one other users namespace root
-                    if ($ns = $storage->get_namespace('other')) {
-                        if ($prefix = $ns[0][0]) {
-                            $folder = $prefix . '/' . $username . '/' . $folder;
-                        }
-                    }
-                }
-                else if (!strlen($folder)) {
-                    $folder = 'INBOX';
-                }
-            }
-            else {
-                return;
-            }
-
-            return array(
-                'folder' => $folder,
-                'uid'    => $uid,
-                'params' => $params,
-            );
-        }
+        return kolab_storage_config::parse_member_url($url);
     }
 
     /**
@@ -614,57 +564,6 @@ class kolab_tags_engine
      */
     protected function build_member_url($params)
     {
-        if (empty($params) || !strlen($params['folder'])) {
-            return null;
-        }
-
-        $rcube   = rcube::get_instance();
-        $storage = $rcube->get_storage();
-
-        // modify folder spec. according to namespace
-        $folder = $params['folder'];
-        $ns     = $storage->folder_namespace($folder);
-
-        if ($ns == 'shared') {
-            // Note: this assumes there's only one shared namespace root
-            if ($ns = $storage->get_namespace('shared')) {
-                if ($prefix = $ns[0][0]) {
-                    $folder = 'shared' . substr($folder, strlen($prefix));
-                }
-            }
-        }
-        else {
-            if ($ns == 'other') {
-                // Note: this assumes there's only one other users namespace root
-                if ($ns = $storage->get_namespace('shared')) {
-                    if ($prefix = $ns[0][0]) {
-                        $folder = 'user' . substr($folder, strlen($prefix));
-                    }
-                }
-            }
-            else {
-                $folder = 'user' . '/' . $rcube->get_user_name() . '/' . $folder;
-            }
-        }
-
-        $folder = implode('/', array_map('rawurlencode', explode('/', $folder)));
-
-        // build URI
-        $url = 'imap:///' . $folder;
-
-        // UID is optional here because sometimes we want
-        // to build just a member uri prefix
-        if ($params['uid']) {
-            $url .= '/' . $params['uid'];
-        }
-
-        unset($params['folder']);
-        unset($params['uid']);
-
-        if (!empty($params)) {
-            $url .= '?' . http_build_query($params, '', '&');
-        }
-
-        return $url;
+        return kolab_storage_config::build_member_url($params);
     }
 }
diff --git a/plugins/libkolab/lib/kolab_storage_config.php b/plugins/libkolab/lib/kolab_storage_config.php
index 9dd1116..d253aba 100644
--- a/plugins/libkolab/lib/kolab_storage_config.php
+++ b/plugins/libkolab/lib/kolab_storage_config.php
@@ -183,4 +183,135 @@ class kolab_storage_config
 
         return $folder;
     }
+
+    /**
+     * Builds relation member URI
+     *
+     * @param string|array Object UUID or Message folder, UID, Search headers (Message-Id, Date)
+     *
+     * @return string $url Member URI
+     */
+    public static function build_member_url($params)
+    {
+        // param is object UUID
+        if (is_string($params) && !empty($params)) {
+            return 'urn:uuid:' . $params;
+        }
+
+        if (empty($params) || !strlen($params['folder'])) {
+            return null;
+        }
+
+        $rcube   = rcube::get_instance();
+        $storage = $rcube->get_storage();
+
+        // modify folder spec. according to namespace
+        $folder = $params['folder'];
+        $ns     = $storage->folder_namespace($folder);
+
+        if ($ns == 'shared') {
+            // Note: this assumes there's only one shared namespace root
+            if ($ns = $storage->get_namespace('shared')) {
+                if ($prefix = $ns[0][0]) {
+                    $folder = 'shared' . substr($folder, strlen($prefix));
+                }
+            }
+        }
+        else {
+            if ($ns == 'other') {
+                // Note: this assumes there's only one other users namespace root
+                if ($ns = $storage->get_namespace('shared')) {
+                    if ($prefix = $ns[0][0]) {
+                        $folder = 'user' . substr($folder, strlen($prefix));
+                    }
+                }
+            }
+            else {
+                $folder = 'user' . '/' . $rcube->get_user_name() . '/' . $folder;
+            }
+        }
+
+        $folder = implode('/', array_map('rawurlencode', explode('/', $folder)));
+
+        // build URI
+        $url = 'imap:///' . $folder;
+
+        // UID is optional here because sometimes we want
+        // to build just a member uri prefix
+        if ($params['uid']) {
+            $url .= '/' . $params['uid'];
+        }
+
+        unset($params['folder']);
+        unset($params['uid']);
+
+        if (!empty($params)) {
+            $url .= '?' . http_build_query($params, '', '&');
+        }
+
+        return $url;
+    }
+
+    /**
+     * Parses relation member string
+     *
+     * @param string $url Member URI
+     *
+     * @return array Message folder, UID, Search headers (Message-Id, Date)
+     */
+    public static function parse_member_url($url)
+    {
+        // Look for IMAP URI:
+        // imap:///(user/username@domain|shared)/<folder>/<UID>?<search_params>
+        if (strpos($url, 'imap:///') === 0) {
+            $rcube   = rcube::get_instance();
+            $storage = $rcube->get_storage();
+
+            // parse_url does not work with imap:/// prefix
+            $url   = parse_url(substr($url, 8));
+            $path  = explode('/', $url['path']);
+            parse_str($url['query'], $params);
+
+            $uid  = array_pop($path);
+            $ns   = array_shift($path);
+            $path = array_map('rawurldecode', $path);
+
+            // resolve folder name
+            if ($ns == 'shared') {
+                $folder = implode('/', $path);
+                // Note: this assumes there's only one shared namespace root
+                if ($ns = $storage->get_namespace('shared')) {
+                    if ($prefix = $ns[0][0]) {
+                        $folder = $prefix . '/' . $folder;
+                    }
+                }
+            }
+            else if ($ns == 'user') {
+                $username = array_shift($path);
+                $folder   = implode('/', $path);
+
+                if ($username != $rcube->get_user_name()) {
+                    // Note: this assumes there's only one other users namespace root
+                    if ($ns = $storage->get_namespace('other')) {
+                        if ($prefix = $ns[0][0]) {
+                            $folder = $prefix . '/' . $username . '/' . $folder;
+                        }
+                    }
+                }
+                else if (!strlen($folder)) {
+                    $folder = 'INBOX';
+                }
+            }
+            else {
+                return;
+            }
+
+            return array(
+                'folder' => $folder,
+                'uid'    => $uid,
+                'params' => $params,
+            );
+        }
+    }
+
 }




More information about the commits mailing list