From 32b794770414818bff96fad0621219e5b960b4fd Mon Sep 17 00:00:00 2001 From: NGPixel Date: Sun, 24 Sep 2017 23:22:33 -0400 Subject: [PATCH] feat: GraphQL translations + login form --- client/js/app.js | 43 +++++++++--------- client/js/components/login.vue | 50 +++++++++++++++++++++ client/js/modules/localization.js | 57 ++++++++++++++++++++++++ client/scss/base/base.scss | 7 ++- client/scss/pages/_login.scss | 7 ++- package.json | 7 ++- server/authentication/azure.js | 1 + server/authentication/facebook.js | 1 + server/authentication/github.js | 1 + server/authentication/google.js | 1 + server/authentication/ldap.js | 1 + server/authentication/local.js | 1 + server/authentication/microsoft.js | 1 + server/authentication/slack.js | 1 + server/master.js | 52 +++------------------ server/modules/graphql.js | 2 + server/modules/localization.js | 52 +++++++++++++++++++++ server/schemas/resolvers-comment.js | 1 - server/schemas/resolvers-document.js | 1 - server/schemas/resolvers-file.js | 1 - server/schemas/resolvers-folder.js | 1 - server/schemas/resolvers-group.js | 1 - server/schemas/resolvers-right.js | 1 - server/schemas/resolvers-setting.js | 1 - server/schemas/resolvers-tag.js | 1 - server/schemas/resolvers-translation.js | 12 +++++ server/schemas/resolvers-user.js | 1 - server/schemas/scalar-date.js | 1 - server/schemas/types.graphql | 6 +++ server/views/auth/login.pug | 30 +------------ server/views/layout.pug | 2 +- yarn.lock | Bin 277299 -> 272761 bytes 32 files changed, 232 insertions(+), 113 deletions(-) create mode 100644 client/js/components/login.vue create mode 100644 client/js/modules/localization.js create mode 100644 server/modules/localization.js create mode 100644 server/schemas/resolvers-translation.js diff --git a/client/js/app.js b/client/js/app.js index 00991909..a42068a1 100644 --- a/client/js/app.js +++ b/client/js/app.js @@ -6,10 +6,14 @@ import Vue from 'vue' import VueResource from 'vue-resource' import VueClipboards from 'vue-clipboards' +import { ApolloClient, createBatchingNetworkInterface } from 'apollo-client' import store from './store' -import i18next from 'i18next' -import i18nextXHR from 'i18next-xhr-backend' -import VueI18Next from '@panter/vue-i18next' + +// ==================================== +// Load Modules +// ==================================== + +import localization from './modules/localization' // ==================================== // Load Helpers @@ -29,6 +33,7 @@ import editorFileComponent from './components/editor-file.vue' import editorVideoComponent from './components/editor-video.vue' import historyComponent from './components/history.vue' import loadingSpinnerComponent from './components/loading-spinner.vue' +import loginComponent from './components/login.vue' import modalCreatePageComponent from './components/modal-create-page.vue' import modalCreateUserComponent from './components/modal-create-user.vue' import modalDeleteUserComponent from './components/modal-delete-user.vue' @@ -49,13 +54,24 @@ import contentViewComponent from './pages/content-view.component.js' import editorComponent from './components/editor.component.js' import sourceViewComponent from './pages/source-view.component.js' +// ==================================== +// Initialize Apollo Client (GraphQL) +// ==================================== + +window.apollo = new ApolloClient({ + networkInterface: createBatchingNetworkInterface({ + uri: window.location.protocol + '//' + window.location.host + siteConfig.path + '/graphql' + }), + connectToDevTools: true +}) + // ==================================== // Initialize Vue Modules // ==================================== Vue.use(VueResource) Vue.use(VueClipboards) -Vue.use(VueI18Next) +Vue.use(localization.VueI18Next) Vue.use(helpers) // ==================================== @@ -76,6 +92,7 @@ Vue.component('editorFile', editorFileComponent) Vue.component('editorVideo', editorVideoComponent) Vue.component('history', historyComponent) Vue.component('loadingSpinner', loadingSpinnerComponent) +Vue.component('login', loginComponent) Vue.component('modalCreatePage', modalCreatePageComponent) Vue.component('modalCreateUser', modalCreateUserComponent) Vue.component('modalDeleteUser', modalDeleteUserComponent) @@ -89,20 +106,6 @@ Vue.component('sourceView', sourceViewComponent) Vue.component('toggle', toggleComponent) Vue.component('tree', treeComponent) -// ==================================== -// Load Localization strings -// ==================================== - -i18next - .use(i18nextXHR) - .init({ - backend: { - loadPath: siteConfig.path + '/js/i18n/{{lng}}.json' - }, - lng: siteConfig.lang, - fallbackLng: siteConfig.lang - }) - document.addEventListener('DOMContentLoaded', ev => { // ==================================== // Notifications @@ -116,13 +119,13 @@ document.addEventListener('DOMContentLoaded', ev => { // Bootstrap Vue // ==================================== - const i18n = new VueI18Next(i18next) + const i18n = localization.init() window.wiki = new Vue({ mixins: [helpers], components: {}, store, i18n, - el: '#root', + el: '#app', methods: { changeTheme(opts) { this.$el.className = `has-stickynav is-primary-${opts.primary} is-alternate-${opts.alt}` diff --git a/client/js/components/login.vue b/client/js/components/login.vue new file mode 100644 index 00000000..3a56be6b --- /dev/null +++ b/client/js/components/login.vue @@ -0,0 +1,50 @@ + + + + diff --git a/client/js/modules/localization.js b/client/js/modules/localization.js new file mode 100644 index 00000000..cf696af1 --- /dev/null +++ b/client/js/modules/localization.js @@ -0,0 +1,57 @@ +import i18next from 'i18next' +import i18nextXHR from 'i18next-xhr-backend' +import i18nextCache from 'i18next-localstorage-cache' +import gql from 'graphql-tag' +import VueI18Next from '@panter/vue-i18next' +import loSet from 'lodash/set' + +/* global siteConfig */ + +module.exports = { + VueI18Next, + init() { + i18next + .use(i18nextXHR) + .use(i18nextCache) + .init({ + backend: { + loadPath: '{{lng}}/{{ns}}', + parse: (data) => data, + ajax: (url, opts, cb, data) => { + let langParams = url.split('/') + console.info(langParams) + window.apollo.query({ + query: gql` + { + translations(locale:"${langParams[0]}", namespace:"${langParams[1]}") { + key + value + } + } + ` + }).then(resp => { + let ns = {} + if (resp.data.translations.length > 0) { + resp.data.translations.forEach(entry => { + loSet(ns, entry.key, entry.value) + }) + } + return cb(ns, {status: '200'}) + }).catch(err => { + console.error(err) + return cb(null, {status: '404'}) + }) + } + }, + cache: { + enabled: true, + expiration: 60 * 60 * 1000 + }, + defaultNS: 'common', + lng: siteConfig.lang, + fallbackLng: siteConfig.lang, + ns: ['common', 'admin', 'auth'] + }) + return new VueI18Next(i18next) + } +} diff --git a/client/scss/base/base.scss b/client/scss/base/base.scss index 6d8a688e..54740abe 100644 --- a/client/scss/base/base.scss +++ b/client/scss/base/base.scss @@ -11,10 +11,15 @@ [v-cloak], .is-hidden { display: none; } -#root { +#app { padding-bottom: 67px; position: relative; min-height: 100%; + + &.is-fullscreen { + width: 100vw; + height: 100vh; + } } body { diff --git a/client/scss/pages/_login.scss b/client/scss/pages/_login.scss index 017ea4dd..be4bd602 100644 --- a/client/scss/pages/_login.scss +++ b/client/scss/pages/_login.scss @@ -81,7 +81,6 @@ .login { border-right: none; border-radius: 6px 0 0 6px; - background-color: mc('grey', '900'); z-index: 1; overflow: hidden; @@ -94,8 +93,8 @@ .login { padding: 5px 15px; border: none; color: #FFF; - background-color: mc('light-blue', '800'); - border-top: 1px solid mc('light-blue', '900'); + background: linear-gradient(to right, rgba(mc('light-blue', '800'), .7), rgba(mc('light-blue', '800'), 1)); + border-top: 1px solid rgba(mc('light-blue', '900'), .5); font-family: $core-font-standard; font-weight: 600; text-align: left; @@ -110,7 +109,7 @@ .login { } &:hover { - background-color: mc('light-blue', '600'); + background-color: mc('light-blue', '900'); } &:first-child { diff --git a/package.json b/package.json index 7e27ce96..05737b09 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "connect-redis": "3.3.0", "cookie-parser": "1.4.3", "diff2html": "2.3.0", + "dotize": "^0.2.0", "execa": "0.8.0", "express": "4.15.4", "express-brute": "1.0.1", @@ -68,6 +69,7 @@ "highlight.js": "9.12.0", "i18next": "9.0.0", "i18next-express-middleware": "1.0.5", + "i18next-localstorage-cache": "^1.1.1", "i18next-node-fs-backend": "1.0.0", "image-size": "0.6.1", "ioredis": "3.1.4", @@ -105,7 +107,6 @@ "passport-local": "1.0.0", "passport-slack": "0.0.7", "passport-windowslive": "1.0.2", - "passport.socketio": "3.7.0", "pg": "7.3.0", "pg-hstore": "2.3.2", "pg-promise": "6.5.2", @@ -120,7 +121,6 @@ "sequelize": "4.8.2", "serve-favicon": "2.4.3", "simplemde": "1.11.2", - "socket.io": "2.0.3", "stopword": "0.1.7", "stream-to-promise": "2.2.0", "tar": "4.0.1", @@ -133,6 +133,7 @@ "devDependencies": { "@glimpse/glimpse": "0.22.15", "@panter/vue-i18next": "0.5.1", + "apollo-client": "^1.9.3", "babel-cli": "6.26.0", "babel-jest": "21.0.2", "babel-plugin-transform-object-assign": "6.22.0", @@ -147,6 +148,7 @@ "eslint-plugin-promise": "3.5.0", "eslint-plugin-standard": "3.0.1", "fuse-box": "^2.2.31", + "graphql-tag": "^2.4.2", "i18next-xhr-backend": "1.4.2", "jest": "21.0.2", "jquery": "3.2.1", @@ -166,6 +168,7 @@ "vue": "2.4.2", "vue-clipboards": "1.1.0", "vue-lodash": "1.0.3", + "vue-material": "^0.7.5", "vue-resource": "1.3.4", "vue-template-compiler": "2.4.2", "vue-template-es2015-compiler": "1.5.3", diff --git a/server/authentication/azure.js b/server/authentication/azure.js index c6d71b4d..70e23cc4 100644 --- a/server/authentication/azure.js +++ b/server/authentication/azure.js @@ -11,6 +11,7 @@ const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2').Strategy module.exports = { key: 'azure', title: 'Azure Active Directory', + useForm: false, props: ['clientId', 'clientSecret', 'callbackURL', 'resource', 'tenant'], init (passport, conf) { const jwt = require('jsonwebtoken') diff --git a/server/authentication/facebook.js b/server/authentication/facebook.js index 03375dac..24b31416 100644 --- a/server/authentication/facebook.js +++ b/server/authentication/facebook.js @@ -11,6 +11,7 @@ const FacebookStrategy = require('passport-facebook').Strategy module.exports = { key: 'facebook', title: 'Facebook', + useForm: false, props: ['clientId', 'clientSecret', 'callbackURL'], init (passport, conf) { passport.use('facebook', diff --git a/server/authentication/github.js b/server/authentication/github.js index ff3aec3b..2dbca0c4 100644 --- a/server/authentication/github.js +++ b/server/authentication/github.js @@ -11,6 +11,7 @@ const GitHubStrategy = require('passport-github2').Strategy module.exports = { key: 'github', title: 'GitHub', + useForm: false, props: ['clientId', 'clientSecret', 'callbackURL'], init (passport, conf) { passport.use('github', diff --git a/server/authentication/google.js b/server/authentication/google.js index 371f4c30..d353e6c4 100644 --- a/server/authentication/google.js +++ b/server/authentication/google.js @@ -11,6 +11,7 @@ const GoogleStrategy = require('passport-google-oauth20').Strategy module.exports = { key: 'google', title: 'Google ID', + useForm: false, props: ['clientId', 'clientSecret', 'callbackURL'], init (passport, conf) { passport.use('google', diff --git a/server/authentication/ldap.js b/server/authentication/ldap.js index c7a993db..720b5f4b 100644 --- a/server/authentication/ldap.js +++ b/server/authentication/ldap.js @@ -12,6 +12,7 @@ const fs = require('fs') module.exports = { key: 'ldap', title: 'LDAP / Active Directory', + useForm: true, props: ['url', 'bindDn', 'bindCredentials', 'searchBase', 'searchFilter', 'tlsEnabled', 'tlsCertPath'], init (passport, conf) { passport.use('ldapauth', diff --git a/server/authentication/local.js b/server/authentication/local.js index 1c4c7c07..e2ca7faa 100644 --- a/server/authentication/local.js +++ b/server/authentication/local.js @@ -11,6 +11,7 @@ const LocalStrategy = require('passport-local').Strategy module.exports = { key: 'local', title: 'Local', + useForm: true, props: [], init (passport, conf) { passport.use('local', diff --git a/server/authentication/microsoft.js b/server/authentication/microsoft.js index e8069de2..20e3e1d9 100644 --- a/server/authentication/microsoft.js +++ b/server/authentication/microsoft.js @@ -11,6 +11,7 @@ const WindowsLiveStrategy = require('passport-windowslive').Strategy module.exports = { key: 'microsoft', title: 'Microsoft Account', + useForm: false, props: ['clientId', 'clientSecret', 'callbackURL'], init (passport, conf) { passport.use('windowslive', diff --git a/server/authentication/slack.js b/server/authentication/slack.js index 17b7e0ae..e4aa6d5f 100644 --- a/server/authentication/slack.js +++ b/server/authentication/slack.js @@ -11,6 +11,7 @@ const SlackStrategy = require('passport-slack').Strategy module.exports = { key: 'slack', title: 'Slack', + useForm: false, props: ['clientId', 'clientSecret', 'callbackURL'], init (passport, conf) { passport.use('slack', diff --git a/server/master.js b/server/master.js index 9199bc71..f4350edf 100644 --- a/server/master.js +++ b/server/master.js @@ -20,7 +20,7 @@ module.exports = Promise.join( wiki.disk = require('./modules/disk').init() wiki.docs = require('./modules/documents').init() wiki.git = require('./modules/git').init(false) - wiki.lang = require('i18next') + wiki.lang = require('./modules/localization').init() wiki.mark = require('./modules/markdown') wiki.search = require('./modules/search').init() wiki.upl = require('./modules/uploads').init() @@ -37,13 +37,10 @@ module.exports = Promise.join( const favicon = require('serve-favicon') const flash = require('connect-flash') const http = require('http') - const i18nBackend = require('i18next-node-fs-backend') const path = require('path') - const passportSocketIo = require('passport.socketio') const session = require('express-session') const SessionRedisStore = require('connect-redis')(session) const graceful = require('node-graceful') - const socketio = require('socket.io') const graphqlApollo = require('apollo-server-express') const graphqlSchema = require('./modules/graphql') @@ -100,23 +97,6 @@ module.exports = Promise.join( app.use(mw.seo) - // ---------------------------------------- - // Localization Engine - // ---------------------------------------- - - wiki.lang.use(i18nBackend).init({ - load: 'languageOnly', - ns: ['common', 'admin', 'auth', 'errors', 'git'], - defaultNS: 'common', - saveMissing: false, - preload: [wiki.config.site.lang], - lng: wiki.config.site.lang, - fallbackLng: 'en', - backend: { - loadPath: path.join(wiki.SERVERPATH, 'locales/{{lng}}/{{ns}}.json') - } - }) - // ---------------------------------------- // View Engine Setup // ---------------------------------------- @@ -133,7 +113,7 @@ module.exports = Promise.join( app.locals.basedir = wiki.ROOTPATH app.locals._ = require('lodash') - app.locals.t = wiki.lang.t.bind(wiki.lang) + app.locals.t = wiki.lang.engine.t.bind(wiki.lang) app.locals.moment = require('moment') app.locals.moment.locale(wiki.config.site.lang) app.locals.config = wiki.config @@ -173,11 +153,10 @@ module.exports = Promise.join( // Start HTTP server // ---------------------------------------- - wiki.logger.info(`HTTP/WS Server on port: ${wiki.config.port}`) + wiki.logger.info(`HTTP Server on port: ${wiki.config.port}`) app.set('port', wiki.config.port) - var server = http.createServer(app) - var io = socketio(server) + let server = http.createServer(app) server.listen(wiki.config.port) server.on('error', (error) => { @@ -199,35 +178,14 @@ module.exports = Promise.join( }) server.on('listening', () => { - wiki.logger.info('HTTP/WS Server: RUNNING') + wiki.logger.info('HTTP Server: RUNNING') }) - // ---------------------------------------- - // WebSocket - // ---------------------------------------- - - io.use(passportSocketIo.authorize({ - key: 'wikijs.sid', - store: sessionStore, - secret: wiki.config.site.sessionSecret, - cookieParser, - success: (data, accept) => { - accept() - }, - fail: (data, message, error, accept) => { - accept() - } - })) - - io.on('connection', ctrl.ws) - // ---------------------------------------- // Graceful shutdown // ---------------------------------------- graceful.on('exit', () => { - // wiki.logger.info('- SHUTTING DOWN - Terminating Background Agent...') - // bgAgent.kill() wiki.logger.info('- SHUTTING DOWN - Performing git sync...') return global.git.resync().then(() => { wiki.logger.info('- SHUTTING DOWN - Git sync successful. Now safe to exit.') diff --git a/server/modules/graphql.js b/server/modules/graphql.js index e1897390..e625afe6 100644 --- a/server/modules/graphql.js +++ b/server/modules/graphql.js @@ -17,6 +17,7 @@ const FolderResolvers = require('../schemas/resolvers-folder') const GroupResolvers = require('../schemas/resolvers-group') const SettingResolvers = require('../schemas/resolvers-setting') const TagResolvers = require('../schemas/resolvers-tag') +const TranslationResolvers = require('../schemas/resolvers-translation') const UserResolvers = require('../schemas/resolvers-user') const resolvers = _.merge( @@ -27,6 +28,7 @@ const resolvers = _.merge( GroupResolvers, SettingResolvers, TagResolvers, + TranslationResolvers, UserResolvers, DateScalar ) diff --git a/server/modules/localization.js b/server/modules/localization.js new file mode 100644 index 00000000..d832935b --- /dev/null +++ b/server/modules/localization.js @@ -0,0 +1,52 @@ +const _ = require('lodash') +const dotize = require('dotize') +const i18nBackend = require('i18next-node-fs-backend') +const i18next = require('i18next') +const path = require('path') +const Promise = require('bluebird') + +/* global wiki */ + +module.exports = { + engine: null, + namespaces: ['common', 'admin', 'auth', 'errors', 'git'], + init() { + this.engine = i18next + this.engine.use(i18nBackend).init({ + load: 'languageOnly', + ns: this.namespaces, + defaultNS: 'common', + saveMissing: false, + preload: [wiki.config.site.lang], + lng: wiki.config.site.lang, + fallbackLng: 'en', + backend: { + loadPath: path.join(wiki.SERVERPATH, 'locales/{{lng}}/{{ns}}.json') + } + }) + return this + }, + getByNamespace(locale, namespace) { + if (this.engine.hasResourceBundle(locale, namespace)) { + let data = this.engine.getResourceBundle(locale, namespace) + return _.map(dotize.convert(data), (value, key) => { + return { + key, + value + } + }) + } else { + throw new Error('Invalid locale or namespace') + } + }, + loadLocale(locale) { + return Promise.fromCallback(cb => { + return this.engine.loadLanguages(locale, cb) + }) + }, + setCurrentLocale(locale) { + return Promise.fromCallback(cb => { + return this.engine.changeLanguage(locale, cb) + }) + } +} diff --git a/server/schemas/resolvers-comment.js b/server/schemas/resolvers-comment.js index f8d00ee2..fec565b3 100644 --- a/server/schemas/resolvers-comment.js +++ b/server/schemas/resolvers-comment.js @@ -1,4 +1,3 @@ -'use strict' /* global wiki */ diff --git a/server/schemas/resolvers-document.js b/server/schemas/resolvers-document.js index 666f8fef..41dc9160 100644 --- a/server/schemas/resolvers-document.js +++ b/server/schemas/resolvers-document.js @@ -1,4 +1,3 @@ -'use strict' /* global wiki */ diff --git a/server/schemas/resolvers-file.js b/server/schemas/resolvers-file.js index c1011d95..c8913413 100644 --- a/server/schemas/resolvers-file.js +++ b/server/schemas/resolvers-file.js @@ -1,4 +1,3 @@ -'use strict' /* global wiki */ diff --git a/server/schemas/resolvers-folder.js b/server/schemas/resolvers-folder.js index 4acf2806..b7fc9a2b 100644 --- a/server/schemas/resolvers-folder.js +++ b/server/schemas/resolvers-folder.js @@ -1,4 +1,3 @@ -'use strict' /* global wiki */ diff --git a/server/schemas/resolvers-group.js b/server/schemas/resolvers-group.js index 61afc2d0..cc9b10b7 100644 --- a/server/schemas/resolvers-group.js +++ b/server/schemas/resolvers-group.js @@ -1,4 +1,3 @@ -'use strict' /* global wiki */ diff --git a/server/schemas/resolvers-right.js b/server/schemas/resolvers-right.js index 72e42b76..3b286deb 100644 --- a/server/schemas/resolvers-right.js +++ b/server/schemas/resolvers-right.js @@ -1,4 +1,3 @@ -'use strict' /* global wiki */ diff --git a/server/schemas/resolvers-setting.js b/server/schemas/resolvers-setting.js index 1ece4a0e..dc0c4f9e 100644 --- a/server/schemas/resolvers-setting.js +++ b/server/schemas/resolvers-setting.js @@ -1,4 +1,3 @@ -'use strict' /* global wiki */ diff --git a/server/schemas/resolvers-tag.js b/server/schemas/resolvers-tag.js index ad11f674..0aa3e89e 100644 --- a/server/schemas/resolvers-tag.js +++ b/server/schemas/resolvers-tag.js @@ -1,4 +1,3 @@ -'use strict' /* global wiki */ diff --git a/server/schemas/resolvers-translation.js b/server/schemas/resolvers-translation.js new file mode 100644 index 00000000..aed8d57d --- /dev/null +++ b/server/schemas/resolvers-translation.js @@ -0,0 +1,12 @@ + +/* global wiki */ + +module.exports = { + Query: { + translations (obj, args, context, info) { + return wiki.lang.getByNamespace(args.locale, args.namespace) + } + }, + Mutation: {}, + Translation: {} +} diff --git a/server/schemas/resolvers-user.js b/server/schemas/resolvers-user.js index 694306e2..90133707 100644 --- a/server/schemas/resolvers-user.js +++ b/server/schemas/resolvers-user.js @@ -1,4 +1,3 @@ -'use strict' /* global wiki */ diff --git a/server/schemas/scalar-date.js b/server/schemas/scalar-date.js index 6fa5b585..472312ad 100644 --- a/server/schemas/scalar-date.js +++ b/server/schemas/scalar-date.js @@ -1,4 +1,3 @@ -'use strict' const gql = require('graphql') diff --git a/server/schemas/types.graphql b/server/schemas/types.graphql index 4f5cc929..99bbbf33 100644 --- a/server/schemas/types.graphql +++ b/server/schemas/types.graphql @@ -114,6 +114,11 @@ type Tag implements Base { documents: [Document] } +type Translation { + key: String! + value: String! +} + # A User type User implements Base { id: Int! @@ -142,6 +147,7 @@ type Query { rights(id: Int): [Right] settings(key: String): [Setting] tags(key: String): [Tag] + translations(locale: String!, namespace: String!): [Translation] users(id: Int, email: String, provider: String, providerId: String, role: UserRole): [User] } diff --git a/server/views/auth/login.pug b/server/views/auth/login.pug index 982036e6..44ebc019 100644 --- a/server/views/auth/login.pug +++ b/server/views/auth/login.pug @@ -2,31 +2,5 @@ extends ../master.pug block body body - .login#root(class={ "is-error": flash.length > 0 }) - .login-container(class={ "is-expanded": hasMultipleStrategies }) - if flash.length > 0 - .login-error - strong - i.icon-warning-outline - = flash[0].title - span= flash[0].message - if hasMultipleStrategies - .login-providers - button.is-active(title=t('auth:providers.local')) - i.nc-icon-outline.ui-1_database - span= t('auth:providers.local') - each strategy in authStrategies - button(onclick='window.location.assign("/login/' + strategy.key + '")', title=strategy.title) - != strategy.icon - span= strategy.title - .login-frame - h1= config.site.title - h2= t('auth:loginrequired') - form(method='post', action='/login') - input#login-user(type='text', name='email', placeholder=t('auth:fields.emailuser')) - input#login-pass(type='password', name='password', placeholder=t('auth:fields.password')) - button.button.is-light-blue.is-fullwidth(type='submit') - span= t('auth:actions.login') - .login-copyright - = t('footer.poweredby') - a(href='https://wiki.js.org', rel='external', title='Wiki.js') Wiki.js + #app.is-fullscreen + login diff --git a/server/views/layout.pug b/server/views/layout.pug index 49d4747a..1d704f7e 100644 --- a/server/views/layout.pug +++ b/server/views/layout.pug @@ -2,7 +2,7 @@ extends ./master.pug block body body - #root.has-stickynav(class=['is-primary-' + appconfig.theme.primary, 'is-alternate-' + appconfig.theme.alt]) + #app.has-stickynav(class=['is-primary-' + appconfig.theme.primary, 'is-alternate-' + appconfig.theme.alt]) include ./common/header.pug alert main diff --git a/yarn.lock b/yarn.lock index 0a625c96ca92e9fb634391d4cb292c26a1496b04..28796802ef77619f55715dbeb85fe4c7b344db7a 100644 GIT binary patch delta 1975 zcmZ`)ZHQD=80OqNxH`J-D*G`??M{=J!rZ&(+;i_e_e!gqX@x%`D@Y2B^L2NJ#+lun z+07Ngun&wNq9G0yLiD9YKZK#K`DZcE`XS}&4+}GmegqLxl!74GxwA9l#}@PB&Uwzq z`@YY4-sjY>c3(TweMF}Fp^c#JyM{5rfUHn?kYODPMXpB>BMb&1 z!J+g+pCT^^^hc_Re~e7+4Xa~cu~2%>;^j%1&FzA=XTzMXFPj~mq~l`~#RjNrx*fG> zY3BJc+1P_^&B1MZtSTMV`y4&d+%_r2)E*6C+igLWncb{hw91{VA}6OQD<<)gx|joZ z1E}8I0tVFOCyd4F$Wf49)nq=MZWi6nS>1RBWNL3+&L`YF+JOL#z(o-wEp7A54Mzs*kQ1^>c@2&HBLS>wD|xkAJha{>ks=T6Llu9E$r^ zf{$05g}qX3PZ=4NeH!GqiE>o~y1w~8x~hccL~nj5JtuOh!w|-<%RN7e9Lx#C97CdU z;Y&|zGM7&4z14!6>oB&)h6Pr3#;t`0XE>)r9W?${+ep^|`K*zrJW$UO-HrkMd zZPbj9C50l8f$#dv<5Kzo#2!RG4Jq+-VB~QJ$E9jLgd<-_l#6odnZ_Hv3Ga zyHb85ldZ|*ElKE+)mS-SKlQN_5)pWgBOum15JxzEgqVXvN*Q)>;6g6q4`Xo67-*<_ zL@PcKcRPF&z^ad%8@2v{y|P&5v{fk;jud?Ti0hRs1xIbR}qw&+~_@1lAsb%r3X}-G4)E1y#JZ1I)QkxjG zHZ?p!7uon~9g(JQLZ1u78`IFn@dz^yuU0(|8pCn;g4ww?o;qv}W!Kzn z=){h7%jL5R&QO;wn3=aP8TlOxrgV`DFnxirX-7`TA|H9erIa8CF(HC5U|Pibp@>4p zUFlE?v54RO);!P?e|f>2>se9~GOEx>j;QRfU~^3WHdD*v%OEwpY;ZTN%ER5BUqr!@>bZP;NDdW?rUB|?1}Y6jz_8&d1QzXu9$EC2ui delta 4895 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 GPyP%1{oyeH