mirror of
https://github.com/wekan/wekan.git
synced 2025-09-22 01:50:48 +02:00
Add Feature: Comments can be richer (can support some safe HTML tags)
This commit is contained in:
parent
f06098f3dd
commit
e3e504310a
3 changed files with 129 additions and 6 deletions
|
@ -94,3 +94,4 @@ lamhieu:unblock
|
||||||
meteorhacks:aggregate@1.3.0
|
meteorhacks:aggregate@1.3.0
|
||||||
wekan-markdown
|
wekan-markdown
|
||||||
konecty:mongo-counter
|
konecty:mongo-counter
|
||||||
|
summernote:summernote
|
||||||
|
|
|
@ -168,6 +168,7 @@ standard-minifier-css@1.5.3
|
||||||
standard-minifier-js@2.4.1
|
standard-minifier-js@2.4.1
|
||||||
staringatlights:fast-render@3.2.0
|
staringatlights:fast-render@3.2.0
|
||||||
staringatlights:inject-data@2.3.0
|
staringatlights:inject-data@2.3.0
|
||||||
|
summernote:summernote@0.8.1
|
||||||
tap:i18n@1.8.2
|
tap:i18n@1.8.2
|
||||||
templates:tabs@2.3.0
|
templates:tabs@2.3.0
|
||||||
templating@1.3.2
|
templating@1.3.2
|
||||||
|
@ -175,6 +176,7 @@ templating-compiler@1.3.3
|
||||||
templating-runtime@1.3.2
|
templating-runtime@1.3.2
|
||||||
templating-tools@1.1.2
|
templating-tools@1.1.2
|
||||||
tracker@1.2.0
|
tracker@1.2.0
|
||||||
|
twbs:bootstrap@3.3.6
|
||||||
ui@1.0.13
|
ui@1.0.13
|
||||||
underscore@1.0.10
|
underscore@1.0.10
|
||||||
url@1.2.0
|
url@1.2.0
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
Template.editor.onRendered(() => {
|
Template.editor.onRendered(() => {
|
||||||
const $textarea = this.$('textarea');
|
const textareaSelector = 'textarea';
|
||||||
|
const disableRicherEditor = Meteor.settings.public.NO_RICHER_EDITOR;
|
||||||
autosize($textarea);
|
const mentions = [
|
||||||
|
|
||||||
$textarea.escapeableTextComplete([
|
|
||||||
// User mentions
|
// User mentions
|
||||||
{
|
{
|
||||||
match: /\B@([\w.]*)$/,
|
match: /\B@([\w.]*)$/,
|
||||||
|
@ -27,7 +25,129 @@ Template.editor.onRendered(() => {
|
||||||
},
|
},
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
if (!disableRicherEditor) {
|
||||||
|
const isSmall = Utils.isMiniScreen();
|
||||||
|
const toolbar = isSmall
|
||||||
|
? [
|
||||||
|
['font', ['bold', 'underline']],
|
||||||
|
['fontsize', ['fontsize']],
|
||||||
|
['color', ['color']],
|
||||||
|
['table', ['table']],
|
||||||
|
['view', ['fullscreen']],
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
['style', ['style']],
|
||||||
|
['font', ['bold', 'underline', 'clear']],
|
||||||
|
['fontsize', ['fontsize']],
|
||||||
|
['fontname', ['fontname']],
|
||||||
|
['color', ['color']],
|
||||||
|
['para', ['ul', 'ol', 'paragraph']],
|
||||||
|
['table', ['table']],
|
||||||
|
//['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||||
|
['insert', ['link', 'picture']],
|
||||||
|
['view', ['fullscreen', 'help']],
|
||||||
|
];
|
||||||
|
const cleanPastedHTML = function(input) {
|
||||||
|
const badTags = [
|
||||||
|
'style',
|
||||||
|
'script',
|
||||||
|
'applet',
|
||||||
|
'embed',
|
||||||
|
'noframes',
|
||||||
|
'noscript',
|
||||||
|
'meta',
|
||||||
|
'link',
|
||||||
|
'button',
|
||||||
|
'form',
|
||||||
|
].join('|');
|
||||||
|
const badPatterns = new RegExp(
|
||||||
|
`(?:${[
|
||||||
|
`<(${badTags})s*[^>][\\s\\S]*?<\\/\\1>`,
|
||||||
|
`<(${badTags})[^>]*?\\/>`,
|
||||||
|
].join('|')})`,
|
||||||
|
'gi',
|
||||||
|
);
|
||||||
|
let output = input;
|
||||||
|
// remove bad Tags
|
||||||
|
output = output.replace(badPatterns, '');
|
||||||
|
// remove attributes ' style="..."'
|
||||||
|
const badAttributes = new RegExp(
|
||||||
|
`(?:${[
|
||||||
|
'on\\S+=([\'"]?).*?\\1',
|
||||||
|
'href=([\'"]?)javascript:.*?\\2',
|
||||||
|
'style=([\'"]?).*?\\3',
|
||||||
|
'target=\\S+',
|
||||||
|
].join('|')})`,
|
||||||
|
'gi',
|
||||||
|
);
|
||||||
|
output = output.replace(badAttributes, '');
|
||||||
|
output = output.replace(/(<a )/gi, '$1target=_ '); // always to new target
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
const editor = '.editor';
|
||||||
|
const selectors = [
|
||||||
|
`.js-new-comment-form ${editor}`,
|
||||||
|
`.js-edit-comment ${editor}`,
|
||||||
|
].join(','); // only new comment and edit comment
|
||||||
|
$(selectors).summernote({
|
||||||
|
callbacks: {
|
||||||
|
onInit(object) {
|
||||||
|
const jEditor = object && object.editor;
|
||||||
|
const toolbar = object && object.toolbar;
|
||||||
|
if (jEditor !== undefined) {
|
||||||
|
jEditor.find('.note-editable').escapeableTextComplete(mentions);
|
||||||
|
}
|
||||||
|
if (toolbar !== undefined) {
|
||||||
|
const fBtn = toolbar.find('.btn-fullscreen');
|
||||||
|
fBtn.on('click', function() {
|
||||||
|
const $this = $(this),
|
||||||
|
isActive = $this.hasClass('active');
|
||||||
|
$('.minicards').toggle(!isActive); // mini card is still showing when editor is in fullscreen mode, we hide here manually
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onPaste() {
|
||||||
|
// clear up unwanted tag info when user pasted in text
|
||||||
|
const thisNote = $(this);
|
||||||
|
const updatePastedText = function(someNote) {
|
||||||
|
const original = someNote.summernote('code');
|
||||||
|
const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
|
||||||
|
someNote.summernote('code', ''); //clear original
|
||||||
|
someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code.
|
||||||
|
};
|
||||||
|
setTimeout(function() {
|
||||||
|
//this kinda sucks, but if you don't do a setTimeout,
|
||||||
|
//the function is called before the text is really pasted.
|
||||||
|
updatePastedText(thisNote);
|
||||||
|
}, 10);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dialogsInBody: true,
|
||||||
|
disableDragAndDrop: true,
|
||||||
|
toolbar,
|
||||||
|
popover: {
|
||||||
|
image: [
|
||||||
|
[
|
||||||
|
'image',
|
||||||
|
['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone'],
|
||||||
|
],
|
||||||
|
['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||||
|
['remove', ['removeMedia']],
|
||||||
|
],
|
||||||
|
table: [
|
||||||
|
['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
|
||||||
|
['delete', ['deleteRow', 'deleteCol', 'deleteTable']],
|
||||||
|
],
|
||||||
|
air: [['color', ['color']], ['font', ['bold', 'underline', 'clear']]],
|
||||||
|
},
|
||||||
|
height: 200,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const $textarea = this.$(textareaSelector);
|
||||||
|
autosize($textarea);
|
||||||
|
$textarea.escapeableTextComplete(mentions);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
import sanitizeXss from 'xss';
|
import sanitizeXss from 'xss';
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue