diff --git a/plugins/kolab_notes/skins/larry/notes.css b/plugins/kolab_notes/skins/larry/notes.css
index beb14349..37e34909 100644
--- a/plugins/kolab_notes/skins/larry/notes.css
+++ b/plugins/kolab_notes/skins/larry/notes.css
@@ -68,17 +68,9 @@
left: 252px;
}
-.notesview #tagsbox {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 240px;
-}
-
.notesview #notebooksbox {
position: absolute;
- top: 252px;
+ top: 0;
left: 0;
width: 100%;
bottom: 0px;
@@ -272,11 +264,6 @@
border-bottom: 0;
}
-.notesview .ui-dialog-content .tagline {
- display: none !important;
-}
-
-.notesview .notetitle .disabled .tagedit-list,
.notesview .notetitle input.inline-edit:disabled {
outline: none;
padding-left: 0;
@@ -306,7 +293,6 @@
}
.notesview .notetitle .dates,
-.notesview .notetitle .tagline,
.notesdialog .notebookselect label {
color: #999;
font-weight: normal;
@@ -318,38 +304,6 @@
margin-top: 4px;
}
-.notesview .notetitle .tagline {
- position: relative;
- cursor: text;
- margin: 4px 0 0 0;
-}
-
-.notesview .notetitle .tagline.disabled {
- margin-top: 0;
-}
-
-.notesview .notetitle .tagline .placeholder {
- position: absolute;
- top: 6px;
- left: 6px;
- z-index: 2;
-}
-
-.notesview .notetitle .tagline.disabled .placeholder {
- left: 0;
-}
-
-.notesview .notetitle .tagedit-list {
- position: relative;
- z-index: 1;
- min-height: 32px;
-/* padding: 2px; */
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- -ms-box-sizing: border-box;
- box-sizing: border-box;
-}
-
.notetitle .col-form-label {
display: none;
}
@@ -366,23 +320,6 @@
clear: both;
}
-/* Firefox 3.6 */
-_:not(), _:-moz-handler-blocked, .notesview .notetitle .tagedit-list {
- min-height: 26px;
-}
-
-.notesview .notetitle .disabled .tagedit-list {
- min-height: 26px;
-}
-
-.notesview .notetitle #tagedit-input {
- background: none;
-}
-
-.notesview .tag-draghelper {
- z-index: 1000;
-}
-
.notesview .notetitle .notecreated,
.notesview .notetitle .notechanged {
display: inline-block;
diff --git a/plugins/kolab_notes/skins/larry/templates/listform.html b/plugins/kolab_notes/skins/larry/templates/listform.html
new file mode 100644
index 00000000..5fc9ea3b
--- /dev/null
+++ b/plugins/kolab_notes/skins/larry/templates/listform.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/kolab_notes/skins/larry/templates/notes.html b/plugins/kolab_notes/skins/larry/templates/notes.html
index ec47f3c8..58e026f0 100644
--- a/plugins/kolab_notes/skins/larry/templates/notes.html
+++ b/plugins/kolab_notes/skins/larry/templates/notes.html
@@ -30,13 +30,6 @@
:
+
+
+
+
+
+
-
').attr('class', tag_class_name(tag))
- .text(tag.name).data('tag', tag.uid).appendTo(list);
+ .text(tag.name).data('tag', tag.uid)
+ .append('')
+ .appendTo(list);
if (clickable) {
element.click(function(e) {
@@ -173,12 +195,38 @@ function add_tag_element(list, tag, clickable)
e.preventDefault();
return false;
- })
+ });
+
+ if (!$('html.touch').length) {
+ element.draggable({
+ addClasses: false,
+ cursor: 'default',
+ cursorAt: {left: -10},
+ revert: 'invalid',
+ revertDuration: 300,
+ helper: tag_draggable_helper,
+ start: tag_draggable_start,
+ appendTo: 'body'
+ });
+ }
}
return element;
}
+function update_counts(p)
+{
+ $('#taglist > li').each(function(i,li) {
+ var elem = $(li), tagid = elem.data('tag'),
+ tag = tag_find(tagid);
+ count = tag && p.counter ? p.counter[tag.name] : '';
+
+ elem.children('.count').text(count ? count : '');
+ });
+
+ tagscounts = p.counter;
+}
+
function manage_tags()
{
// display it as popup
@@ -388,6 +436,29 @@ function update_tags(response)
// reset tag selector popup
tag_selector_reset();
+ if (response.refresh) {
+ var list = $('#taglist');
+
+ tagsfilter = $.map(list.children('.selected'), function(li) {
+ return $(li).data('tag');
+ });
+
+ list.html('');
+ load_tags();
+ update_counts({counter: tagscounts});
+
+ if (tagsfilter.length) {
+ list.children('li').each(function() {
+ if ($.inArray($(this).data('tag'), tagsfilter) > -1) {
+ $(this).addClass('selected');
+ }
+ });
+ apply_tags_filter();
+ }
+
+ return;
+ }
+
// remove deleted tags
remove_tags(response['delete'], response.mark);
@@ -405,9 +476,9 @@ function update_tags(response)
var i, old, tag = this, id = tag.uid,
filter = function() { return $(this).data('tag') == id; },
tagbox = $('#taglist li').filter(filter),
- elements = $('span.tagbox').filter(filter),
+ elements = $('.tagbox').filter(filter),
win = rcmail.get_frame_window(rcmail.env.contentframe),
- framed = win && win.jQuery ? win.jQuery('span.tagbox').filter(function() { return win.jQuery(this).data('tag') == id; }) : [];
+ framed = win && win.jQuery ? win.jQuery('.tagbox').filter(function() { return win.jQuery(this).data('tag') == id; }) : [];
selected = $.inArray(String(id), tagsfilter);
for (i in rcmail.env.tags)
@@ -454,7 +525,8 @@ function remove_tags(tags, selection)
}
var taglist = $('#taglist li'),
- tagboxes = $((selection && rcmail.message_list ? 'tr.selected ' : '') + 'span.tagbox'),
+ list = main_list_widget(),
+ tagboxes = $((selection && list ? 'tr.selected ' : '') + 'span.tagbox'),
win = rcmail.get_frame_window(rcmail.env.contentframe),
frame_tagboxes = win && win.jQuery ? win.jQuery('span.tagbox') : [],
update_filter = false;
@@ -492,11 +564,28 @@ function remove_tags(tags, selection)
}
});
- if (update_filter && rcmail.message_list) {
+ if (update_filter && list) {
apply_tags_filter();
}
}
+// kolab-tags-refresh event handler, allowing plugins to refresh
+// the tags list e.g. when a new tag is created
+function refresh_tags(e)
+{
+ // find a new tag
+ $.each(e.tags || [], function() {
+ for (var i in rcmail.env.tags) {
+ if (rcmail.env.tags[i].name == this) {
+ return;
+ }
+ }
+
+ rcmail.http_post('plugin.kolab_tags', {_act: 'refresh'}, rcmail.display_message(rcmail.get_label('loading'), 'loading'));
+ return false;
+ });
+}
+
// unselect all selected tags in the tag cloud
function reset_tags()
{
@@ -602,8 +691,21 @@ function tag_remove(props, obj, event)
// executes messages search according to selected messages
function apply_tags_filter()
{
- rcmail.enable_command('reset-tags', tagsfilter.length && rcmail.message_list);
- rcmail.qsearch();
+ rcmail.enable_command('reset-tags', tagsfilter.length && main_list_widget());
+
+ if (rcmail.task == 'mail')
+ rcmail.qsearch();
+ else {
+ // Convert tag id to tag label
+ var tags = [];
+ $.each(rcmail.env.tags, function() {
+ if ($.inArray(this.uid, tagsfilter) > -1) {
+ tags.push(this.name);
+ }
+ });
+
+ rcmail.triggerEvent('kolab-tags-search', tags);
+ }
}
// adds _tags argument to http search request
@@ -896,3 +998,162 @@ function tag_class_name(tag)
{
return 'kolab-tag-' + tag.uid.replace(/[^a-z0-9]/ig, '');
}
+
+function kolab_tags_input(element, tags, readonly)
+{
+ var list,
+ tagline = $(element)[readonly ? 'addClass' : 'removeClass']('disabled').empty().show(),
+ source_callback = function(request, response) {
+ request = request.term.toUpperCase();
+ response($.map(rcmail.env.tags || [], function(v) {
+ if (request.length && v.name.toUpperCase().indexOf(request) > -1)
+ return v.name;
+ }));
+ };
+
+ $.each(tags && tags.length ? tags : [''], function(i,val) {
+ $('').attr({name: 'tags[]', tabindex: '0', 'class': 'tag'})
+ .val(val)
+ .appendTo(tagline);
+ });
+
+ if (!tags || !tags.length) {
+ $('').addClass('placeholder')
+ .text(rcmail.gettext('kolab_tags.notags'))
+ .appendTo(tagline)
+ .click(function(e) { list.trigger('click'); });
+ }
+
+ $('input.tag', element).tagedit({
+ animSpeed: 100,
+ allowEdit: false,
+ allowAdd: !readonly,
+ allowDelete: !readonly,
+ checkNewEntriesCaseSensitive: false,
+ autocompleteOptions: { source: source_callback, minLength: 0, noCheck: true },
+ texts: { removeLinkTitle: rcmail.gettext('kolab_tags.untag') }
+ });
+
+ list = element.find('.tagedit-list');
+
+ list.addClass('form-control'); // Elastic
+
+ if (!readonly) {
+ list.on('click', function() { $('.placeholder', element).hide(); });
+ }
+
+ // Track changes on the list to style tag elements
+ if (window.MutationObserver) {
+ var observer = new MutationObserver(kolab_tags_input_update);
+ observer.observe(list[0], {childList: true});
+ }
+ else {
+ list.on('click keyup', kolab_tags_input_update);
+ }
+
+ kolab_tags_input_update({target: list});
+
+ return list;
+}
+
+function kolab_tags_input_update(e)
+{
+ var tags = {},
+ target = e.length && e[0].target ? e[0].target : e.target;
+
+ // first generate tags list indexed by name
+ $.each(rcmail.env.tags, function(i, tag) {
+ tags[tag.name] = tag;
+ });
+
+ $(target).find('li.tagedit-listelement-old:not([class*="tagbox"])').each(function() {
+ var text = $('span', this).text();
+
+ $(this).addClass('tagbox');
+
+ if (tags[text]) {
+ $(this).data('tag', tags[text].uid);
+ tag_set_color(this, tags[text]);
+ }
+ });
+}
+
+function kolab_tags_input_value(element)
+{
+ var tags = [];
+
+ $(element || '.tagedit-list').find('input').each(function(i, elem) {
+ if (elem.value)
+ tags.push(elem.value);
+ });
+
+ return tags;
+}
+
+function tag_draggable_helper()
+{
+ var draghelper = $('.tag-draghelper'),
+ tagid = $(this).data('tag'),
+ node = $(this).clone(),
+ tagbox = $('');
+
+ if (!draghelper.length) {
+ draghelper = $('');
+ }
+
+ $('span.count', node).remove();
+ tagbox.text(node.text()).appendTo(draghelper.html(''));
+ tag_set_color(tagbox, tag_find(tagid));
+
+ return draghelper[0];
+}
+
+function tag_draggable_start(event, ui)
+{
+ // register notes list to receive drop events
+ if (rcmail.gui_objects.noteslist) {
+ $('tr', rcmail.gui_objects.noteslist).droppable({
+ addClasses: false,
+ hoverClass: 'droptarget',
+ accept: tag_droppable_accept,
+ drop: tag_draggable_dropped
+ });
+ }
+
+ // allow to drop tags onto edit form title
+ $('body.task-notes .content.formcontainer,#notedetailstitle.boxtitle').droppable({
+ addClasses: false,
+ accept: function() { return $(this).is('.formcontainer,.boxtitle'); },
+ drop: function(event, ui) {
+ var tag = tag_find(ui.draggable.data('tag'));
+ $('#tagedit-input').val(tag.name).trigger('transformToTag');
+ $('.tagline .placeholder', rcmail.gui_objects.noteviewtitle).hide();
+ }
+ }).addClass('tag-droppable');
+}
+
+function tag_draggable_dropped(event, ui)
+{
+ var drop_id = $(this).attr('id').replace(/^rcmrow/, ''),
+ tag = tag_find(ui.draggable.data('tag'));
+
+ rcmail.triggerEvent('kolab-tags-drop', {id: drop_id, tag: tag.name, list: $(this).parent()});
+}
+
+function tag_droppable_accept(draggable)
+{
+ if (rcmail.busy) {
+ return false;
+ }
+
+ var tag = tag_find(draggable.data('tag')),
+ drop_id = $(this).attr('id').replace(/^rcmrow/, ''),
+ data = rcmail.triggerEvent('kolab-tags-drop-data', {id: drop_id});
+
+ // target already has this tag assigned
+ if (!data || (data.tags && $.inArray(tag.name, data.tags) >= 0)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/plugins/kolab_tags/kolab_tags.php b/plugins/kolab_tags/kolab_tags.php
index 12fbb01d..b4fff996 100644
--- a/plugins/kolab_tags/kolab_tags.php
+++ b/plugins/kolab_tags/kolab_tags.php
@@ -23,7 +23,7 @@
class kolab_tags extends rcube_plugin
{
- public $task = 'mail';
+ public $task = 'mail|notes';
public $rc;
public $home;
diff --git a/plugins/kolab_tags/lib/kolab_tags_engine.php b/plugins/kolab_tags/lib/kolab_tags_engine.php
index 226edb0e..1c45040f 100644
--- a/plugins/kolab_tags/lib/kolab_tags_engine.php
+++ b/plugins/kolab_tags/lib/kolab_tags_engine.php
@@ -46,12 +46,7 @@ class kolab_tags_engine
*/
public function ui()
{
- // set templates of Files UI and widgets
- if ($this->rc->task != 'mail') {
- return;
- }
-
- if ($this->rc->action && !in_array($this->rc->action, array('show', 'preview'))) {
+ if ($this->rc->action && !in_array($this->rc->action, array('show', 'preview', 'dialog-ui'))) {
return;
}
@@ -62,7 +57,7 @@ class kolab_tags_engine
$this->rc->output->add_label('cancel', 'save');
$this->plugin->add_label('tags', 'add', 'edit', 'delete', 'saving',
'nameempty', 'nameexists', 'colorinvalid', 'untag', 'tagname',
- 'tagcolor', 'tagsearchnew', 'newtag');
+ 'tagcolor', 'tagsearchnew', 'newtag', 'notags');
$this->rc->output->add_handlers(array(
'plugin.taglist' => array($this, 'taglist'),
@@ -71,11 +66,12 @@ class kolab_tags_engine
$ui = $this->rc->output->parse('kolab_tags.ui', false, false);
$this->rc->output->add_footer($ui);
- // load miniColors
+ // load miniColors and tagedit
jqueryui::miniColors();
+ jqueryui::tagedit();
// Modify search filter (and set selected tags)
- if ($this->rc->action == 'show' || !$this->rc->action) {
+ if ($this->rc->task == 'mail' && ($this->rc->action == 'show' || !$this->rc->action)) {
$this->search_filter_mods();
}
}
@@ -283,6 +279,18 @@ class kolab_tags_engine
}
}
+ /**
+ * Refresh tags list
+ */
+ public function action_refresh()
+ {
+ $taglist = $this->backend->list_tags();
+ $taglist = array_map(array($this, 'parse_tag'), $taglist);
+
+ $this->rc->output->set_env('tags', $taglist);
+ $this->rc->output->command('plugin.kolab_tags', array('refresh' => 1));
+ }
+
/**
* Template object building tags list/cloud
*/
diff --git a/plugins/kolab_tags/localization/en_US.inc b/plugins/kolab_tags/localization/en_US.inc
index 26b07248..4dab4012 100644
--- a/plugins/kolab_tags/localization/en_US.inc
+++ b/plugins/kolab_tags/localization/en_US.inc
@@ -15,6 +15,7 @@ $labels['tagactions'] = 'Tag actions...';
$labels['tagadd'] = 'Tag as...';
$labels['tagremove'] = 'Remove tag...';
$labels['untag'] = 'Remove tag';
+$labels['notags'] = 'No tags';
$labels['tagremoveall'] = 'Remove all tags';
$labels['add'] = 'Add';
$labels['edit'] = 'Edit';
diff --git a/plugins/kolab_tags/skins/elastic/templates/ui.html b/plugins/kolab_tags/skins/elastic/templates/ui.html
index 9eee997d..728d4e8e 100644
--- a/plugins/kolab_tags/skins/elastic/templates/ui.html
+++ b/plugins/kolab_tags/skins/elastic/templates/ui.html
@@ -28,7 +28,7 @@
$(document).ready(function(e) {
// put tags cloud under folders list
var tagcloud = $('#tagcloud').detach();
- $('#folderlist-content > ul:last').after(tagcloud.show());
+ $('#folderlist-content,#notebooks-content').children('ul:first').after(tagcloud.show());
// add tag message menu positions to Mark menu
var menu = $('#tagmessagemenu li').detach();
@@ -36,7 +36,7 @@ $(document).ready(function(e) {
// add tags management menu positions to folder actions menu
menu = $('#tagsmenu li').detach();
- $('#mailboxoptions-menu ul').append(menu);
+ $('#mailboxoptions-menu,#notebookactions-menu').find('ul').append(menu);
// Apply tags colors in Elastic-way
rcmail.addEventListener('kolab-tags-update', function() {
@@ -58,7 +58,7 @@ $(document).ready(function(e) {
// Ignore tags coloring on lists, handled above, use default method for tagboxes only
rcmail.addEventListener('kolab-tag-color', function(prop) {
- if ($(prop.obj).is('li')) {
+ if ($(prop.obj).is('li:not(.tagedit-listelement)')) {
return false;
}
});
diff --git a/plugins/kolab_tags/skins/larry/style.css b/plugins/kolab_tags/skins/larry/style.css
index 175cef0a..8edcfef7 100644
--- a/plugins/kolab_tags/skins/larry/style.css
+++ b/plugins/kolab_tags/skins/larry/style.css
@@ -70,6 +70,7 @@ ul.toolbarmenu li span.icon.tagremoveall {
}
.tag-draghelper .tag .count,
+#taglist li .count:empty,
#taglist li.inactive .count {
display: none;
}
@@ -138,3 +139,86 @@ ul.toolbarmenu li span.icon.tagremoveall {
#tag-selector li.search input {
width: 120px;
}
+
+.tagline {
+ color: #999;
+ font-weight: normal;
+ font-size: 0.9em;
+ position: relative;
+ cursor: text;
+ margin: 4px 0 0 0;
+ min-height: 31px;
+}
+
+.tagline.disabled {
+ margin-top: 0;
+}
+
+.tagline .tagedit-list {
+ min-height: 31px;
+}
+
+.tagline.disabled .tagedit-list {
+ outline: none;
+ padding-left: 0;
+ border: 0;
+ min-height: 31px;
+ background: rgba(255,255,255,0.01);
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ -o-box-shadow: none;
+ box-shadow: none;
+}
+
+.tagline .placeholder {
+ position: absolute;
+ top: 6px;
+ left: 6px;
+ z-index: 2;
+}
+
+.tagline.disabled .placeholder {
+ left: 0;
+}
+
+.tagedit-list {
+ position: relative;
+ z-index: 1;
+ min-height: 32px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.tagedit-list #tagedit-input {
+ background: none;
+}
+
+.tag-draghelper {
+ z-index: 1000;
+}
+
+.tagedit span.tag-element,
+.tagedit-list li.tagedit-listelement-old {
+ background: #ddeef5 !important;
+ border: 0 !important;
+ padding: 2px 20px 0 6px !important;
+ line-height: 1.5 !important;
+ position: relative !important;
+}
+
+.tagedit-list li.tagedit-listelement-old a.tagedit-close,
+.tagedit-list li.tagedit-listelement-old a.tagedit-break,
+.tagedit-list li.tagedit-listelement-old a.tagedit-delete,
+.tagedit-list li.tagedit-listelement-old a.tagedit-save {
+ position: absolute !important;
+ top: 1px !important;
+ right: 0;
+ background: transparent !important;
+ text-indent: initial !important;
+}
+
+.tag-droppable.ui-droppable-hover {
+ background-color: #e8e798;
+}
\ No newline at end of file
diff --git a/plugins/kolab_tags/skins/larry/templates/ui.html b/plugins/kolab_tags/skins/larry/templates/ui.html
index 0b8d66a7..ffa02531 100644
--- a/plugins/kolab_tags/skins/larry/templates/ui.html
+++ b/plugins/kolab_tags/skins/larry/templates/ui.html
@@ -31,11 +31,25 @@
$(document).ready(function(e) {
// put tags cloud under folders list
- var tagcloud = $('#tagcloud').detach();
- $('#mailview-left').append(tagcloud.show());
+ var containers, tagcloud = $('#tagcloud').detach();
- new rcube_splitter({ id:'mailtagsplitter', p1:'#mailboxcontainer', p2:'#tagcloud',
- orientation:'h', relative:true, start:242, min:120, size:12, offset:4 }).init();
+ if (rcmail.env.task == 'mail')
+ containers = {
+ sidebar: '#mailview-left',
+ list: '#mailboxcontainer'
+ };
+ else if (rcmail.env.task == 'notes')
+ containers = {
+ sidebar: '#sidebar',
+ list: '#notebooksbox'
+ };
+
+ if (containers) {
+ $(containers.sidebar).append(tagcloud.show());
+
+ new rcube_splitter({ id: rcmail.task + 'tagsplitter', p1:containers.list, p2:'#tagcloud',
+ orientation:'h', relative:true, start:242, min:120, size:12, offset:4 }).init();
+ }
// add tag message menu positions to Mark menu
var menu = $('#tagmessagemenu li').detach();
diff --git a/plugins/libkolab/skins/elastic/include/kolab_tags.less b/plugins/libkolab/skins/elastic/include/kolab_tags.less
index a1ab0d2a..bc93a8d0 100644
--- a/plugins/libkolab/skins/elastic/include/kolab_tags.less
+++ b/plugins/libkolab/skins/elastic/include/kolab_tags.less
@@ -42,6 +42,26 @@
margin: 0 .5rem 0 .2rem;
}
}
+
+ .count {
+ position: absolute;
+ top: 0;
+ right: 0;
+ min-width: 2em;
+ line-height: 1.4rem;
+ margin: (@listing-line-height - 1.4rem)/2;
+ padding: 0 .3em;
+ border-radius: .4em;
+ background: @color-list-secondary;
+ color: #fff;
+ text-align: center;
+ font-weight: bold;
+
+ html.touch & {
+ line-height: 2rem;
+ margin: (@listing-touch-line-height - 2rem)/2;
+ }
+ }
}
#tagsform option:before,
@@ -93,14 +113,15 @@
.tagbox {
color: #fff;
- background-color: @color-main;
+ background-color: @color-main !important;
+ border: 0 !important;
border-radius: .25rem;
max-width: 4em;
padding: .1rem .4rem;
margin-right: .2rem;
font-weight: bold;
- a {
+ &:not(.tagedit-listelement) a {
color: inherit;
padding-left: .5rem;
text-decoration: none;
@@ -110,3 +131,12 @@
font-size: 1.2rem;
}
}
+
+.tag-droppable.formcontainer {
+ &.ui-droppable-active {
+ background-color: @color-black-shade-bg !important;
+ }
+ &.ui-droppable-hover {
+ background-color: @color-list-droptarget-background !important;
+ }
+}
-
-
-
-
-
-