Refactored links to email messages using libkolabxml relation objects (#3091)
This commit is contained in:
parent
b0eeafcc3e
commit
79ebe9e227
4 changed files with 315 additions and 179 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
return $folder->delete($note['uid'], $force);
|
||||
$status = $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,41 +966,161 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find messages assigned to specified note
|
||||
*/
|
||||
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'])) {
|
||||
foreach ($relation['members'] as $member) {
|
||||
if ($member != $search) {
|
||||
$result[] = $member;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find notes assigned to specified message
|
||||
*/
|
||||
private function get_message_notes($message, $folder)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find relation objects referring to specified note
|
||||
*/
|
||||
private function get_relations($uid = null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return sprintf('imap://%s/%s?message-id=%s&subject=%s',
|
||||
$headers->folder ?: $folder,
|
||||
$headers->uid,
|
||||
urlencode($headers->messageID),
|
||||
urlencode($headers->get('subject'))
|
||||
$params = array(
|
||||
'folder' => $headers->folder ?: $folder,
|
||||
'uid' => $headers->uid,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract message reference components from the given URI
|
||||
*/
|
||||
private function parse_message_uri($uri)
|
||||
{
|
||||
$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'],
|
||||
);
|
||||
if (($messageid = $headers->get('message-id', false)) && ($date = $headers->get('date', false))) {
|
||||
$params['message-id'] = $messageid;
|
||||
$params['date'] = $date;
|
||||
|
||||
$path = explode('/', $url['host'] . $url['path']);
|
||||
$linkref['msguid'] = array_pop($path);
|
||||
$linkref['folder'] = join('/', $path);
|
||||
|
||||
return $linkref;
|
||||
if ($subject = $headers->get('subject')) {
|
||||
$params['subject'] = $subject;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return kolab_storage_config::build_member_url($params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1006,34 +1128,18 @@ class kolab_notes extends rcube_plugin
|
|||
*/
|
||||
public function get_message_reference($uri, $resolve = false)
|
||||
{
|
||||
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 ($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;
|
||||
|
|
|
@ -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);
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue