From 26f71de1dbc146e71f692ec8ad09642b50f3a08d Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 18 Aug 2014 19:58:44 -0400 Subject: [PATCH] Store note tags in relation objects (#3395) --- plugins/kolab_notes/kolab_notes.php | 77 +++++++--- plugins/kolab_notes/notes.js | 40 +++--- plugins/libkolab/lib/kolab_storage_config.php | 131 ++++++++++++++++++ 3 files changed, 208 insertions(+), 40 deletions(-) diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php index 8475b7d0..ad40a860 100644 --- a/plugins/kolab_notes/kolab_notes.php +++ b/plugins/kolab_notes/kolab_notes.php @@ -406,10 +406,16 @@ class kolab_notes extends rcube_plugin public function notes_fetch() { $search = rcube_utils::get_input_value('_q', RCUBE_INPUT_GPC, true); - $list = rcube_utils::get_input_value('_list', RCUBE_INPUT_GPC); + $list = rcube_utils::get_input_value('_list', RCUBE_INPUT_GPC); $data = $this->notes_data($this->list_notes($list, $search), $tags); - $this->rc->output->command('plugin.data_ready', array('list' => $list, 'search' => $search, 'data' => $data, 'tags' => array_values($tags))); + + $this->rc->output->command('plugin.data_ready', array( + 'list' => $list, + 'search' => $search, + 'data' => $data, + 'tags' => array_values($tags) + )); } /** @@ -417,18 +423,14 @@ class kolab_notes extends rcube_plugin */ protected function notes_data($records, &$tags) { - $tags = array(); + $config = kolab_storage_config::get_instance(); + $tags = $config->apply_tags($records); foreach ($records as $i => $rec) { unset($records[$i]['description']); $this->_client_encode($records[$i]); - - foreach ((array)$rec['categories'] as $tag) { - $tags[] = $tag; - } } - $tags = array_unique($tags); return $records; } @@ -460,8 +462,7 @@ class kolab_notes extends rcube_plugin $matches = 0; $contents = mb_strtolower( $record['title'] . - ($this->is_html($record) ? strip_tags($record['description']) : $record['description']) . - join(' ', (array)$record['categories']) + ($this->is_html($record) ? strip_tags($record['description']) : $record['description']) ); foreach ($words as $word) { if (mb_strpos($contents, $word) !== false) { @@ -519,10 +520,12 @@ class kolab_notes extends rcube_plugin return $this->cache[$key]; } + $result = false; + $this->_read_lists(); if ($list_id) { if ($folder = $this->get_folder($list_id)) { - return $folder->get_object($uid); + $result = $folder->get_object($uid); } } // iterate over all calendar folders and search for the event ID @@ -530,12 +533,17 @@ class kolab_notes extends rcube_plugin foreach ($this->folders as $list_id => $folder) { if ($result = $folder->get_object($uid)) { $result['list'] = $list_id; - return $result; + break; } } } - return false; + if ($result) { + // get note tags + $result['tags'] = $this->get_tags($result['uid']); + } + + return $result; } /** @@ -576,7 +584,7 @@ class kolab_notes extends rcube_plugin public function note_action() { $action = rcube_utils::get_input_value('_do', RCUBE_INPUT_POST); - $note = rcube_utils::get_input_value('_data', RCUBE_INPUT_POST, true); + $note = rcube_utils::get_input_value('_data', RCUBE_INPUT_POST, true); $success = false; switch ($action) { @@ -665,9 +673,12 @@ class kolab_notes extends rcube_plugin // generate new note object from input $object = $this->_write_preprocess($note, $old); - // email links are handled separately + // email links and tags are handled separately $links = $object['links']; + $tags = $object['tags']; + unset($object['links']); + unset($object['tags']); $saved = $folder->save($object, 'note', $note['uid']); @@ -682,9 +693,12 @@ class kolab_notes extends rcube_plugin 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); $note = $object; $note['list'] = $list_id; + $note['tags'] = (array) $tags; // cache this in memory for later read $key = $list_id . ':' . $note['uid']; @@ -700,7 +714,8 @@ class kolab_notes extends rcube_plugin function move_note($note, $list_id) { $this->_read_lists(); - $tofolder = $this->get_folder($list_id); + + $tofolder = $this->get_folder($list_id); $fromfolder = $this->get_folder($note['list']); if ($fromfolder && $tofolder) { @@ -730,6 +745,7 @@ class kolab_notes extends rcube_plugin if ($status) { $this->save_links($note['uid'], null); + $this->save_tags($note['uid'], null); } return $status; @@ -740,8 +756,8 @@ class kolab_notes extends rcube_plugin */ public function list_action() { - $action = rcube_utils::get_input_value('_do', RCUBE_INPUT_GPC); - $list = rcube_utils::get_input_value('_list', RCUBE_INPUT_GPC, true); + $action = rcube_utils::get_input_value('_do', RCUBE_INPUT_GPC); + $list = rcube_utils::get_input_value('_list', RCUBE_INPUT_GPC, true); $success = $update_cmd = false; if (empty($action)) { @@ -1037,6 +1053,18 @@ class kolab_notes extends rcube_plugin return array_unique($result); } + /** + * Get note tags + */ + private function get_tags($uid) + { + $config = kolab_storage_config::get_instance(); + $tags = $config->get_tags($uid); + $tags = array_map(function($v) { return $v['name']; }, $tags); + + return $tags; + } + /** * Find notes assigned to specified message */ @@ -1148,6 +1176,15 @@ class kolab_notes extends rcube_plugin return $linkref; } + /** + * Update note tags + */ + private function save_tags($uid, $tags) + { + $config = kolab_storage_config::get_instance(); + $config->save_tags($uid, $tags); + } + /** * Process the given note data (submitted by the client) before saving it */ @@ -1192,8 +1229,8 @@ class kolab_notes extends rcube_plugin } // make list of categories unique - if (is_array($object['categories'])) { - $object['categories'] = array_unique(array_filter($object['categories'])); + if (is_array($object['tags'])) { + $object['tags'] = array_unique(array_filter($object['tags'])); } unset($object['list'], $object['tempid'], $object['created'], $object['changed'], $object['created_'], $object['changed_']); diff --git a/plugins/kolab_notes/notes.js b/plugins/kolab_notes/notes.js index a1763705..974e5255 100644 --- a/plugins/kolab_notes/notes.js +++ b/plugins/kolab_notes/notes.js @@ -325,7 +325,7 @@ function rcube_kolab_notes_ui(settings) uid: null, title: rcmail.gettext('newnote','kolab_notes'), description: '', - categories: [], + tags: [], created: rcmail.gettext('now', 'kolab_notes'), changed: rcmail.gettext('now', 'kolab_notes') }, rcmail.env.kolab_notes_template || {}); @@ -417,7 +417,7 @@ function rcube_kolab_notes_ui(settings) uid: null, title: rcmail.gettext('newnote','kolab_notes'), description: '', - categories: [], + tags: [], created: rcmail.gettext('now', 'kolab_notes'), changed: rcmail.gettext('now', 'kolab_notes') } @@ -687,9 +687,9 @@ function rcube_kolab_notes_ui(settings) for (var id in noteslist.rows) { tr = noteslist.rows[id].obj; note = notesdata[id]; - match = note.categories && note.categories.length; + match = note.tags && note.tags.length; for (var i=0; match && note && i < tagsfilter.length; i++) { - if ($.inArray(tagsfilter[i], note.categories) < 0) + if ($.inArray(tagsfilter[i], note.tags) < 0) match = false; } @@ -787,7 +787,7 @@ function rcube_kolab_notes_ui(settings) // tag-edit line var tagline = $('.tagline', rcmail.gui_objects.noteviewtitle).empty()[readonly?'addClass':'removeClass']('disabled').show(); - $.each(typeof data.categories == 'object' && data.categories.length ? data.categories : [''], function(i,val){ + $.each(typeof data.tags == 'object' && data.tags.length ? data.tags : [''], function(i,val) { $('') .attr('name', 'tags[]') .attr('tabindex', '0') @@ -796,7 +796,7 @@ function rcube_kolab_notes_ui(settings) .appendTo(tagline); }); - if (!data.categories || !data.categories.length) { + if (!data.tags || !data.tags.length) { $('').addClass('placeholder') .html(rcmail.gettext('notags', 'kolab_notes')) .appendTo(tagline) @@ -941,7 +941,7 @@ function rcube_kolab_notes_ui(settings) printwin.document.title = data.title; $('#notetitle', printwin.document).html(Q(data.title)); $('#notebody', printwin.document).html(data.description); - $('#notetags', printwin.document).html('' + data.categories.join('') + ''); + $('#notetags', printwin.document).html('' + data.tags.join('') + ''); $('#notecreated', printwin.document).html(Q(me.selected_note.created)); $('#notechanged', printwin.document).html(Q(me.selected_note.changed)); printwin.print(); @@ -1026,8 +1026,8 @@ function rcube_kolab_notes_ui(settings) if (typeof counts == 'undefined') { counts = {}; $.each(notesdata, function(id, rec){ - for (var t, j=0; rec && rec.categories && j < rec.categories.length; j++) { - t = rec.categories[j]; + for (var t, j=0; rec && rec.tags && j < rec.tags.length; j++) { + t = rec.tags[j]; if (typeof counts[t] == 'undefined') counts[t] = 0; counts[t]++; @@ -1064,10 +1064,10 @@ function rcube_kolab_notes_ui(settings) if (is_new || me.selected_note && data.id == me.selected_note.id) { render_note(data); - render_tagslist(data.categories || []); + render_tagslist(data.tags || []); } - else if (data.categories) { - render_tagslist(data.categories); + else if (data.tags) { + render_tagslist(data.tags); } // add list item on top @@ -1153,18 +1153,18 @@ function rcube_kolab_notes_ui(settings) description: editor ? editor.getContent({ format:'html' }).replace(/^

<\/p>/, '') : $('#notecontent').val(), list: listselect.length ? listselect.val() : me.selected_note.list || me.selected_list, uid: me.selected_note.uid, - categories: [] + tags: [] }; // collect tags $('.tagedit-list input[type="hidden"]', rcmail.gui_objects.noteviewtitle).each(function(i, elem){ if (elem.value) - savedata.categories.push(elem.value); + savedata.tags.push(elem.value); }); // including the "pending" one in the text box var newtag = $('#tagedit-input').val(); if (newtag != '') { - savedata.categories.push(newtag); + savedata.tags.push(newtag); } return savedata; @@ -1183,7 +1183,7 @@ function rcube_kolab_notes_ui(settings) return savedata.title != me.selected_note.title || savedata.description != me.selected_note.description - || savedata.categories.join(',') != (me.selected_note.categories || []).join(',') + || savedata.tags.join(',') != (me.selected_note.tags || []).join(',') || savedata.list != me.selected_note.list; } @@ -1367,7 +1367,7 @@ function rcube_kolab_notes_ui(settings) drop_rec = notesdata[drop_id]; // target already has this tag assigned - if (!drop_rec || (drop_rec.categories && $.inArray(tag, drop_rec.categories) >= 0)) { + if (!drop_rec || (drop_rec.tags && $.inArray(tag, drop_rec.tags) >= 0)) { return false; } @@ -1387,9 +1387,9 @@ function rcube_kolab_notes_ui(settings) if (savedata.id) delete savedata.id; if (savedata.html) delete savedata.html; - if (!savedata.categories) - savedata.categories = []; - savedata.categories.push(tag); + if (!savedata.tags) + savedata.tags = []; + savedata.tags.push(tag); rcmail.lock_form(rcmail.gui_objects.noteseditform, true); saving_lock = rcmail.set_busy(true, 'kolab_notes.savingdata'); diff --git a/plugins/libkolab/lib/kolab_storage_config.php b/plugins/libkolab/lib/kolab_storage_config.php index 96c4460b..a80cf468 100644 --- a/plugins/libkolab/lib/kolab_storage_config.php +++ b/plugins/libkolab/lib/kolab_storage_config.php @@ -515,4 +515,135 @@ class kolab_storage_config return $result; } + + /** + * Assign tags to kolab objects + * + * @param array $records List of kolab objects + * + * @return array List of tags + */ + public function apply_tags(&$records) + { + // first convert categories into tags + foreach ($records as $i => $rec) { + if (!empty($rec['categories'])) { + $folder = new kolab_storage_folder($rec['_mailbox']); + if ($object = $folder->get_object($rec['uid'])) { + $tags = $rec['categories']; + + unset($object['categories']); + unset($records[$i]['categories']); + + $this->save_tags($rec['uid'], $tags); + $folder->save($object, $rec['_type'], $rec['uid']); + } + } + } + + $tags = array(); + + // assign tags to objects + foreach ($this->get_tags() as $tag) { + foreach ($records as $idx => $rec) { + $uid = self::build_member_url($rec['uid']); + if (in_array($uid, (array) $tag['members'])) { + $records[$idx]['tags'][] = $tag['name']; + } + } + + $tags[] = $tag['name']; + } + + $tags = array_unique($tags); + + return $tags; + } + + /** + * Update object tags + * + * @param string $uid Kolab object UID + * @param array $tags List of tag names + */ + public function save_tags($uid, $tags) + { + $url = self::build_member_url($uid); + $relations = $this->get_tags(); + + foreach ($relations as $idx => $relation) { + $selected = !empty($tags) && in_array($relation['name'], $tags); + $found = !empty($relation['members']) && in_array($url, $relation['members']); + $update = false; + + // remove member from the relation + if ($found && !$selected) { + $relation['members'] = array_diff($relation['members'], (array) $url); + $update = true; + } + // add member to the relation + else if (!$found && $selected) { + $relation['members'][] = $url; + $update = true; + } + + if ($update) { + if ($this->save($relation, 'relation')) { + $this->tags[$idx] = $relation; // update in-memory cache + } + } + + if ($selected) { + $tags = array_diff($tags, (array)$relation['name']); + } + } + + // create new relations + if (!empty($tags)) { + foreach ($tags as $tag) { + $relation = array( + 'name' => $tag, + 'members' => (array) $url, + 'category' => 'tag', + ); + + if ($this->save($relation, 'relation')) { + $this->tags[] = $relation; // update in-memory cache + } + } + } + } + + /** + * Get tags (all or referring to specified object) + * + * @param string $uid Optional object UID + * + * @return array List of Relation objects + */ + public function get_tags($uid = '*') + { + if (!isset($this->tags)) { + $filter = array(array('type', '=', 'relation')); + $default = true; + $data_filter = array('category' => 'tag'); + + $this->tags = $this->get_objects($filter, $default, $data_filter); + } + + if ($uid === '*') { + return $this->tags; + } + + $result = array(); + $search = self::build_member_url($uid); + + foreach ($this->tags as $tag) { + if (in_array($search, (array) $tag['members'])) { + $result[] = $tag; + } + } + + return $result; + } }