diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php index b92e71be..87a7a7e5 100644 --- a/plugins/kolab_notes/kolab_notes.php +++ b/plugins/kolab_notes/kolab_notes.php @@ -32,6 +32,7 @@ class kolab_notes extends rcube_plugin private $ui; private $lists; private $folders; + private $cache = array(); /** * Required startup method of a Roundcube plugin @@ -202,7 +203,7 @@ class kolab_notes extends rcube_plugin } /** - * + * Handler to retrieve note records for the given list and/or search query */ public function notes_fetch() { @@ -214,7 +215,7 @@ class kolab_notes extends rcube_plugin } /** - * + * Convert the given note records for delivery to the client */ protected function notes_data($records, &$tags) { @@ -234,7 +235,7 @@ class kolab_notes extends rcube_plugin } /** - * + * Read note records for the given list from the storage backend */ protected function list_notes($list_id, $search = null) { @@ -261,6 +262,9 @@ class kolab_notes extends rcube_plugin return $results; } + /** + * Handler for delivering a full note record to the client + */ public function note_record() { $data = $this->get_note(array( @@ -276,6 +280,9 @@ class kolab_notes extends rcube_plugin $this->rc->output->command('plugin.render_note', $data); } + /** + * Get the full note record identified by the given UID + Lolder identifier + */ public function get_note($note) { if (is_array($note)) { @@ -286,6 +293,12 @@ class kolab_notes extends rcube_plugin $uid = $note; } + // deliver from in-memory cache + $key = $list_id . ':' . $uid; + if ($this->cache[$key]) { + return $this->cache[$key]; + } + $this->_read_lists(); if ($list_id) { if ($folder = $this->folders[$list_id]) { @@ -306,7 +319,7 @@ class kolab_notes extends rcube_plugin } /** - * + * Helper method to encode the given note record for use in the client */ private function _client_encode(&$note) { @@ -331,6 +344,9 @@ class kolab_notes extends rcube_plugin return $note; } + /** + * Handler for client-initiated actions on a single note record + */ public function note_action() { $action = rcube_utils::get_input_value('_do', RCUBE_INPUT_POST); @@ -347,6 +363,17 @@ class kolab_notes extends rcube_plugin $refresh['tempid'] = $temp_id; } break; + + case 'delete': + $uids = explode(',', $note['uid']); + foreach ($uids as $uid) { + $note['uid'] = $uid; + if (!($success = $this->delete_note($note))) { + $refresh = $this->get_note($note); + break; + } + } + break; } // show confirmation/error message @@ -354,7 +381,7 @@ class kolab_notes extends rcube_plugin $this->rc->output->show_message('successfullysaved', 'confirmation'); } else { - $this->rc->output->show_message('kolab_notes.errorsaving', 'error'); + $this->rc->output->show_message('errorsaving', 'error'); } // unlock client @@ -368,10 +395,10 @@ class kolab_notes extends rcube_plugin /** * Update an note record with the given data * - * @param array Hash array with note properties + * @param array Hash array with note properties (id, list) * @return boolean True on success, False on error */ - private function save_note($note) + private function save_note(&$note) { $this->_read_lists(); @@ -413,12 +440,33 @@ class kolab_notes extends rcube_plugin else { $note = $object; $note['list'] = $list_id; - // TODO: cache this in memory for later read + + // cache this in memory for later read + $key = $list_id . ':' . $note['uid']; + $this->cache[$key] = $note; } return $saved; } + /** + * Remove a single note record from the backend + * + * @param array Hash array with note properties (id, list) + * @param boolean Remove record irreversible (mark as deleted otherwise) + * @return boolean True on success, False on error + */ + public function delete_note($note, $force = true) + { + $this->_read_lists(); + + $list_id = $note['list']; + if (!$list_id || !($folder = $this->folders[$list_id])) + return false; + + return $folder->delete($note['uid'], $force); + } + /** * Process the given note data (submitted by the client) before saving it @@ -431,6 +479,7 @@ class kolab_notes extends rcube_plugin // clean up HTML content $object['description'] = $this->_wash_html($note['description']); + $is_html = true; // try to be smart and convert to plain-text if no real formatting is detected if (preg_match('!
(.*)
!ims', $object['description'], $m)) { @@ -438,9 +487,16 @@ class kolab_notes extends rcube_plugin // $converter = new rcube_html2text($m[1], false, true, 0); // $object['description'] = rtrim($converter->get_text()); $object['description'] = preg_replace('!!', "\n", $m[1]); + $is_html = false; } } + // Add proper HTML header, otherwise Kontact renders it as plain text + if ($is_html) { + $object['description'] = ''."\n" . + str_replace('', '', $object['description']); + } + // copy meta data (starting with _) from old object foreach ((array)$old as $key => $val) { if (!isset($object[$key]) && $key[0] == '_') @@ -461,13 +517,13 @@ class kolab_notes extends rcube_plugin . '' . '' . $html . ''; - // clean HTML with washhtml by Frederic Motte + // clean HTML with washtml by Frederic Motte $wash_opts = array( 'show_washed' => false, 'allow_remote' => 1, 'charset' => RCUBE_CHARSET, - 'html_elements' => array('html', 'body', 'link'), - 'html_attribs' => array('rel', 'type'), + 'html_elements' => array('html', 'head', 'meta', 'body', 'link'), + 'html_attribs' => array('rel', 'type', 'name', 'http-equiv'), ); // initialize HTML washer @@ -476,7 +532,7 @@ class kolab_notes extends rcube_plugin //$washer->add_callback('form', 'rcmail_washtml_callback'); //$washer->add_callback('style', 'rcmail_washtml_callback'); - // Remove non-UTF8 characters (#1487813) + // Remove non-UTF8 characters $html = rcube_charset::clean($html); $html = $washer->wash($html); @@ -486,6 +542,6 @@ class kolab_notes extends rcube_plugin return $html; } - + } diff --git a/plugins/kolab_notes/kolab_notes_ui.php b/plugins/kolab_notes/kolab_notes_ui.php index 264288d4..f3821400 100644 --- a/plugins/kolab_notes/kolab_notes_ui.php +++ b/plugins/kolab_notes/kolab_notes_ui.php @@ -67,7 +67,7 @@ class kolab_notes_ui $settings['editor'] = array( 'lang' => $lang, - 'editor_css' => $this->plugin->url() . $this->plugin->local_skin_path() . '/editor.css', + 'editor_css' => $this->plugin->url($this->plugin->local_skin_path() . '/editor.css'), 'spellcheck' => intval($this->rc->config->get('enable_spellcheck')), 'spelldict' => intval($this->rc->config->get('spellcheck_dictionary')) ); diff --git a/plugins/kolab_notes/localization/en_US.inc b/plugins/kolab_notes/localization/en_US.inc index 131c6c60..4301d1a7 100644 --- a/plugins/kolab_notes/localization/en_US.inc +++ b/plugins/kolab_notes/localization/en_US.inc @@ -15,3 +15,4 @@ $labels['changed'] = 'Last Modified'; $labels['savingdata'] = 'Saving data...'; $labels['recordnotfound'] = 'Record not found'; $labels['entertitle'] = 'Please enter a title for this note!'; +$labels['deletenotesconfirm'] = 'Do you really want to delete the selected notes?'; diff --git a/plugins/kolab_notes/notes.js b/plugins/kolab_notes/notes.js index d2b9319d..ff3aef88 100644 --- a/plugins/kolab_notes/notes.js +++ b/plugins/kolab_notes/notes.js @@ -43,11 +43,11 @@ function rcube_kolab_notes_ui(settings) { // register button commands rcmail.register_command('createnote', function(){ edit_note(null, 'new'); }, false); - rcmail.register_command('list-create', function(){ list_edit_dialog(null); }, true); + rcmail.register_command('list-create', function(){ list_edit_dialog(null); }, false); rcmail.register_command('list-edit', function(){ list_edit_dialog(me.selected_list); }, false); rcmail.register_command('list-remove', function(){ list_remove(me.selected_list); }, false); rcmail.register_command('save', save_note, true); - rcmail.register_command('delete', delete_note, true); + rcmail.register_command('delete', delete_note, false); rcmail.register_command('search', quicksearch, true); rcmail.register_command('reset-search', reset_search, true); @@ -87,6 +87,8 @@ function rcube_kolab_notes_ui(settings) else { reset_view(); } + + rcmail.enable_command('delete', me.notebooks[me.selected_list] && me.notebooks[me.selected_list].editable && list.selection.length > 0); }) .init(); } @@ -293,7 +295,7 @@ function rcube_kolab_notes_ui(settings) tr = noteslist.rows[id].obj; note = notesdata[id]; match = note.categories && note.categories.length; - for (var i=0; match && i < tagsfilter.length; i++) { + for (var i=0; match && note && i < tagsfilter.length; i++) { if ($.inArray(tagsfilter[i], note.categories) < 0) match = false; } @@ -306,7 +308,8 @@ function rcube_kolab_notes_ui(settings) } if (me.selected_note && me.selected_note.uid == note.uid && !match) { - reset_view(); + noteslist.clear_selection(); +// reset_view(); } } } @@ -389,7 +392,8 @@ function rcube_kolab_notes_ui(settings) .on('click', function(){ $('.tagline .placeholder').hide(); }); me.selected_note = data; - rcmail.enable_command('save', 'delete', list.editable && !data.readonly); + me.selected_note.id = rcmail.html_identifier_encode(data.uid); + rcmail.enable_command('save', list.editable && !data.readonly); var html, node, editor = tinyMCE.get('notecontent'); if (editor) { @@ -495,12 +499,25 @@ function rcube_kolab_notes_ui(settings) function update_note(data) { data.id = rcmail.html_identifier_encode(data.uid); + + var row, is_new = notesdata[data.id] == undefined notesdata[data.id] = data; render_note(data); + // add list item on top + if (is_new) { + noteslist.insert_row({ + id: 'rcmrow' + data.id, + cols: [ + { className:'title', innerHTML:Q(data.title) }, + { className:'date', innerHTML:Q(data.changed || '') } + ] + }, true); + + noteslist.select(data.id); + } // update list item - var row = noteslist.rows[data.id]; - if (row) { + else if (row = noteslist.rows[data.id]) { $('.title', row.obj).html(Q(data.title)); $('.date', row.obj).html(Q(data.changed || '')); // TODO: move to top @@ -515,11 +532,10 @@ function rcube_kolab_notes_ui(settings) function reset_view() { me.selected_note = null; - noteslist.clear_selection(); $('.notetitle', rcmail.gui_objects.noteviewtitle).val(''); $('.tagline, .dates', rcmail.gui_objects.noteviewtitle).hide(); $(rcmail.gui_objects.noteseditform).hide(); - rcmail.enable_command('save', 'delete', false); + rcmail.enable_command('save', false); } /** @@ -564,12 +580,29 @@ function rcube_kolab_notes_ui(settings) function delete_note() { - if (!me.selected_note) { + if (!noteslist.selection.length) { return false; } - alert(me.selected_note.title) - reset_view(); + if (confirm(rcmail.gettext('deletenotesconfirm','kolab_notes'))) { + var rec, id, uids = []; + for (var i=0; i < noteslist.selection.length; i++) { + id = noteslist.selection[i]; + rec = notesdata[id]; + if (rec) { + noteslist.remove_row(id); + uids.push(rec.uid); + delete notesdata[id]; + } + } + + saving_lock = rcmail.set_busy(true, 'kolab_notes.savingdata'); + rcmail.http_post('action', { _data: { uid: uids.join(','), list: me.selected_list }, _do: 'delete' }, true); + + reset_view(); + update_tagcloud(); + } + } } diff --git a/plugins/kolab_notes/skins/larry/notes.css b/plugins/kolab_notes/skins/larry/notes.css index a3ba1589..747e0d19 100644 --- a/plugins/kolab_notes/skins/larry/notes.css +++ b/plugins/kolab_notes/skins/larry/notes.css @@ -10,6 +10,18 @@ * See http://creativecommons.org/licenses/by-sa/3.0/ for details. */ + +#taskbar a.button-notes span.button-inner { + background-image: url('sprites.png'); + background-position: 0 0; +} + +#taskbar a.button-notes:hover span.button-inner, +#taskbar a.button-notes.button-selected span.button-inner { + background-image: url('sprites.png'); + background-position: 0 -26px; +} + .notesview #sidebar { position: absolute; top: 42px; @@ -28,15 +40,8 @@ } .notesview #notestoolbar a.button.createnote { - -} - -.notesview #taskbar a.button-notes span.button-inner { - -} - -.notesview #taskbar a.button-notes.button-selected span.button-inner { - + background-image: url('sprites.png'); + background-position: center -54px; } .notesview #quicksearchbar { @@ -146,7 +151,7 @@ } .notesview #notedetailstitle { - height: 68px; + height: auto; } .notesview #notedetailstitle .tagedit-list, @@ -180,7 +185,8 @@ } .notesview #notedetailstitle .dates { - margin-top: 0; + margin-top: 4px; + margin-bottom: 4px; } .notesview #notedetailstitle .tagline { diff --git a/plugins/kolab_notes/skins/larry/sprites.png b/plugins/kolab_notes/skins/larry/sprites.png new file mode 100644 index 00000000..cee37c3f Binary files /dev/null and b/plugins/kolab_notes/skins/larry/sprites.png differ