Implement creation and deletion of notes; create icons for Larry theme

This commit is contained in:
Thomas Bruederli 2014-03-31 19:03:29 +02:00
parent aa4d0e2b94
commit 91e3227e64
6 changed files with 133 additions and 37 deletions

View file

@ -32,6 +32,7 @@ class kolab_notes extends rcube_plugin
private $ui; private $ui;
private $lists; private $lists;
private $folders; private $folders;
private $cache = array();
/** /**
* Required startup method of a Roundcube plugin * 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() 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) 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) protected function list_notes($list_id, $search = null)
{ {
@ -261,6 +262,9 @@ class kolab_notes extends rcube_plugin
return $results; return $results;
} }
/**
* Handler for delivering a full note record to the client
*/
public function note_record() public function note_record()
{ {
$data = $this->get_note(array( $data = $this->get_note(array(
@ -276,6 +280,9 @@ class kolab_notes extends rcube_plugin
$this->rc->output->command('plugin.render_note', $data); $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) public function get_note($note)
{ {
if (is_array($note)) { if (is_array($note)) {
@ -286,6 +293,12 @@ class kolab_notes extends rcube_plugin
$uid = $note; $uid = $note;
} }
// deliver from in-memory cache
$key = $list_id . ':' . $uid;
if ($this->cache[$key]) {
return $this->cache[$key];
}
$this->_read_lists(); $this->_read_lists();
if ($list_id) { if ($list_id) {
if ($folder = $this->folders[$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) private function _client_encode(&$note)
{ {
@ -331,6 +344,9 @@ class kolab_notes extends rcube_plugin
return $note; return $note;
} }
/**
* Handler for client-initiated actions on a single note record
*/
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);
@ -347,6 +363,17 @@ class kolab_notes extends rcube_plugin
$refresh['tempid'] = $temp_id; $refresh['tempid'] = $temp_id;
} }
break; 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 // show confirmation/error message
@ -354,7 +381,7 @@ class kolab_notes extends rcube_plugin
$this->rc->output->show_message('successfullysaved', 'confirmation'); $this->rc->output->show_message('successfullysaved', 'confirmation');
} }
else { else {
$this->rc->output->show_message('kolab_notes.errorsaving', 'error'); $this->rc->output->show_message('errorsaving', 'error');
} }
// unlock client // unlock client
@ -368,10 +395,10 @@ class kolab_notes extends rcube_plugin
/** /**
* Update an note record with the given data * 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 * @return boolean True on success, False on error
*/ */
private function save_note($note) private function save_note(&$note)
{ {
$this->_read_lists(); $this->_read_lists();
@ -413,12 +440,33 @@ class kolab_notes extends rcube_plugin
else { else {
$note = $object; $note = $object;
$note['list'] = $list_id; $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; 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 * 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 // clean up HTML content
$object['description'] = $this->_wash_html($note['description']); $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 // try to be smart and convert to plain-text if no real formatting is detected
if (preg_match('!<body><pre>(.*)</pre></body>!ims', $object['description'], $m)) { if (preg_match('!<body><pre>(.*)</pre></body>!ims', $object['description'], $m)) {
@ -438,9 +487,16 @@ class kolab_notes extends rcube_plugin
// $converter = new rcube_html2text($m[1], false, true, 0); // $converter = new rcube_html2text($m[1], false, true, 0);
// $object['description'] = rtrim($converter->get_text()); // $object['description'] = rtrim($converter->get_text());
$object['description'] = preg_replace('!<br(\s+/)>!', "\n", $m[1]); $object['description'] = preg_replace('!<br(\s+/)>!', "\n", $m[1]);
$is_html = false;
} }
} }
// Add proper HTML header, otherwise Kontact renders it as plain text
if ($is_html) {
$object['description'] = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">'."\n" .
str_replace('<head>', '<head><meta name="qrichtext" content="1" />', $object['description']);
}
// copy meta data (starting with _) from old object // copy meta data (starting with _) from old object
foreach ((array)$old as $key => $val) { foreach ((array)$old as $key => $val) {
if (!isset($object[$key]) && $key[0] == '_') if (!isset($object[$key]) && $key[0] == '_')
@ -461,13 +517,13 @@ class kolab_notes extends rcube_plugin
. '<meta http-equiv="Content-Type" content="text/html; charset='.RCUBE_CHARSET.'" />' . '<meta http-equiv="Content-Type" content="text/html; charset='.RCUBE_CHARSET.'" />'
. '</head><body>' . $html . '</body></html>'; . '</head><body>' . $html . '</body></html>';
// clean HTML with washhtml by Frederic Motte // clean HTML with washtml by Frederic Motte
$wash_opts = array( $wash_opts = array(
'show_washed' => false, 'show_washed' => false,
'allow_remote' => 1, 'allow_remote' => 1,
'charset' => RCUBE_CHARSET, 'charset' => RCUBE_CHARSET,
'html_elements' => array('html', 'body', 'link'), 'html_elements' => array('html', 'head', 'meta', 'body', 'link'),
'html_attribs' => array('rel', 'type'), 'html_attribs' => array('rel', 'type', 'name', 'http-equiv'),
); );
// initialize HTML washer // initialize HTML washer
@ -476,7 +532,7 @@ class kolab_notes extends rcube_plugin
//$washer->add_callback('form', 'rcmail_washtml_callback'); //$washer->add_callback('form', 'rcmail_washtml_callback');
//$washer->add_callback('style', '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 = rcube_charset::clean($html);
$html = $washer->wash($html); $html = $washer->wash($html);
@ -486,6 +542,6 @@ class kolab_notes extends rcube_plugin
return $html; return $html;
} }
} }

View file

@ -67,7 +67,7 @@ class kolab_notes_ui
$settings['editor'] = array( $settings['editor'] = array(
'lang' => $lang, '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')), 'spellcheck' => intval($this->rc->config->get('enable_spellcheck')),
'spelldict' => intval($this->rc->config->get('spellcheck_dictionary')) 'spelldict' => intval($this->rc->config->get('spellcheck_dictionary'))
); );

View file

@ -15,3 +15,4 @@ $labels['changed'] = 'Last Modified';
$labels['savingdata'] = 'Saving data...'; $labels['savingdata'] = 'Saving data...';
$labels['recordnotfound'] = 'Record not found'; $labels['recordnotfound'] = 'Record not found';
$labels['entertitle'] = 'Please enter a title for this note!'; $labels['entertitle'] = 'Please enter a title for this note!';
$labels['deletenotesconfirm'] = 'Do you really want to delete the selected notes?';

View file

@ -43,11 +43,11 @@ function rcube_kolab_notes_ui(settings)
{ {
// register button commands // register button commands
rcmail.register_command('createnote', function(){ edit_note(null, 'new'); }, false); 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-edit', function(){ list_edit_dialog(me.selected_list); }, false);
rcmail.register_command('list-remove', function(){ list_remove(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('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('search', quicksearch, true);
rcmail.register_command('reset-search', reset_search, true); rcmail.register_command('reset-search', reset_search, true);
@ -87,6 +87,8 @@ function rcube_kolab_notes_ui(settings)
else { else {
reset_view(); reset_view();
} }
rcmail.enable_command('delete', me.notebooks[me.selected_list] && me.notebooks[me.selected_list].editable && list.selection.length > 0);
}) })
.init(); .init();
} }
@ -293,7 +295,7 @@ function rcube_kolab_notes_ui(settings)
tr = noteslist.rows[id].obj; tr = noteslist.rows[id].obj;
note = notesdata[id]; note = notesdata[id];
match = note.categories && note.categories.length; 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) if ($.inArray(tagsfilter[i], note.categories) < 0)
match = false; match = false;
} }
@ -306,7 +308,8 @@ function rcube_kolab_notes_ui(settings)
} }
if (me.selected_note && me.selected_note.uid == note.uid && !match) { 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(); }); .on('click', function(){ $('.tagline .placeholder').hide(); });
me.selected_note = data; 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'); var html, node, editor = tinyMCE.get('notecontent');
if (editor) { if (editor) {
@ -495,12 +499,25 @@ function rcube_kolab_notes_ui(settings)
function update_note(data) function update_note(data)
{ {
data.id = rcmail.html_identifier_encode(data.uid); data.id = rcmail.html_identifier_encode(data.uid);
var row, is_new = notesdata[data.id] == undefined
notesdata[data.id] = data; notesdata[data.id] = data;
render_note(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 // update list item
var row = noteslist.rows[data.id]; else if (row = noteslist.rows[data.id]) {
if (row) {
$('.title', row.obj).html(Q(data.title)); $('.title', row.obj).html(Q(data.title));
$('.date', row.obj).html(Q(data.changed || '')); $('.date', row.obj).html(Q(data.changed || ''));
// TODO: move to top // TODO: move to top
@ -515,11 +532,10 @@ function rcube_kolab_notes_ui(settings)
function reset_view() function reset_view()
{ {
me.selected_note = null; me.selected_note = null;
noteslist.clear_selection();
$('.notetitle', rcmail.gui_objects.noteviewtitle).val(''); $('.notetitle', rcmail.gui_objects.noteviewtitle).val('');
$('.tagline, .dates', rcmail.gui_objects.noteviewtitle).hide(); $('.tagline, .dates', rcmail.gui_objects.noteviewtitle).hide();
$(rcmail.gui_objects.noteseditform).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() function delete_note()
{ {
if (!me.selected_note) { if (!noteslist.selection.length) {
return false; return false;
} }
alert(me.selected_note.title) if (confirm(rcmail.gettext('deletenotesconfirm','kolab_notes'))) {
reset_view(); 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();
}
} }
} }

View file

@ -10,6 +10,18 @@
* See http://creativecommons.org/licenses/by-sa/3.0/ for details. * 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 { .notesview #sidebar {
position: absolute; position: absolute;
top: 42px; top: 42px;
@ -28,15 +40,8 @@
} }
.notesview #notestoolbar a.button.createnote { .notesview #notestoolbar a.button.createnote {
background-image: url('sprites.png');
} background-position: center -54px;
.notesview #taskbar a.button-notes span.button-inner {
}
.notesview #taskbar a.button-notes.button-selected span.button-inner {
} }
.notesview #quicksearchbar { .notesview #quicksearchbar {
@ -146,7 +151,7 @@
} }
.notesview #notedetailstitle { .notesview #notedetailstitle {
height: 68px; height: auto;
} }
.notesview #notedetailstitle .tagedit-list, .notesview #notedetailstitle .tagedit-list,
@ -180,7 +185,8 @@
} }
.notesview #notedetailstitle .dates { .notesview #notedetailstitle .dates {
margin-top: 0; margin-top: 4px;
margin-bottom: 4px;
} }
.notesview #notedetailstitle .tagline { .notesview #notedetailstitle .tagline {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB