From 2b26bbe78a1a2b8b427963a6c44c3853efdb737e Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Fri, 6 Mar 2020 03:52:12 +0200 Subject: Fix: img tag did not allow width and height. Removed swipebox from markdown editor img tag and updated marked markdown to newest version. Thanks to hradec and xet7 ! Closes #2956 --- client/components/main/editor.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'client/components/main') diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 39c03aa9..272be197 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -57,6 +57,9 @@ const sanitizeXss = (input, options) => { } } } + /* Don't use swipebox on markdown, so that img tag can now use width + * and height parameters. https://github.com/wekan/wekan/issues/2956 + * Previously this was added at https://github.com/wekan/wekan/pull/2593 } else if (tag === 'img') { if (!options.isClosing) { const src = getAttr('src'); @@ -64,6 +67,7 @@ const sanitizeXss = (input, options) => { return ``; } } + */ } return undefined; }, -- cgit v1.2.3-1-g7c22 From 482682e50079d70c5113169020d6834013b57c11 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Mon, 23 Mar 2020 22:29:20 +0200 Subject: SECURITY VULNERABILITY FIX: Fix XSS bug reported today 4 hours ago by Cyb3rjunky. Logged in users could run javascript in input fields. This affects Wekan versions v3.12-v3.84. In [Wekan v3.12](https://github.com/wekan/wekan/blob/master/CHANGELOG.md#v312-2019-08-09-wekan-release) there was [changes for XSS filter to allow inserting images, videos etc on comment WYSIWYG editor](https://github.com/wekan/wekan/pull/2593) so features related to that are now removed. After this fix, Javascript in input fields is not executed. Thanks to Cyb3rjunky and xet7 ! --- client/components/main/editor.js | 215 +++++++++++++-------------------------- 1 file changed, 68 insertions(+), 147 deletions(-) (limited to 'client/components/main') diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 272be197..97a96b8e 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -1,93 +1,7 @@ -import _sanitizeXss from 'xss'; -const ASIS = 'asis'; -const sanitizeXss = (input, options) => { - const defaultAllowedIframeSrc = /^(https:){0,1}\/\/.*?(youtube|vimeo|dailymotion|youku)/i; - const allowedIframeSrcRegex = (function() { - let reg = defaultAllowedIframeSrc; - const SAFE_IFRAME_SRC_PATTERN = - Meteor.settings.public.SAFE_IFRAME_SRC_PATTERN; - try { - if (SAFE_IFRAME_SRC_PATTERN !== undefined) { - reg = new RegExp(SAFE_IFRAME_SRC_PATTERN, 'i'); - } - } catch (e) { - /*eslint no-console: ["error", { allow: ["warn", "error"] }] */ - - console.error('Wrong pattern specified', SAFE_IFRAM_SRC_PATTERN, e); - } - return reg; - })(); - const targetWindow = '_blank'; - const getHtmlDOM = html => { - const i = document.createElement('i'); - i.innerHTML = html; - return i.firstChild; - }; - options = { - onTag(tag, html, options) { - const htmlDOM = getHtmlDOM(html); - const getAttr = attr => { - return htmlDOM && attr && htmlDOM.getAttribute(attr); - }; - if (tag === 'iframe') { - const clipCls = 'note-vide-clip'; - if (!options.isClosing) { - const iframeCls = getAttr('class'); - let safe = iframeCls.indexOf(clipCls) > -1; - const src = getAttr('src'); - if (allowedIframeSrcRegex.exec(src)) { - safe = true; - } - if (safe) - return ``; - } else { - // remove tag - return ''; - } - } else if (tag === 'a') { - if (!options.isClosing) { - if (getAttr(ASIS) === 'true') { - // if has a ASIS attribute, don't do anything, it's a member id - return html; - } else { - const href = getAttr('href'); - if (href.match(/^((http(s){0,1}:){0,1}\/\/|\/)/)) { - // a valid url - return ``; - } - } - } - /* Don't use swipebox on markdown, so that img tag can now use width - * and height parameters. https://github.com/wekan/wekan/issues/2956 - * Previously this was added at https://github.com/wekan/wekan/pull/2593 - } else if (tag === 'img') { - if (!options.isClosing) { - const src = getAttr('src'); - if (src) { - return ``; - } - } - */ - } - return undefined; - }, - onTagAttr(tag, name, value) { - if (tag === 'img' && name === 'src') { - if (value && value.substr(0, 5) === 'data:') { - // allow image with dataURI src - return `${name}='${value}'`; - } - } else if (tag === 'a' && name === 'target') { - return `${name}='${targetWindow}'`; // always change a href target to a new window - } - return undefined; - }, - ...options, - }; - return _sanitizeXss(input, options); -}; Template.editor.onRendered(() => { const textareaSelector = 'textarea'; + const enableRicherEditor = + Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR || true; const mentions = [ // User mentions { @@ -98,13 +12,7 @@ Template.editor.onRendered(() => { currentBoard .activeMembers() .map(member => { - const user = Users.findOne(member.userId); - if (user._id === Meteor.userId()) { - return null; - } - const value = user.username; - const username = - value && value.match(/\s+/) ? `"${value}"` : value; + const username = Users.findOne(member.userId).username; return username.includes(term) ? username : null; }) .filter(Boolean), @@ -124,16 +32,15 @@ Template.editor.onRendered(() => { autosize($textarea); $textarea.escapeableTextComplete(mentions); }; - if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) { + if (enableRicherEditor) { const isSmall = Utils.isMiniScreen(); const toolbar = isSmall ? [ ['view', ['fullscreen']], ['table', ['table']], - ['font', ['bold']], - ['color', ['color']], - ['insert', ['video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled + ['font', ['bold', 'underline']], //['fontsize', ['fontsize']], + ['color', ['color']], ] : [ ['style', ['style']], @@ -143,11 +50,47 @@ Template.editor.onRendered(() => { ['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', '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']], // modal popup has issue somehow :( ['view', ['fullscreen', 'help']], ]; - const cleanPastedHTML = sanitizeXss; + 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(/( { } return undefined; }; - let popupShown = false; inputs.each(function(idx, input) { mSummernotes[idx] = $(input).summernote({ placeholder, callbacks: { - onKeydown(e) { - if (popupShown) { - e.preventDefault(); - } - }, - onKeyup(e) { - if (popupShown) { - e.preventDefault(); - } - }, onInit(object) { const originalInput = this; - const setAutocomplete = function(jEditor) { - if (jEditor !== undefined) { - jEditor.escapeableTextComplete(mentions).on({ - 'textComplete:show'() { - popupShown = true; - }, - 'textComplete:hide'() { - popupShown = false; - }, - }); - } - }; - $(originalInput).on('submitted', function() { - // resetCommentInput has been called + $(originalInput).on('input', function() { + // when comment is submitted, the original textarea will be set to '', so shall we if (!this.value) { const sn = getSummernote(this); - sn && sn.summernote('code', ''); + sn && sn.summernote('reset'); + object && object.editingArea.find('.note-placeholder').show(); } }); const jEditor = object && object.editable; const toolbar = object && object.toolbar; - setAutocomplete(jEditor); + if (jEditor !== undefined) { + jEditor.escapeableTextComplete(mentions); + } if (toolbar !== undefined) { const fBtn = toolbar.find('.btn-fullscreen'); fBtn.on('click', function() { @@ -215,6 +138,7 @@ Template.editor.onRendered(() => { }); } }, + onImageUpload(files) { const $summernote = getSummernote(this); if (files && files.length > 0) { @@ -295,7 +219,7 @@ Template.editor.onRendered(() => { const someNote = getSummernote(object); 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('reset'); //clear original someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code. }; setTimeout(function() { @@ -335,6 +259,8 @@ Template.editor.onRendered(() => { } }); +import sanitizeXss from 'xss'; + // XXX I believe we should compute a HTML rendered field on the server that // would handle markdown and user mentions. We can simply have two // fields, one source, and one compiled version (in HTML) and send only the @@ -356,12 +282,11 @@ Blaze.Template.registerHelper( } return member; }); - const mentionRegex = /\B@(?:(?:"([\w.\s]*)")|([\w.]+))/gi; // including space in username + const mentionRegex = /\B@([\w.]*)/gi; let currentMention; while ((currentMention = mentionRegex.exec(content)) !== null) { - const [fullMention, quoteduser, simple] = currentMention; - const username = quoteduser || simple; + const [fullMention, username] = currentMention; const knowedUser = _.findWhere(knowedUsers, { username }); if (!knowedUser) { continue; @@ -380,42 +305,38 @@ Blaze.Template.registerHelper( // `userId` to the popup as usual, and we need to store it in the DOM // using a data attribute. 'data-userId': knowedUser.userId, - [ASIS]: 'true', }, linkValue, ); content = content.replace(fullMention, Blaze.toHTML(link)); } + return HTML.Raw(sanitizeXss(content)); }), ); + Template.viewer.events({ // Viewer sometimes have click-able wrapper around them (for instance to edit // the corresponding text). Clicking a link shouldn't fire these actions, stop // we stop these event at the viewer component level. 'click a'(event, templateInstance) { - let prevent = true; + event.stopPropagation(); + + // XXX We hijack the build-in browser action because we currently don't have + // `_blank` attributes in viewer links, and the transformer function is + // handled by a third party package that we can't configure easily. Fix that + // by using directly `_blank` attribute in the rendered HTML. + event.preventDefault(); + const userId = event.currentTarget.dataset.userid; if (userId) { Popup.open('member').call({ userId }, event, templateInstance); } else { const href = event.currentTarget.href; - const child = event.currentTarget.firstElementChild; - if (child && child.tagName === 'IMG') { - prevent = false; - } else if (href) { + if (href) { window.open(href, '_blank'); } } - if (prevent) { - event.stopPropagation(); - - // XXX We hijack the build-in browser action because we currently don't have - // `_blank` attributes in viewer links, and the transformer function is - // handled by a third party package that we can't configure easily. Fix that - // by using directly `_blank` attribute in the rendered HTML. - event.preventDefault(); - } }, }); -- cgit v1.2.3-1-g7c22 From 12ab8fac5db9c5ac8069d0ca2bca340d6004a25b Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Tue, 24 Mar 2020 11:04:04 +0200 Subject: Fix Rich editor can not be disabled, regression from changes yesterday at Wekan v3.85. Thanks to uusijani, vjrj and xet7 ! Closes #2967, closes #104 --- client/components/main/editor.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'client/components/main') diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 97a96b8e..18b823a2 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -1,7 +1,5 @@ Template.editor.onRendered(() => { const textareaSelector = 'textarea'; - const enableRicherEditor = - Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR || true; const mentions = [ // User mentions { @@ -32,7 +30,7 @@ Template.editor.onRendered(() => { autosize($textarea); $textarea.escapeableTextComplete(mentions); }; - if (enableRicherEditor) { + if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) { const isSmall = Utils.isMiniScreen(); const toolbar = isSmall ? [ -- cgit v1.2.3-1-g7c22 From b9099a8b7ea6f63c79bdcbb871cb993b2cb7e325 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Tue, 24 Mar 2020 20:39:49 +0200 Subject: 1) Fix Pasting text into a card is adding a line before and after (and multiplies by pasting more) by changing paste "p" to "br". 2) Fixes to summernote and markdown comment editors, related to keeping them open when adding comments, having @member mention not close card, and disabling clicking of @member mention. Thanks to xet7 ! Closes #2890 --- client/components/main/editor.jade | 7 +++++- client/components/main/editor.js | 51 ++++++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 6 deletions(-) (limited to 'client/components/main') diff --git a/client/components/main/editor.jade b/client/components/main/editor.jade index dbd61715..5c5454ee 100644 --- a/client/components/main/editor.jade +++ b/client/components/main/editor.jade @@ -1,8 +1,13 @@ template(name="editor") + // With Richer editor is in use, and comment is submitted, + // clear comment form with JQuery Comment at + // client/components/activities/comments.js . Id #summernote is defined + // here at client/components/main/editor.jade where it previously was + // id=id, now it is id="summernote". textarea.editor( dir="auto" class="{{class}}" - id=id + id="summernote" autofocus=autofocus placeholder="{{_ 'comment-placeholder'}}") +Template.contentBlock diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 18b823a2..3f09d284 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -30,7 +30,7 @@ Template.editor.onRendered(() => { autosize($textarea); $textarea.escapeableTextComplete(mentions); }; - if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) { + if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === 'true') { const isSmall = Utils.isMiniScreen(); const toolbar = isSmall ? [ @@ -108,10 +108,37 @@ Template.editor.onRendered(() => { } return undefined; }; + // Prevent @member mentions on Add Comment input field + // from closing card, part 1. + let popupShown = false; inputs.each(function(idx, input) { mSummernotes[idx] = $(input).summernote({ placeholder, + // Prevent @member mentions on Add Comment input field + // from closing card, part 2. + onKeydown(e) { + if (popupShown) { + e.preventDefault(); + } + }, + onKeyup(e) { + if (popupShown) { + e.preventDefault(); + } + }, callbacks: { + // Prevent @member mentions on Add Comment input field + // from closing card, part 3. + onKeydown(e) { + if (popupShown) { + e.preventDefault(); + } + }, + onKeyup(e) { + if (popupShown) { + e.preventDefault(); + } + }, onInit(object) { const originalInput = this; $(originalInput).on('input', function() { @@ -136,7 +163,6 @@ Template.editor.onRendered(() => { }); } }, - onImageUpload(files) { const $summernote = getSummernote(this); if (files && files.length > 0) { @@ -215,6 +241,12 @@ Template.editor.onRendered(() => { const thisNote = this; const updatePastedText = function(object) { const someNote = getSummernote(object); + // Fix Pasting text into a card is adding a line before and after + // (and multiplies by pasting more) by changing paste "p" to "br". + // Fixes https://github.com/wekan/wekan/2890 . + // == Fix Start == + someNote.execCommand('defaultParagraphSeparator', false, 'br'); + // == Fix End == 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('reset'); //clear original @@ -291,11 +323,17 @@ Blaze.Template.registerHelper( } const linkValue = [' ', at, knowedUser.username]; - let linkClass = 'atMention js-open-member'; + //let linkClass = 'atMention js-open-member'; + let linkClass = 'atMention'; if (knowedUser.userId === Meteor.userId()) { linkClass += ' me'; } - const link = HTML.A( + // This @user mention link generation did open same Wekan + // window in new tab, so now A is changed to U so it's + // underlined and there is no link popup. This way also + // text can be selected more easily. + //const link = HTML.A( + const link = HTML.U( { class: linkClass, // XXX Hack. Since we stringify this render function result below with @@ -329,7 +367,10 @@ Template.viewer.events({ const userId = event.currentTarget.dataset.userid; if (userId) { - Popup.open('member').call({ userId }, event, templateInstance); + // Prevent @member mentions on Add Comment input field + // from closing card, part 4. + PopupNoClose.open('member').call({ userId }, event, templateInstance); + event.preventDefault(); } else { const href = event.currentTarget.href; if (href) { -- cgit v1.2.3-1-g7c22 From 9819c9f801128d07374b0703b482bdb83a672297 Mon Sep 17 00:00:00 2001 From: Jonathan Baird Date: Fri, 27 Mar 2020 11:35:03 -0600 Subject: add a notification drawer like trello --- client/components/main/header.jade | 2 ++ client/components/main/header.styl | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'client/components/main') diff --git a/client/components/main/header.jade b/client/components/main/header.jade index 75e84c0c..9a5a6b9b 100644 --- a/client/components/main/header.jade +++ b/client/components/main/header.jade @@ -35,6 +35,8 @@ template(name="header") a#header-new-board-icon.js-create-board i.fa.fa-plus(title="Create a new board") + +notifications + +headerUserBar #header(class=currentBoard.colorClass) diff --git a/client/components/main/header.styl b/client/components/main/header.styl index f77c2aca..632d1535 100644 --- a/client/components/main/header.styl +++ b/client/components/main/header.styl @@ -99,7 +99,7 @@ height: 28px font-size: 12px display: flex - z-index: 17 + z-index: 21 #header-user-bar, #header-new-board-icon, -- cgit v1.2.3-1-g7c22 From 3546d7aa02bc65cf1183cb493adeb543ba51945d Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Tue, 31 Mar 2020 16:56:32 +0300 Subject: Fix Browser always reload the whole page when I change one of the card color. Fixed by making label colors and text again editable. Regression from [Wekan v3.86 2)](https://github.com/wekan/wekan/commit/b9099a8b7ea6f63c79bdcbb871cb993b2cb7e325). Thanks to javen9881 and xet7 ! Closes #2971 --- client/components/main/editor.jade | 7 +------ client/components/main/editor.js | 38 ++++---------------------------------- 2 files changed, 5 insertions(+), 40 deletions(-) (limited to 'client/components/main') diff --git a/client/components/main/editor.jade b/client/components/main/editor.jade index 5c5454ee..dbd61715 100644 --- a/client/components/main/editor.jade +++ b/client/components/main/editor.jade @@ -1,13 +1,8 @@ template(name="editor") - // With Richer editor is in use, and comment is submitted, - // clear comment form with JQuery Comment at - // client/components/activities/comments.js . Id #summernote is defined - // here at client/components/main/editor.jade where it previously was - // id=id, now it is id="summernote". textarea.editor( dir="auto" class="{{class}}" - id="summernote" + id=id autofocus=autofocus placeholder="{{_ 'comment-placeholder'}}") +Template.contentBlock diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 3f09d284..86c0078f 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -30,7 +30,7 @@ Template.editor.onRendered(() => { autosize($textarea); $textarea.escapeableTextComplete(mentions); }; - if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === 'true') { + if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) { const isSmall = Utils.isMiniScreen(); const toolbar = isSmall ? [ @@ -108,37 +108,10 @@ Template.editor.onRendered(() => { } return undefined; }; - // Prevent @member mentions on Add Comment input field - // from closing card, part 1. - let popupShown = false; inputs.each(function(idx, input) { mSummernotes[idx] = $(input).summernote({ placeholder, - // Prevent @member mentions on Add Comment input field - // from closing card, part 2. - onKeydown(e) { - if (popupShown) { - e.preventDefault(); - } - }, - onKeyup(e) { - if (popupShown) { - e.preventDefault(); - } - }, callbacks: { - // Prevent @member mentions on Add Comment input field - // from closing card, part 3. - onKeydown(e) { - if (popupShown) { - e.preventDefault(); - } - }, - onKeyup(e) { - if (popupShown) { - e.preventDefault(); - } - }, onInit(object) { const originalInput = this; $(originalInput).on('input', function() { @@ -163,6 +136,7 @@ Template.editor.onRendered(() => { }); } }, + onImageUpload(files) { const $summernote = getSummernote(this); if (files && files.length > 0) { @@ -323,8 +297,7 @@ Blaze.Template.registerHelper( } const linkValue = [' ', at, knowedUser.username]; - //let linkClass = 'atMention js-open-member'; - let linkClass = 'atMention'; + let linkClass = 'atMention js-open-member'; if (knowedUser.userId === Meteor.userId()) { linkClass += ' me'; } @@ -367,10 +340,7 @@ Template.viewer.events({ const userId = event.currentTarget.dataset.userid; if (userId) { - // Prevent @member mentions on Add Comment input field - // from closing card, part 4. - PopupNoClose.open('member').call({ userId }, event, templateInstance); - event.preventDefault(); + Popup.open('member').call({ userId }, event, templateInstance); } else { const href = event.currentTarget.href; if (href) { -- cgit v1.2.3-1-g7c22 From 033d6710470b2ecd7a0ec0b2f0741ff459e68b32 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Tue, 31 Mar 2020 23:17:58 +0300 Subject: Fix richer editor submit did not clear edit area. Thanks to xet7 ! --- client/components/main/editor.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'client/components/main') diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 86c0078f..081c6521 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -114,12 +114,11 @@ Template.editor.onRendered(() => { callbacks: { onInit(object) { const originalInput = this; - $(originalInput).on('input', function() { + $(originalInput).on('submitted', function() { // when comment is submitted, the original textarea will be set to '', so shall we if (!this.value) { const sn = getSummernote(this); - sn && sn.summernote('reset'); - object && object.editingArea.find('.note-placeholder').show(); + sn && sn.summernote('code', ''); } }); const jEditor = object && object.editable; @@ -223,7 +222,7 @@ Template.editor.onRendered(() => { // == Fix End == 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('reset'); //clear original + someNote.summernote('code', ''); //clear original someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code. }; setTimeout(function() { @@ -290,7 +289,8 @@ Blaze.Template.registerHelper( let currentMention; while ((currentMention = mentionRegex.exec(content)) !== null) { - const [fullMention, username] = currentMention; + const [fullMention, quoteduser, simple] = currentMention; + const username = quoteduser || simple; const knowedUser = _.findWhere(knowedUsers, { username }); if (!knowedUser) { continue; @@ -330,14 +330,7 @@ Template.viewer.events({ // the corresponding text). Clicking a link shouldn't fire these actions, stop // we stop these event at the viewer component level. 'click a'(event, templateInstance) { - event.stopPropagation(); - - // XXX We hijack the build-in browser action because we currently don't have - // `_blank` attributes in viewer links, and the transformer function is - // handled by a third party package that we can't configure easily. Fix that - // by using directly `_blank` attribute in the rendered HTML. - event.preventDefault(); - + let prevent = true; const userId = event.currentTarget.dataset.userid; if (userId) { Popup.open('member').call({ userId }, event, templateInstance); @@ -347,5 +340,14 @@ Template.viewer.events({ window.open(href, '_blank'); } } + if (prevent) { + event.stopPropagation(); + + // XXX We hijack the build-in browser action because we currently don't have + // `_blank` attributes in viewer links, and the transformer function is + // handled by a third party package that we can't configure easily. Fix that + // by using directly `_blank` attribute in the rendered HTML. + event.preventDefault(); + } }, }); -- cgit v1.2.3-1-g7c22 From b849d4c2ba088554a68d00b87afc249a1d3fa180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Will=20=E4=BF=9D=E5=93=A5?= Date: Mon, 6 Apr 2020 18:16:17 +0800 Subject: Update layouts.js --- client/components/main/layouts.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'client/components/main') diff --git a/client/components/main/layouts.js b/client/components/main/layouts.js index ec4a35cc..a4768900 100644 --- a/client/components/main/layouts.js +++ b/client/components/main/layouts.js @@ -73,6 +73,8 @@ Template.userFormsLayout.helpers({ name = 'Igbo'; } else if (lang.name === 'oc') { name = 'Occitan'; + } else if (lang.name === 'zh-TW') { + name = '繁體中文(台灣)'; } return { tag, name }; }).sort(function(a, b) { -- cgit v1.2.3-1-g7c22 From e0930c32a609813c154acec9b3015ad0b095d462 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 7 Apr 2020 09:43:23 +0000 Subject: Correct zh-TW language name (the right way) --- client/components/main/layouts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'client/components/main') diff --git a/client/components/main/layouts.js b/client/components/main/layouts.js index a4768900..e5c86d76 100644 --- a/client/components/main/layouts.js +++ b/client/components/main/layouts.js @@ -73,7 +73,7 @@ Template.userFormsLayout.helpers({ name = 'Igbo'; } else if (lang.name === 'oc') { name = 'Occitan'; - } else if (lang.name === 'zh-TW') { + } else if (lang.name === '繁体中文(台湾)') { name = '繁體中文(台灣)'; } return { tag, name }; -- cgit v1.2.3-1-g7c22 From edf52bc4382823ed8768251954371094a849213e Mon Sep 17 00:00:00 2001 From: Nico Date: Sun, 12 Apr 2020 00:56:35 +0200 Subject: Public boards overview --- client/components/main/header.jade | 5 +++++ client/components/main/header.styl | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'client/components/main') diff --git a/client/components/main/header.jade b/client/components/main/header.jade index 9a5a6b9b..de7ead93 100644 --- a/client/components/main/header.jade +++ b/client/components/main/header.jade @@ -24,6 +24,11 @@ template(name="header") a(href="{{pathFor 'home'}}") span.fa.fa-home | {{_ 'all-boards'}} + li.separator - + li + a(href="{{pathFor 'public'}}") + span.fa.fa-globe + | {{_ 'public'}} each currentUser.starredBoards li.separator - li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}") diff --git a/client/components/main/header.styl b/client/components/main/header.styl index 632d1535..38ff0560 100644 --- a/client/components/main/header.styl +++ b/client/components/main/header.styl @@ -127,7 +127,7 @@ &.current color: darken(white, 5%) - &:first-child .fa-home + &:first-child .fa-home,&:nth-child(3) .fa-globe margin-right: 5px a.js-create-board -- cgit v1.2.3-1-g7c22 From 3e817257ef6d7a527aaad040cdcdcc642caea3c1 Mon Sep 17 00:00:00 2001 From: salleman Date: Mon, 13 Apr 2020 21:06:27 +0200 Subject: hide password auth with PASSWORD_LOGIN_ENABLED variable --- client/components/main/layouts.js | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'client/components/main') diff --git a/client/components/main/layouts.js b/client/components/main/layouts.js index e5c86d76..23960a9e 100644 --- a/client/components/main/layouts.js +++ b/client/components/main/layouts.js @@ -31,6 +31,11 @@ Template.userFormsLayout.onCreated(function() { return this.stop(); }, }); + Meteor.call('isPasswordDisabled', (_, result) => { + if (result) { + $('.at-pwd-form').hide(); + } + }); }); Template.userFormsLayout.onRendered(() => { -- cgit v1.2.3-1-g7c22 From 1389ed0fce80c622220104e6c0e693ac52ce2177 Mon Sep 17 00:00:00 2001 From: Allemand <37148072+salleman33@users.noreply.github.com> Date: Tue, 14 Apr 2020 08:48:51 +0200 Subject: Update layouts.js --- client/components/main/layouts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'client/components/main') diff --git a/client/components/main/layouts.js b/client/components/main/layouts.js index 23960a9e..83678e73 100644 --- a/client/components/main/layouts.js +++ b/client/components/main/layouts.js @@ -31,7 +31,7 @@ Template.userFormsLayout.onCreated(function() { return this.stop(); }, }); - Meteor.call('isPasswordDisabled', (_, result) => { + Meteor.call('isPasswordLoginDisabled', (_, result) => { if (result) { $('.at-pwd-form').hide(); } -- cgit v1.2.3-1-g7c22 From 8e14459cff4da1391f536dfbc6441abb21e9c215 Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Wed, 22 Apr 2020 14:44:08 +0200 Subject: Implement option to change the first day of week in user settings Implements #2535. --- client/components/main/popup.styl | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'client/components/main') diff --git a/client/components/main/popup.styl b/client/components/main/popup.styl index 023cba3d..f1db3927 100644 --- a/client/components/main/popup.styl +++ b/client/components/main/popup.styl @@ -135,6 +135,10 @@ $popupWidth = 300px margin-bottom: 8px .pop-over-list + li + display: block + clear: both + li > a clear: both cursor: pointer -- cgit v1.2.3-1-g7c22 From c1287248a6a4975133790deab6747530e5ece3cb Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Wed, 22 Apr 2020 00:24:32 +0200 Subject: Mobile device layout fix --- client/components/main/popup.styl | 1 + 1 file changed, 1 insertion(+) (limited to 'client/components/main') diff --git a/client/components/main/popup.styl b/client/components/main/popup.styl index f1db3927..b4815ca6 100644 --- a/client/components/main/popup.styl +++ b/client/components/main/popup.styl @@ -320,6 +320,7 @@ $popupWidth = 300px input[type="file"] margin: 4px 0 12px width: 100% + box-sizing: border-box .pop-over-list li > a -- cgit v1.2.3-1-g7c22 From f7a0d15db7ffff9cc395859f8b593b4545b87103 Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Mon, 27 Apr 2020 02:04:46 +0200 Subject: Make sure that the board header buttons fit into one line ...even for devices with 360px width resolution. --- client/components/main/header.styl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'client/components/main') diff --git a/client/components/main/header.styl b/client/components/main/header.styl index 38ff0560..d8093861 100644 --- a/client/components/main/header.styl +++ b/client/components/main/header.styl @@ -175,7 +175,7 @@ .board-header-btn height: 32px line-height: @height - font-size: 16px + font-size: 15px i.fa line-height: 32px -- cgit v1.2.3-1-g7c22 From 8d5adc04645e3e71423f16869f39b8d79969bccd Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Thu, 30 Apr 2020 06:54:48 +0300 Subject: Install Wekan to mobile homescreen icon and use fullscreen PWA. Docs at https://github.com/wekan/wekan/wiki/PWA Thanks to xet7 ! Closes #2879 --- client/components/main/layouts.jade | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'client/components/main') diff --git a/client/components/main/layouts.jade b/client/components/main/layouts.jade index 9543c5c5..08dfc58c 100644 --- a/client/components/main/layouts.jade +++ b/client/components/main/layouts.jade @@ -6,10 +6,16 @@ head where the application is deployed with a path prefix, but it seems to be difficult to do that cleanly with Blaze -- at least without adding extra packages. - link(rel="shortcut icon" href="/wekan-favicon.png") - link(rel="apple-touch-icon" href="/wekan-favicon.png") - link(rel="mask-icon" href="/wekan-logo-150.svg") - link(rel="manifest" href="/wekan-manifest.json") + link(rel="shortcut icon" type="image/x-icon" href="/favicon.ico") + link(rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png") + link(rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png") + link(rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png") + link(rel="manifest" href="/site.webmanifest") + link(rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5") + meta(name="apple-mobile-web-app-title" content="Wekan") + meta(name="application-name" content="Wekan") + meta(name="msapplication-TileColor" content="#00aba9") + meta(name="theme-color" content="#ffffff") template(name="userFormsLayout") section.auth-layout -- cgit v1.2.3-1-g7c22