diff options
Diffstat (limited to 'client/components/main')
-rw-r--r-- | client/components/main/editor.jade | 3 | ||||
-rwxr-xr-x | client/components/main/editor.js | 9 | ||||
-rw-r--r-- | client/components/main/header.jade | 73 | ||||
-rw-r--r-- | client/components/main/header.js | 5 | ||||
-rw-r--r-- | client/components/main/header.styl | 4 | ||||
-rw-r--r-- | client/components/main/layouts.jade | 59 | ||||
-rw-r--r-- | client/components/main/layouts.js | 115 | ||||
-rw-r--r-- | client/components/main/layouts.styl | 123 | ||||
-rw-r--r-- | client/components/main/popup.styl | 3 |
9 files changed, 330 insertions, 64 deletions
diff --git a/client/components/main/editor.jade b/client/components/main/editor.jade index 31f533e6..dbd61715 100644 --- a/client/components/main/editor.jade +++ b/client/components/main/editor.jade @@ -1,5 +1,6 @@ template(name="editor") textarea.editor( + dir="auto" class="{{class}}" id=id autofocus=autofocus @@ -7,7 +8,7 @@ template(name="editor") +Template.contentBlock template(name="viewer") - .viewer + .viewer(dir="auto") +mentions +markdown {{> UI.contentBlock }} diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 888fbe00..88d8abf0 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -36,13 +36,18 @@ import sanitizeXss from 'xss'; const at = HTML.CharRef({html: '@', str: '@'}); Blaze.Template.registerHelper('mentions', new Template('mentions', function() { const view = this; + let content = Blaze.toHTML(view.templateContentBlock); const currentBoard = Boards.findOne(Session.get('currentBoard')); + if (!currentBoard) + return HTML.Raw(sanitizeXss(content)); const knowedUsers = currentBoard.members.map((member) => { - member.username = Users.findOne(member.userId).username; + const u = Users.findOne(member.userId); + if(u){ + member.username = u.username; + } return member; }); const mentionRegex = /\B@([\w.]*)/gi; - let content = Blaze.toHTML(view.templateContentBlock); let currentMention; while ((currentMention = mentionRegex.exec(content)) !== null) { diff --git a/client/components/main/header.jade b/client/components/main/header.jade index dd071b3e..75e84c0c 100644 --- a/client/components/main/header.jade +++ b/client/components/main/header.jade @@ -4,39 +4,38 @@ template(name="header") list all starred boards with a link to go there. This is inspired by the Reddit "subreddit" bar. The first link goes to the boards page. - unless isSandstorm - if currentUser - #header-quick-access(class=currentBoard.colorClass) - if isMiniScreen - ul - li - a(href="{{pathFor 'home'}}") - span.fa.fa-home + if currentUser + #header-quick-access(class=currentBoard.colorClass) + if isMiniScreen + ul + li + a(href="{{pathFor 'home'}}") + span.fa.fa-home - if currentList - each currentBoard.lists - li(class="{{#if $.Session.equals 'currentList' _id}}current{{/if}}") - a.js-select-list - = title - #header-new-board-icon - else - ul - li - a(href="{{pathFor 'home'}}") - span.fa.fa-home - | {{_ 'all-boards'}} - each currentUser.starredBoards - li.separator - - li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}") - a(href="{{pathFor 'board' id=_id slug=slug}}") + if currentList + each currentBoard.lists + li(class="{{#if $.Session.equals 'currentList' _id}}current{{/if}}") + a.js-select-list = title - else - li.current {{_ 'quick-access-description'}} + #header-new-board-icon + else + ul + li + a(href="{{pathFor 'home'}}") + span.fa.fa-home + | {{_ 'all-boards'}} + each currentUser.starredBoards + li.separator - + li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}") + a(href="{{pathFor 'board' id=_id slug=slug}}") + = title + else + li.current {{_ 'quick-access-description'}} - a#header-new-board-icon.js-create-board - i.fa.fa-plus(title="Create a new board") + a#header-new-board-icon.js-create-board + i.fa.fa-plus(title="Create a new board") - +headerUserBar + +headerUserBar #header(class=currentBoard.colorClass) //- @@ -46,17 +45,16 @@ template(name="header") #header-main-bar(class="{{#if wrappedHeader}}wrapper{{/if}}") +Template.dynamic(template=headerBar) - unless hideLogo + //unless hideLogo + //- On sandstorm, the logo shouldn't be clickable, because we only have one page/document on it, and we don't want to see the home page containing the list of all boards. - if isSandstorm - .wekan-logo - img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan") - else - a.wekan-logo(href="{{pathFor 'home'}}" title="{{_ 'header-logo-title'}}") - img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan") + + // unless currentSetting.hideLogo + // a.wekan-logo(href="{{pathFor 'home'}}" title="{{_ 'header-logo-title'}}") + // img(src="{{pathFor '/logo-header.png'}}" alt="") if appIsOffline +offlineWarning @@ -66,7 +64,8 @@ template(name="header") .announcement p i.fa.fa-bullhorn - | #{announcement} + +viewer + | #{announcement} i.fa.fa-times-circle.js-close-announcement template(name="offlineWarning") diff --git a/client/components/main/header.js b/client/components/main/header.js index 7fbc5716..c05b1c3c 100644 --- a/client/components/main/header.js +++ b/client/components/main/header.js @@ -1,11 +1,16 @@ Meteor.subscribe('user-admin'); Meteor.subscribe('boards'); +Meteor.subscribe('setting'); Template.header.helpers({ wrappedHeader() { return !Session.get('currentBoard'); }, + currentSetting() { + return Settings.findOne(); + }, + hideLogo() { return Utils.isMiniScreen() && Session.get('currentBoard'); }, diff --git a/client/components/main/header.styl b/client/components/main/header.styl index f9455f8e..e3c7618d 100644 --- a/client/components/main/header.styl +++ b/client/components/main/header.styl @@ -188,8 +188,6 @@ width: 100% padding: 10px 0px z-index: 30 - position: absolute - bottom: 0px ul width: calc(100% - 60px) @@ -218,7 +216,7 @@ position: absolute right: 0px padding: 10px - margin: -10px + margin: -10px 0 -10px -10px .announcement, .offline-warning diff --git a/client/components/main/layouts.jade b/client/components/main/layouts.jade index 4d76aabb..9543c5c5 100644 --- a/client/components/main/layouts.jade +++ b/client/components/main/layouts.jade @@ -1,7 +1,6 @@ head - title Wekan - meta(name="viewport" - content="maximum-scale=1.0,width=device-width,initial-scale=1.0,user-scalable=0") + title + meta(name="viewport" content="maximum-scale=1.0,width=device-width,initial-scale=1.0,user-scalable=0") meta(http-equiv="X-UA-Compatible" content="IE=edge") //- XXX We should use pathFor in the following `href` to support the case where the application is deployed with a path prefix, but it seems to be @@ -9,34 +8,47 @@ head 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") template(name="userFormsLayout") section.auth-layout - h1.at-form-landing-logo - img(src="{{pathFor '/wekan-logo.png'}}" alt="Wekan") section.auth-dialog - +Template.dynamic(template=content) - div.at-form-lang - select.select-lang.js-userform-set-language - each languages - if isCurrentLanguage - option(value="{{tag}}" selected="selected") {{name}} - else - option(value="{{tag}}") {{name}} + if isLoading + +loader + else + +Template.dynamic(template=content) + if currentSetting.displayAuthenticationMethod + +connectionMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod) + div.at-form-lang + select.select-lang.js-userform-set-language + each languages + if isCurrentLanguage + option(value="{{tag}}" selected="selected") {{name}} + else + option(value="{{tag}}") {{name}} template(name="defaultLayout") +header #content + | {{{afterBodyStart}}} +Template.dynamic(template=content) + | {{{beforeBodyEnd}}} if (Modal.isOpen) #modal .overlay - .modal-content - a.modal-close-btn.js-close-modal - i.fa.fa-times-thin - +Template.dynamic(template=Modal.getHeaderName) - +Template.dynamic(template=Modal.getTemplateName) + if (Modal.isWide) + .modal-content-wide.modal-container + a.modal-close-btn.js-close-modal + i.fa.fa-times-thin + +Template.dynamic(template=Modal.getHeaderName) + +Template.dynamic(template=Modal.getTemplateName) + else + .modal-content.modal-container + a.modal-close-btn.js-close-modal + i.fa.fa-times-thin + +Template.dynamic(template=Modal.getHeaderName) + +Template.dynamic(template=Modal.getTemplateName) template(name="notFound") +message(label='page-not-found') @@ -47,3 +59,14 @@ template(name="message") unless currentUser with(pathFor route='atSignIn') p {{{_ 'page-maybe-private' this}}} + +template(name="loader") + h1.loadingText {{_ 'loading'}} + .lds-roller + div + div + div + div + div + div + div diff --git a/client/components/main/layouts.js b/client/components/main/layouts.js index f12718a7..d5113a25 100644 --- a/client/components/main/layouts.js +++ b/client/components/main/layouts.js @@ -6,7 +6,36 @@ const i18nTagToT9n = (i18nTag) => { return i18nTag; }; +const validator = { + set(obj, prop, value) { + if (prop === 'state' && value !== 'signIn') { + $('.at-form-authentication').hide(); + } else if (prop === 'state' && value === 'signIn') { + $('.at-form-authentication').show(); + } + // The default behavior to store the value + obj[prop] = value; + // Indicate success + return true; + }, +}; + +Template.userFormsLayout.onCreated(function() { + const instance = this; + instance.currentSetting = new ReactiveVar(); + instance.isLoading = new ReactiveVar(false); + + Meteor.subscribe('setting', { + onReady() { + instance.currentSetting.set(Settings.findOne()); + return this.stop(); + }, + }); +}); + Template.userFormsLayout.onRendered(() => { + AccountsTemplates.state.form.keys = new Proxy(AccountsTemplates.state.form.keys, validator); + const i18nTag = navigator.language; if (i18nTag) { T9n.setLanguage(i18nTagToT9n(i18nTag)); @@ -15,6 +44,22 @@ Template.userFormsLayout.onRendered(() => { }); Template.userFormsLayout.helpers({ + currentSetting() { + return Template.instance().currentSetting.get(); + }, + + isLoading() { + return Template.instance().isLoading.get(); + }, + + afterBodyStart() { + return currentSetting.customHTMLafterBodyStart; + }, + + beforeBodyEnd() { + return currentSetting.customHTMLbeforeBodyEnd; + }, + languages() { return _.map(TAPi18n.getLanguages(), (lang, code) => { const tag = code; @@ -47,6 +92,15 @@ Template.userFormsLayout.events({ T9n.setLanguage(i18nTagToT9n(i18nTag)); evt.preventDefault(); }, + 'click #at-btn'(event, instance) { + if (FlowRouter.getRouteName() === 'atSignIn') { + instance.isLoading.set(true); + authentication(event, instance) + .then(() => { + instance.isLoading.set(false); + }); + } + }, }); Template.defaultLayout.events({ @@ -54,3 +108,64 @@ Template.defaultLayout.events({ Modal.close(); }, }); + +async function authentication(event, instance) { + const match = $('#at-field-username_and_email').val(); + const password = $('#at-field-password').val(); + + if (!match || !password) return undefined; + + const result = await getAuthenticationMethod(instance.currentSetting.get(), match); + + if (result === 'password') return undefined; + + // Stop submit #at-pwd-form + event.preventDefault(); + event.stopImmediatePropagation(); + + switch (result) { + case 'ldap': + return new Promise((resolve) => { + Meteor.loginWithLDAP(match, password, function() { + resolve(FlowRouter.go('/')); + }); + }); + + case 'cas': + return new Promise((resolve) => { + Meteor.loginWithCas(match, password, function() { + resolve(FlowRouter.go('/')); + }); + }); + + default: + return undefined; + } +} + +function getAuthenticationMethod({displayAuthenticationMethod, defaultAuthenticationMethod}, match) { + if (displayAuthenticationMethod) { + return $('.select-authentication').val(); + } + return getUserAuthenticationMethod(defaultAuthenticationMethod, match); +} + +function getUserAuthenticationMethod(defaultAuthenticationMethod, match) { + return new Promise((resolve) => { + try { + Meteor.subscribe('user-authenticationMethod', match, { + onReady() { + const user = Users.findOne(); + + const authenticationMethod = user + ? user.authenticationMethod + : defaultAuthenticationMethod; + + resolve(authenticationMethod); + }, + }); + } catch(error) { + resolve(defaultAuthenticationMethod); + } + }); +} diff --git a/client/components/main/layouts.styl b/client/components/main/layouts.styl index a79ff337..46ee720c 100644 --- a/client/components/main/layouts.styl +++ b/client/components/main/layouts.styl @@ -62,6 +62,23 @@ body float: right font-size: 24px + .modal-content-wide + width: 800px + min-height: 0px + margin: 42px auto + padding: 12px + border-radius: 4px + background: darken(white, 13%) + z-index: 110 + + h2 + margin-bottom: 25px + + .modal-close-btn + display: block + float: right + font-size: 24px + h1 font-size: 22px line-height: 1.2em @@ -273,7 +290,7 @@ kbd // Implement a thiner close icon as suggested in // https://github.com/FortAwesome/Font-Awesome/issues/1540#issuecomment-68689950 .fa.fa-times-thin:before - content: '\00d7'; + content: '\00d7' .fa.fa-globe.colorful, .fa.fa-bell.colorful color: #4caf50 @@ -368,8 +385,8 @@ a @media screen and (max-width: 800px) #content - margin: 1px 0px 49px 0px - height: calc(100% - 96px) + margin: 1px 0px 0px 0px + height: calc(100% - 0px) > .wrapper margin-top: 0px @@ -382,3 +399,103 @@ a height: 37px margin: 8px 10px 0 0 width: 50px + +.select-authentication + width: 100% + +.auth-layout + display: flex + flex-direction: column + align-items: center + justify-content: center + height: 100% + + .auth-dialog + margin: 0 !important + +.loadingText + text-align: center + +.lds-roller + display: block + margin: auto + position: relative + width: 64px + height: 64px + + div + animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite + transform-origin: 32px 32px + + div:after + content: " " + display: block + position: absolute + width: 6px + height: 6px + border-radius: 50% + background: #dedede + margin: -3px 0 0 -3px + + div:nth-child(1) + animation-delay: -0.036s + + div:nth-child(1):after + top: 50px + left: 50px + + div:nth-child(2) + animation-delay: -0.072s + + div:nth-child(2):after + top: 54px + left: 45px + + div:nth-child(3) + animation-delay: -0.108s + + div:nth-child(3):after + top: 57px + left: 39px + + div:nth-child(4) + animation-delay: -0.144s + + div:nth-child(4):after + top: 58px + left: 32px + + div:nth-child(5) + animation-delay: -0.18s + + div:nth-child(5):after + top: 57px + left: 25px + + div:nth-child(6) + animation-delay: -0.216s + + div:nth-child(6):after + top: 54px + left: 19px + + div:nth-child(7) + animation-delay: -0.252s + + div:nth-child(7):after + top: 50px + left: 14px + + div:nth-child(8) + animation-delay: -0.288s + + div:nth-child(8):after + top: 45px + left: 10px + +@keyframes lds-roller + 0% + transform: rotate(0deg) + + 100% + transform: rotate(360deg) diff --git a/client/components/main/popup.styl b/client/components/main/popup.styl index b7c9e264..ff00eef3 100644 --- a/client/components/main/popup.styl +++ b/client/components/main/popup.styl @@ -33,6 +33,9 @@ $popupWidth = 300px textarea height: 72px + form a span + padding: 0 0.5rem + .header height: 36px position: relative |