فهرست منبع

Add Feature: Comments can be richer (can support some safe HTML tags)

Sam X. Chen 6 سال پیش
والد
کامیت
e3e504310a
3فایلهای تغییر یافته به همراه129 افزوده شده و 6 حذف شده
  1. 1 0
      .meteor/packages
  2. 2 0
      .meteor/versions
  3. 126 6
      client/components/main/editor.js

+ 1 - 0
.meteor/packages

@@ -94,3 +94,4 @@ lamhieu:unblock
 meteorhacks:aggregate@1.3.0
 wekan-markdown
 konecty:mongo-counter
+summernote:summernote

+ 2 - 0
.meteor/versions

@@ -168,6 +168,7 @@ standard-minifier-css@1.5.3
 standard-minifier-js@2.4.1
 staringatlights:fast-render@3.2.0
 staringatlights:inject-data@2.3.0
+summernote:summernote@0.8.1
 tap:i18n@1.8.2
 templates:tabs@2.3.0
 templating@1.3.2
@@ -175,6 +176,7 @@ templating-compiler@1.3.3
 templating-runtime@1.3.2
 templating-tools@1.1.2
 tracker@1.2.0
+twbs:bootstrap@3.3.6
 ui@1.0.13
 underscore@1.0.10
 url@1.2.0

+ 126 - 6
client/components/main/editor.js

@@ -1,9 +1,7 @@
 Template.editor.onRendered(() => {
-  const $textarea = this.$('textarea');
-
-  autosize($textarea);
-
-  $textarea.escapeableTextComplete([
+  const textareaSelector = 'textarea';
+  const disableRicherEditor = Meteor.settings.public.NO_RICHER_EDITOR;
+  const mentions = [
     // User mentions
     {
       match: /\B@([\w.]*)$/,
@@ -27,7 +25,129 @@ Template.editor.onRendered(() => {
       },
       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';