Store note tags in relation objects (#3395)

This commit is contained in:
Aleksander Machniak 2014-08-18 19:58:44 -04:00
parent f554c20175
commit 26f71de1db
3 changed files with 208 additions and 40 deletions

View file

@ -406,10 +406,16 @@ class kolab_notes extends rcube_plugin
public function notes_fetch() public function notes_fetch()
{ {
$search = rcube_utils::get_input_value('_q', RCUBE_INPUT_GPC, true); $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); $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) protected function notes_data($records, &$tags)
{ {
$tags = array(); $config = kolab_storage_config::get_instance();
$tags = $config->apply_tags($records);
foreach ($records as $i => $rec) { foreach ($records as $i => $rec) {
unset($records[$i]['description']); unset($records[$i]['description']);
$this->_client_encode($records[$i]); $this->_client_encode($records[$i]);
foreach ((array)$rec['categories'] as $tag) {
$tags[] = $tag;
}
} }
$tags = array_unique($tags);
return $records; return $records;
} }
@ -460,8 +462,7 @@ class kolab_notes extends rcube_plugin
$matches = 0; $matches = 0;
$contents = mb_strtolower( $contents = mb_strtolower(
$record['title'] . $record['title'] .
($this->is_html($record) ? strip_tags($record['description']) : $record['description']) . ($this->is_html($record) ? strip_tags($record['description']) : $record['description'])
join(' ', (array)$record['categories'])
); );
foreach ($words as $word) { foreach ($words as $word) {
if (mb_strpos($contents, $word) !== false) { if (mb_strpos($contents, $word) !== false) {
@ -519,10 +520,12 @@ class kolab_notes extends rcube_plugin
return $this->cache[$key]; return $this->cache[$key];
} }
$result = false;
$this->_read_lists(); $this->_read_lists();
if ($list_id) { if ($list_id) {
if ($folder = $this->get_folder($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 // 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) { foreach ($this->folders as $list_id => $folder) {
if ($result = $folder->get_object($uid)) { if ($result = $folder->get_object($uid)) {
$result['list'] = $list_id; $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() public function note_action()
{ {
$action = rcube_utils::get_input_value('_do', RCUBE_INPUT_POST); $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; $success = false;
switch ($action) { switch ($action) {
@ -665,9 +673,12 @@ class kolab_notes extends rcube_plugin
// generate new note object from input // generate new note object from input
$object = $this->_write_preprocess($note, $old); $object = $this->_write_preprocess($note, $old);
// email links are handled separately // email links and tags are handled separately
$links = $object['links']; $links = $object['links'];
$tags = $object['tags'];
unset($object['links']); unset($object['links']);
unset($object['tags']);
$saved = $folder->save($object, 'note', $note['uid']); $saved = $folder->save($object, 'note', $note['uid']);
@ -682,9 +693,12 @@ class kolab_notes extends rcube_plugin
else { else {
// save links in configuration.relation object // save links in configuration.relation object
$this->save_links($object['uid'], $links); $this->save_links($object['uid'], $links);
// save tags in configuration.relation object
$this->save_tags($object['uid'], $tags);
$note = $object; $note = $object;
$note['list'] = $list_id; $note['list'] = $list_id;
$note['tags'] = (array) $tags;
// cache this in memory for later read // cache this in memory for later read
$key = $list_id . ':' . $note['uid']; $key = $list_id . ':' . $note['uid'];
@ -700,7 +714,8 @@ class kolab_notes extends rcube_plugin
function move_note($note, $list_id) function move_note($note, $list_id)
{ {
$this->_read_lists(); $this->_read_lists();
$tofolder = $this->get_folder($list_id);
$tofolder = $this->get_folder($list_id);
$fromfolder = $this->get_folder($note['list']); $fromfolder = $this->get_folder($note['list']);
if ($fromfolder && $tofolder) { if ($fromfolder && $tofolder) {
@ -730,6 +745,7 @@ class kolab_notes extends rcube_plugin
if ($status) { if ($status) {
$this->save_links($note['uid'], null); $this->save_links($note['uid'], null);
$this->save_tags($note['uid'], null);
} }
return $status; return $status;
@ -740,8 +756,8 @@ class kolab_notes extends rcube_plugin
*/ */
public function list_action() public function list_action()
{ {
$action = rcube_utils::get_input_value('_do', RCUBE_INPUT_GPC); $action = rcube_utils::get_input_value('_do', RCUBE_INPUT_GPC);
$list = rcube_utils::get_input_value('_list', RCUBE_INPUT_GPC, true); $list = rcube_utils::get_input_value('_list', RCUBE_INPUT_GPC, true);
$success = $update_cmd = false; $success = $update_cmd = false;
if (empty($action)) { if (empty($action)) {
@ -1037,6 +1053,18 @@ class kolab_notes extends rcube_plugin
return array_unique($result); 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 * Find notes assigned to specified message
*/ */
@ -1148,6 +1176,15 @@ class kolab_notes extends rcube_plugin
return $linkref; 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 * 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 // make list of categories unique
if (is_array($object['categories'])) { if (is_array($object['tags'])) {
$object['categories'] = array_unique(array_filter($object['categories'])); $object['tags'] = array_unique(array_filter($object['tags']));
} }
unset($object['list'], $object['tempid'], $object['created'], $object['changed'], $object['created_'], $object['changed_']); unset($object['list'], $object['tempid'], $object['created'], $object['changed'], $object['created_'], $object['changed_']);

View file

@ -325,7 +325,7 @@ function rcube_kolab_notes_ui(settings)
uid: null, uid: null,
title: rcmail.gettext('newnote','kolab_notes'), title: rcmail.gettext('newnote','kolab_notes'),
description: '', description: '',
categories: [], tags: [],
created: rcmail.gettext('now', 'kolab_notes'), created: rcmail.gettext('now', 'kolab_notes'),
changed: rcmail.gettext('now', 'kolab_notes') changed: rcmail.gettext('now', 'kolab_notes')
}, rcmail.env.kolab_notes_template || {}); }, rcmail.env.kolab_notes_template || {});
@ -417,7 +417,7 @@ function rcube_kolab_notes_ui(settings)
uid: null, uid: null,
title: rcmail.gettext('newnote','kolab_notes'), title: rcmail.gettext('newnote','kolab_notes'),
description: '', description: '',
categories: [], tags: [],
created: rcmail.gettext('now', 'kolab_notes'), created: rcmail.gettext('now', 'kolab_notes'),
changed: 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) { for (var id in noteslist.rows) {
tr = noteslist.rows[id].obj; tr = noteslist.rows[id].obj;
note = notesdata[id]; 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++) { 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; match = false;
} }
@ -787,7 +787,7 @@ function rcube_kolab_notes_ui(settings)
// tag-edit line // tag-edit line
var tagline = $('.tagline', rcmail.gui_objects.noteviewtitle).empty()[readonly?'addClass':'removeClass']('disabled').show(); 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) {
$('<input>') $('<input>')
.attr('name', 'tags[]') .attr('name', 'tags[]')
.attr('tabindex', '0') .attr('tabindex', '0')
@ -796,7 +796,7 @@ function rcube_kolab_notes_ui(settings)
.appendTo(tagline); .appendTo(tagline);
}); });
if (!data.categories || !data.categories.length) { if (!data.tags || !data.tags.length) {
$('<span>').addClass('placeholder') $('<span>').addClass('placeholder')
.html(rcmail.gettext('notags', 'kolab_notes')) .html(rcmail.gettext('notags', 'kolab_notes'))
.appendTo(tagline) .appendTo(tagline)
@ -941,7 +941,7 @@ function rcube_kolab_notes_ui(settings)
printwin.document.title = data.title; printwin.document.title = data.title;
$('#notetitle', printwin.document).html(Q(data.title)); $('#notetitle', printwin.document).html(Q(data.title));
$('#notebody', printwin.document).html(data.description); $('#notebody', printwin.document).html(data.description);
$('#notetags', printwin.document).html('<span class="tag">' + data.categories.join('</span><span class="tag">') + '</span>'); $('#notetags', printwin.document).html('<span class="tag">' + data.tags.join('</span><span class="tag">') + '</span>');
$('#notecreated', printwin.document).html(Q(me.selected_note.created)); $('#notecreated', printwin.document).html(Q(me.selected_note.created));
$('#notechanged', printwin.document).html(Q(me.selected_note.changed)); $('#notechanged', printwin.document).html(Q(me.selected_note.changed));
printwin.print(); printwin.print();
@ -1026,8 +1026,8 @@ function rcube_kolab_notes_ui(settings)
if (typeof counts == 'undefined') { if (typeof counts == 'undefined') {
counts = {}; counts = {};
$.each(notesdata, function(id, rec){ $.each(notesdata, function(id, rec){
for (var t, j=0; rec && rec.categories && j < rec.categories.length; j++) { for (var t, j=0; rec && rec.tags && j < rec.tags.length; j++) {
t = rec.categories[j]; t = rec.tags[j];
if (typeof counts[t] == 'undefined') if (typeof counts[t] == 'undefined')
counts[t] = 0; counts[t] = 0;
counts[t]++; counts[t]++;
@ -1064,10 +1064,10 @@ function rcube_kolab_notes_ui(settings)
if (is_new || me.selected_note && data.id == me.selected_note.id) { if (is_new || me.selected_note && data.id == me.selected_note.id) {
render_note(data); render_note(data);
render_tagslist(data.categories || []); render_tagslist(data.tags || []);
} }
else if (data.categories) { else if (data.tags) {
render_tagslist(data.categories); render_tagslist(data.tags);
} }
// add list item on top // add list item on top
@ -1153,18 +1153,18 @@ function rcube_kolab_notes_ui(settings)
description: editor ? editor.getContent({ format:'html' }).replace(/^<p><\/p>/, '') : $('#notecontent').val(), description: editor ? editor.getContent({ format:'html' }).replace(/^<p><\/p>/, '') : $('#notecontent').val(),
list: listselect.length ? listselect.val() : me.selected_note.list || me.selected_list, list: listselect.length ? listselect.val() : me.selected_note.list || me.selected_list,
uid: me.selected_note.uid, uid: me.selected_note.uid,
categories: [] tags: []
}; };
// collect tags // collect tags
$('.tagedit-list input[type="hidden"]', rcmail.gui_objects.noteviewtitle).each(function(i, elem){ $('.tagedit-list input[type="hidden"]', rcmail.gui_objects.noteviewtitle).each(function(i, elem){
if (elem.value) if (elem.value)
savedata.categories.push(elem.value); savedata.tags.push(elem.value);
}); });
// including the "pending" one in the text box // including the "pending" one in the text box
var newtag = $('#tagedit-input').val(); var newtag = $('#tagedit-input').val();
if (newtag != '') { if (newtag != '') {
savedata.categories.push(newtag); savedata.tags.push(newtag);
} }
return savedata; return savedata;
@ -1183,7 +1183,7 @@ function rcube_kolab_notes_ui(settings)
return savedata.title != me.selected_note.title return savedata.title != me.selected_note.title
|| savedata.description != me.selected_note.description || 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; || savedata.list != me.selected_note.list;
} }
@ -1367,7 +1367,7 @@ function rcube_kolab_notes_ui(settings)
drop_rec = notesdata[drop_id]; drop_rec = notesdata[drop_id];
// target already has this tag assigned // 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; return false;
} }
@ -1387,9 +1387,9 @@ function rcube_kolab_notes_ui(settings)
if (savedata.id) delete savedata.id; if (savedata.id) delete savedata.id;
if (savedata.html) delete savedata.html; if (savedata.html) delete savedata.html;
if (!savedata.categories) if (!savedata.tags)
savedata.categories = []; savedata.tags = [];
savedata.categories.push(tag); savedata.tags.push(tag);
rcmail.lock_form(rcmail.gui_objects.noteseditform, true); rcmail.lock_form(rcmail.gui_objects.noteseditform, true);
saving_lock = rcmail.set_busy(true, 'kolab_notes.savingdata'); saving_lock = rcmail.set_busy(true, 'kolab_notes.savingdata');

View file

@ -515,4 +515,135 @@ class kolab_storage_config
return $result; 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;
}
} }