Make URLs in plain-text notes clickable in editor (and remove the links again when saving)

This commit is contained in:
Thomas Bruederli 2014-04-01 11:06:58 +02:00
parent 91e3227e64
commit 46cd325a3c
2 changed files with 64 additions and 12 deletions

View file

@ -337,7 +337,7 @@ class kolab_notes extends rcube_plugin
}
// clean HTML contents
if (!empty($note['description']) && preg_match('/<(html|body|div|p|span)(\s+[a-z]|>)/', $note['description'])) {
if (!empty($note['description']) && preg_match('/<(html|body)(\s+[a-z]|>)/', $note['description'], $m) && strpos($note['description'], '</'.$m[1].'>') > 0) {
$note['html'] = $this->_wash_html($note['description']);
}
@ -426,7 +426,7 @@ class kolab_notes extends rcube_plugin
}
// generate new note object from input
$object = $this->_write_preprocess($note, $old);
$object = $this->_write_preprocess($note, $old);# return false;
$saved = $folder->save($object, 'note', $note['uid']);
if (!$saved) {
@ -483,10 +483,10 @@ class kolab_notes extends rcube_plugin
// 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('!<(a|b|i|strong|em|p|span|div|pre|li)(\s+[a-z]|>)!im', $m[1])) {
if (!preg_match('!<(a|b|i|strong|em|p|span|div|pre|li)(\s+[a-z]|>)!im', $m[1], $n) || !strpos($m[1], '</'.$n[1].'>')) {
// $converter = new rcube_html2text($m[1], false, true, 0);
// $object['description'] = rtrim($converter->get_text());
$object['description'] = preg_replace('!<br(\s+/)>!', "\n", $m[1]);
$object['description'] = html_entity_decode(preg_replace('!<br(\s+/)>!', "\n", $m[1]));
$is_html = false;
}
}
@ -529,8 +529,8 @@ class kolab_notes extends rcube_plugin
// initialize HTML washer
$washer = new rcube_washtml($wash_opts);
//$washer->add_callback('form', 'rcmail_washtml_callback');
//$washer->add_callback('style', 'rcmail_washtml_callback');
$washer->add_callback('form', array($this, '_washtml_callback'));
$washer->add_callback('a', array($this, '_washtml_callback'));
// Remove non-UTF8 characters
$html = rcube_charset::clean($html);
@ -543,5 +543,36 @@ class kolab_notes extends rcube_plugin
return $html;
}
/**
* Callback function for washtml cleaning class
*/
public function _washtml_callback($tagname, $attrib, $content, $washtml)
{
switch ($tagname) {
case 'form':
$out = html::div('form', $content);
break;
case 'a':
// strip temporary link tags from plain-text markup
$attrib = html::parse_attrib_string($attrib);
if (!empty($attrib['class']) && strpos($attrib['class'], 'x-templink') !== false) {
// remove link entirely
if (strpos($attrib['href'], html_entity_decode($content)) !== false) {
$out = $content;
break;
}
$attrib['class'] = trim(str_replace('x-templink', '', $attrib['class']));
}
$out = html::a($attrib, $content);
break;
default:
$out = '';
}
return $out;
}
}

View file

@ -151,7 +151,6 @@ function rcube_kolab_notes_ui(settings)
theme_advanced_toolbar_align: 'left',
theme_advanced_buttons3: '',
theme_advanced_statusbar_location: 'none',
// extended_valid_elements: 'font[face|size|color|style],span[id|class|align|style]',
relative_urls: false,
remove_script_host: false,
gecko_spellcheck: true,
@ -165,7 +164,8 @@ function rcube_kolab_notes_ui(settings)
ed.onClick.add(function(ed, e) {
var link = $(e.target).closest('a');
if (link.length && e.shiftKey) {
window.open(link.get(0).href, '_blank');
if (!bw.mz) window.open(link.get(0).href, '_blank');
return false;
}
});
}
@ -187,9 +187,9 @@ function rcube_kolab_notes_ui(settings)
/**
* Quote HTML entities
*/
function Q(html)
function Q(str)
{
return String(html).replace(/&/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return String(str).replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
/**
@ -223,6 +223,7 @@ function rcube_kolab_notes_ui(settings)
function edit_note(uid, action)
{
if (!uid) {
noteslist.clear_selection();
me.selected_note = { list:me.selected_list, uid:null, title:rcmail.gettext('newnote','kolab_notes'), description:'', categories:[] }
render_note(me.selected_note);
}
@ -398,8 +399,11 @@ function rcube_kolab_notes_ui(settings)
var html, node, editor = tinyMCE.get('notecontent');
if (editor) {
html = data.html || data.description;
if (!html.match(/<(html|body|p|div|span)/))
html = '<pre>' + Q(html) + '</pre>';
// convert plain text to HTML and make URLs clickable
if (!data.html || !html.match(/<(html|body)/)) {
html = text2html(html);
}
editor.setContent(html);
node = editor.getContentAreaContainer().childNodes[0];
@ -411,6 +415,23 @@ function rcube_kolab_notes_ui(settings)
$(window).resize();
}
/**
* Convert the given plain text to HTML contents to be displayed in editor
*/
function text2html(str)
{
// simple link parser (similar to rcube_string_replacer class in PHP)
var utf_domain = '[^?&@"\'/\\(\\)\\s\\r\\t\\n]+\\.([^\x00-\x2f\x3b-\x40\x5b-\x60\x7b-\x7f]{2,}|xn--[a-z0-9]{2,})',
url1 = '.:;,', url2 = 'a-z0-9%=#@+?&/_~\\[\\]-',
link_pattern = new RegExp('([hf]t+ps?://|www.)('+utf_domain+'(['+url1+']?['+url2+']+)*)?', 'ig'),
link_replace = function(matches, p1, p2) {
var url = (p1 == 'www.' ? 'http://' : '') + p1 + p2;
return '<a href="' + url + '" class="x-templink">' + p1 + p2 + '</a>';
};
return '<pre>' + Q(str).replace(link_pattern, link_replace) + '</pre>';
}
/**
*
*/