Commit 32b79477 authored by NGPixel's avatar NGPixel

feat: GraphQL translations + login form

parent 16a47e46
...@@ -6,10 +6,14 @@ ...@@ -6,10 +6,14 @@
import Vue from 'vue' import Vue from 'vue'
import VueResource from 'vue-resource' import VueResource from 'vue-resource'
import VueClipboards from 'vue-clipboards' import VueClipboards from 'vue-clipboards'
import { ApolloClient, createBatchingNetworkInterface } from 'apollo-client'
import store from './store' 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 // Load Helpers
...@@ -29,6 +33,7 @@ import editorFileComponent from './components/editor-file.vue' ...@@ -29,6 +33,7 @@ import editorFileComponent from './components/editor-file.vue'
import editorVideoComponent from './components/editor-video.vue' import editorVideoComponent from './components/editor-video.vue'
import historyComponent from './components/history.vue' import historyComponent from './components/history.vue'
import loadingSpinnerComponent from './components/loading-spinner.vue' import loadingSpinnerComponent from './components/loading-spinner.vue'
import loginComponent from './components/login.vue'
import modalCreatePageComponent from './components/modal-create-page.vue' import modalCreatePageComponent from './components/modal-create-page.vue'
import modalCreateUserComponent from './components/modal-create-user.vue' import modalCreateUserComponent from './components/modal-create-user.vue'
import modalDeleteUserComponent from './components/modal-delete-user.vue' import modalDeleteUserComponent from './components/modal-delete-user.vue'
...@@ -50,12 +55,23 @@ import editorComponent from './components/editor.component.js' ...@@ -50,12 +55,23 @@ import editorComponent from './components/editor.component.js'
import sourceViewComponent from './pages/source-view.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 // Initialize Vue Modules
// ==================================== // ====================================
Vue.use(VueResource) Vue.use(VueResource)
Vue.use(VueClipboards) Vue.use(VueClipboards)
Vue.use(VueI18Next) Vue.use(localization.VueI18Next)
Vue.use(helpers) Vue.use(helpers)
// ==================================== // ====================================
...@@ -76,6 +92,7 @@ Vue.component('editorFile', editorFileComponent) ...@@ -76,6 +92,7 @@ Vue.component('editorFile', editorFileComponent)
Vue.component('editorVideo', editorVideoComponent) Vue.component('editorVideo', editorVideoComponent)
Vue.component('history', historyComponent) Vue.component('history', historyComponent)
Vue.component('loadingSpinner', loadingSpinnerComponent) Vue.component('loadingSpinner', loadingSpinnerComponent)
Vue.component('login', loginComponent)
Vue.component('modalCreatePage', modalCreatePageComponent) Vue.component('modalCreatePage', modalCreatePageComponent)
Vue.component('modalCreateUser', modalCreateUserComponent) Vue.component('modalCreateUser', modalCreateUserComponent)
Vue.component('modalDeleteUser', modalDeleteUserComponent) Vue.component('modalDeleteUser', modalDeleteUserComponent)
...@@ -89,20 +106,6 @@ Vue.component('sourceView', sourceViewComponent) ...@@ -89,20 +106,6 @@ Vue.component('sourceView', sourceViewComponent)
Vue.component('toggle', toggleComponent) Vue.component('toggle', toggleComponent)
Vue.component('tree', treeComponent) 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 => { document.addEventListener('DOMContentLoaded', ev => {
// ==================================== // ====================================
// Notifications // Notifications
...@@ -116,13 +119,13 @@ document.addEventListener('DOMContentLoaded', ev => { ...@@ -116,13 +119,13 @@ document.addEventListener('DOMContentLoaded', ev => {
// Bootstrap Vue // Bootstrap Vue
// ==================================== // ====================================
const i18n = new VueI18Next(i18next) const i18n = localization.init()
window.wiki = new Vue({ window.wiki = new Vue({
mixins: [helpers], mixins: [helpers],
components: {}, components: {},
store, store,
i18n, i18n,
el: '#root', el: '#app',
methods: { methods: {
changeTheme(opts) { changeTheme(opts) {
this.$el.className = `has-stickynav is-primary-${opts.primary} is-alternate-${opts.alt}` this.$el.className = `has-stickynav is-primary-${opts.primary} is-alternate-${opts.alt}`
......
<template lang="pug">
.login(:class='{ "is-error": error }')
.login-container(:class='{ "is-expanded": strategies.length > 1 }')
.login-error(v-if='error')
strong
i.icon-warning-outline
| {{ error.title }}
span {{ error.message }}
.login-providers(v-show='strategies.length > 1')
button.is-active(:title='$t("auth:providers.local")')
i.nc-icon-outline.ui-1_database
span {{ $t('auth:providers.local') }}
button(v-for='strategy in strategies', @onclick='selectProvider(strategy.key, strategy.useForm)', :title='strategy.title')
//-!= strategy.icon
span {{ strategy.title }}
.login-frame
h1 {{ siteTitle }}
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
span {{ $t('footer.poweredby') }}
a(href='https://wiki.js.org', rel='external', title='Wiki.js') Wiki.js
</template>
<script>
export default {
name: 'login',
data() {
return {
error: false,
strategies: []
}
},
computed: {
siteTitle() {
return siteConfig.title
}
}
methods: {
selectProvider(key, useForm) {
}
}
}
</script>
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)
}
}
...@@ -11,10 +11,15 @@ html { ...@@ -11,10 +11,15 @@ html {
display: none; display: none;
} }
#root { #app {
padding-bottom: 67px; padding-bottom: 67px;
position: relative; position: relative;
min-height: 100%; min-height: 100%;
&.is-fullscreen {
width: 100vw;
height: 100vh;
}
} }
body { body {
......
...@@ -81,7 +81,6 @@ ...@@ -81,7 +81,6 @@
border-right: none; border-right: none;
border-radius: 6px 0 0 6px; border-radius: 6px 0 0 6px;
background-color: mc('grey', '900');
z-index: 1; z-index: 1;
overflow: hidden; overflow: hidden;
...@@ -94,8 +93,8 @@ ...@@ -94,8 +93,8 @@
padding: 5px 15px; padding: 5px 15px;
border: none; border: none;
color: #FFF; color: #FFF;
background-color: mc('light-blue', '800'); background: linear-gradient(to right, rgba(mc('light-blue', '800'), .7), rgba(mc('light-blue', '800'), 1));
border-top: 1px solid mc('light-blue', '900'); border-top: 1px solid rgba(mc('light-blue', '900'), .5);
font-family: $core-font-standard; font-family: $core-font-standard;
font-weight: 600; font-weight: 600;
text-align: left; text-align: left;
...@@ -110,7 +109,7 @@ ...@@ -110,7 +109,7 @@
} }
&:hover { &:hover {
background-color: mc('light-blue', '600'); background-color: mc('light-blue', '900');
} }
&:first-child { &:first-child {
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
"connect-redis": "3.3.0", "connect-redis": "3.3.0",
"cookie-parser": "1.4.3", "cookie-parser": "1.4.3",
"diff2html": "2.3.0", "diff2html": "2.3.0",
"dotize": "^0.2.0",
"execa": "0.8.0", "execa": "0.8.0",
"express": "4.15.4", "express": "4.15.4",
"express-brute": "1.0.1", "express-brute": "1.0.1",
...@@ -68,6 +69,7 @@ ...@@ -68,6 +69,7 @@
"highlight.js": "9.12.0", "highlight.js": "9.12.0",
"i18next": "9.0.0", "i18next": "9.0.0",
"i18next-express-middleware": "1.0.5", "i18next-express-middleware": "1.0.5",
"i18next-localstorage-cache": "^1.1.1",
"i18next-node-fs-backend": "1.0.0", "i18next-node-fs-backend": "1.0.0",
"image-size": "0.6.1", "image-size": "0.6.1",
"ioredis": "3.1.4", "ioredis": "3.1.4",
...@@ -105,7 +107,6 @@ ...@@ -105,7 +107,6 @@
"passport-local": "1.0.0", "passport-local": "1.0.0",
"passport-slack": "0.0.7", "passport-slack": "0.0.7",
"passport-windowslive": "1.0.2", "passport-windowslive": "1.0.2",
"passport.socketio": "3.7.0",
"pg": "7.3.0", "pg": "7.3.0",
"pg-hstore": "2.3.2", "pg-hstore": "2.3.2",
"pg-promise": "6.5.2", "pg-promise": "6.5.2",
...@@ -120,7 +121,6 @@ ...@@ -120,7 +121,6 @@
"sequelize": "4.8.2", "sequelize": "4.8.2",
"serve-favicon": "2.4.3", "serve-favicon": "2.4.3",
"simplemde": "1.11.2", "simplemde": "1.11.2",
"socket.io": "2.0.3",
"stopword": "0.1.7", "stopword": "0.1.7",
"stream-to-promise": "2.2.0", "stream-to-promise": "2.2.0",
"tar": "4.0.1", "tar": "4.0.1",
...@@ -133,6 +133,7 @@ ...@@ -133,6 +133,7 @@
"devDependencies": { "devDependencies": {
"@glimpse/glimpse": "0.22.15", "@glimpse/glimpse": "0.22.15",
"@panter/vue-i18next": "0.5.1", "@panter/vue-i18next": "0.5.1",
"apollo-client": "^1.9.3",
"babel-cli": "6.26.0", "babel-cli": "6.26.0",
"babel-jest": "21.0.2", "babel-jest": "21.0.2",
"babel-plugin-transform-object-assign": "6.22.0", "babel-plugin-transform-object-assign": "6.22.0",
...@@ -147,6 +148,7 @@ ...@@ -147,6 +148,7 @@
"eslint-plugin-promise": "3.5.0", "eslint-plugin-promise": "3.5.0",
"eslint-plugin-standard": "3.0.1", "eslint-plugin-standard": "3.0.1",
"fuse-box": "^2.2.31", "fuse-box": "^2.2.31",
"graphql-tag": "^2.4.2",
"i18next-xhr-backend": "1.4.2", "i18next-xhr-backend": "1.4.2",
"jest": "21.0.2", "jest": "21.0.2",
"jquery": "3.2.1", "jquery": "3.2.1",
...@@ -166,6 +168,7 @@ ...@@ -166,6 +168,7 @@
"vue": "2.4.2", "vue": "2.4.2",
"vue-clipboards": "1.1.0", "vue-clipboards": "1.1.0",
"vue-lodash": "1.0.3", "vue-lodash": "1.0.3",
"vue-material": "^0.7.5",
"vue-resource": "1.3.4", "vue-resource": "1.3.4",
"vue-template-compiler": "2.4.2", "vue-template-compiler": "2.4.2",
"vue-template-es2015-compiler": "1.5.3", "vue-template-es2015-compiler": "1.5.3",
......
...@@ -11,6 +11,7 @@ const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2').Strategy ...@@ -11,6 +11,7 @@ const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2').Strategy
module.exports = { module.exports = {
key: 'azure', key: 'azure',
title: 'Azure Active Directory', title: 'Azure Active Directory',
useForm: false,
props: ['clientId', 'clientSecret', 'callbackURL', 'resource', 'tenant'], props: ['clientId', 'clientSecret', 'callbackURL', 'resource', 'tenant'],
init (passport, conf) { init (passport, conf) {
const jwt = require('jsonwebtoken') const jwt = require('jsonwebtoken')
......
...@@ -11,6 +11,7 @@ const FacebookStrategy = require('passport-facebook').Strategy ...@@ -11,6 +11,7 @@ const FacebookStrategy = require('passport-facebook').Strategy
module.exports = { module.exports = {
key: 'facebook', key: 'facebook',
title: 'Facebook', title: 'Facebook',
useForm: false,
props: ['clientId', 'clientSecret', 'callbackURL'], props: ['clientId', 'clientSecret', 'callbackURL'],
init (passport, conf) { init (passport, conf) {
passport.use('facebook', passport.use('facebook',
......
...@@ -11,6 +11,7 @@ const GitHubStrategy = require('passport-github2').Strategy ...@@ -11,6 +11,7 @@ const GitHubStrategy = require('passport-github2').Strategy
module.exports = { module.exports = {
key: 'github', key: 'github',
title: 'GitHub', title: 'GitHub',
useForm: false,
props: ['clientId', 'clientSecret', 'callbackURL'], props: ['clientId', 'clientSecret', 'callbackURL'],
init (passport, conf) { init (passport, conf) {
passport.use('github', passport.use('github',
......
...@@ -11,6 +11,7 @@ const GoogleStrategy = require('passport-google-oauth20').Strategy ...@@ -11,6 +11,7 @@ const GoogleStrategy = require('passport-google-oauth20').Strategy
module.exports = { module.exports = {
key: 'google', key: 'google',
title: 'Google ID', title: 'Google ID',
useForm: false,
props: ['clientId', 'clientSecret', 'callbackURL'], props: ['clientId', 'clientSecret', 'callbackURL'],
init (passport, conf) { init (passport, conf) {
passport.use('google', passport.use('google',
......
...@@ -12,6 +12,7 @@ const fs = require('fs') ...@@ -12,6 +12,7 @@ const fs = require('fs')
module.exports = { module.exports = {
key: 'ldap', key: 'ldap',
title: 'LDAP / Active Directory', title: 'LDAP / Active Directory',
useForm: true,
props: ['url', 'bindDn', 'bindCredentials', 'searchBase', 'searchFilter', 'tlsEnabled', 'tlsCertPath'], props: ['url', 'bindDn', 'bindCredentials', 'searchBase', 'searchFilter', 'tlsEnabled', 'tlsCertPath'],
init (passport, conf) { init (passport, conf) {
passport.use('ldapauth', passport.use('ldapauth',
......
...@@ -11,6 +11,7 @@ const LocalStrategy = require('passport-local').Strategy ...@@ -11,6 +11,7 @@ const LocalStrategy = require('passport-local').Strategy
module.exports = { module.exports = {
key: 'local', key: 'local',
title: 'Local', title: 'Local',
useForm: true,
props: [], props: [],
init (passport, conf) { init (passport, conf) {
passport.use('local', passport.use('local',
......
...@@ -11,6 +11,7 @@ const WindowsLiveStrategy = require('passport-windowslive').Strategy ...@@ -11,6 +11,7 @@ const WindowsLiveStrategy = require('passport-windowslive').Strategy
module.exports = { module.exports = {
key: 'microsoft', key: 'microsoft',
title: 'Microsoft Account', title: 'Microsoft Account',
useForm: false,
props: ['clientId', 'clientSecret', 'callbackURL'], props: ['clientId', 'clientSecret', 'callbackURL'],
init (passport, conf) { init (passport, conf) {
passport.use('windowslive', passport.use('windowslive',
......
...@@ -11,6 +11,7 @@ const SlackStrategy = require('passport-slack').Strategy ...@@ -11,6 +11,7 @@ const SlackStrategy = require('passport-slack').Strategy
module.exports = { module.exports = {
key: 'slack', key: 'slack',
title: 'Slack', title: 'Slack',
useForm: false,
props: ['clientId', 'clientSecret', 'callbackURL'], props: ['clientId', 'clientSecret', 'callbackURL'],
init (passport, conf) { init (passport, conf) {
passport.use('slack', passport.use('slack',
......
...@@ -20,7 +20,7 @@ module.exports = Promise.join( ...@@ -20,7 +20,7 @@ module.exports = Promise.join(
wiki.disk = require('./modules/disk').init() wiki.disk = require('./modules/disk').init()
wiki.docs = require('./modules/documents').init() wiki.docs = require('./modules/documents').init()
wiki.git = require('./modules/git').init(false) wiki.git = require('./modules/git').init(false)
wiki.lang = require('i18next') wiki.lang = require('./modules/localization').init()
wiki.mark = require('./modules/markdown') wiki.mark = require('./modules/markdown')
wiki.search = require('./modules/search').init() wiki.search = require('./modules/search').init()
wiki.upl = require('./modules/uploads').init() wiki.upl = require('./modules/uploads').init()
...@@ -37,13 +37,10 @@ module.exports = Promise.join( ...@@ -37,13 +37,10 @@ module.exports = Promise.join(
const favicon = require('serve-favicon') const favicon = require('serve-favicon')
const flash = require('connect-flash') const flash = require('connect-flash')
const http = require('http') const http = require('http')
const i18nBackend = require('i18next-node-fs-backend')
const path = require('path') const path = require('path')
const passportSocketIo = require('passport.socketio')
const session = require('express-session') const session = require('express-session')
const SessionRedisStore = require('connect-redis')(session) const SessionRedisStore = require('connect-redis')(session)
const graceful = require('node-graceful') const graceful = require('node-graceful')
const socketio = require('socket.io')
const graphqlApollo = require('apollo-server-express') const graphqlApollo = require('apollo-server-express')
const graphqlSchema = require('./modules/graphql') const graphqlSchema = require('./modules/graphql')
...@@ -101,23 +98,6 @@ module.exports = Promise.join( ...@@ -101,23 +98,6 @@ module.exports = Promise.join(
app.use(mw.seo) 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 // View Engine Setup
// ---------------------------------------- // ----------------------------------------
...@@ -133,7 +113,7 @@ module.exports = Promise.join( ...@@ -133,7 +113,7 @@ module.exports = Promise.join(
app.locals.basedir = wiki.ROOTPATH app.locals.basedir = wiki.ROOTPATH
app.locals._ = require('lodash') 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 = require('moment')
app.locals.moment.locale(wiki.config.site.lang) app.locals.moment.locale(wiki.config.site.lang)
app.locals.config = wiki.config app.locals.config = wiki.config
...@@ -173,11 +153,10 @@ module.exports = Promise.join( ...@@ -173,11 +153,10 @@ module.exports = Promise.join(
// Start HTTP server // 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) app.set('port', wiki.config.port)
var server = http.createServer(app) let server = http.createServer(app)
var io = socketio(server)
server.listen(wiki.config.port) server.listen(wiki.config.port)
server.on('error', (error) => { server.on('error', (error) => {
...@@ -199,35 +178,14 @@ module.exports = Promise.join( ...@@ -199,35 +178,14 @@ module.exports = Promise.join(
}) })
server.on('listening', () => { 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 shutdown
// ---------------------------------------- // ----------------------------------------
graceful.on('exit', () => { graceful.on('exit', () => {
// wiki.logger.info('- SHUTTING DOWN - Terminating Background Agent...')
// bgAgent.kill()
wiki.logger.info('- SHUTTING DOWN - Performing git sync...') wiki.logger.info('- SHUTTING DOWN - Performing git sync...')
return global.git.resync().then(() => { return global.git.resync().then(() => {
wiki.logger.info('- SHUTTING DOWN - Git sync successful. Now safe to exit.') wiki.logger.info('- SHUTTING DOWN - Git sync successful. Now safe to exit.')
......
...@@ -17,6 +17,7 @@ const FolderResolvers = require('../schemas/resolvers-folder') ...@@ -17,6 +17,7 @@ const FolderResolvers = require('../schemas/resolvers-folder')
const GroupResolvers = require('../schemas/resolvers-group') const GroupResolvers = require('../schemas/resolvers-group')
const SettingResolvers = require('../schemas/resolvers-setting') const SettingResolvers = require('../schemas/resolvers-setting')
const TagResolvers = require('../schemas/resolvers-tag') const TagResolvers = require('../schemas/resolvers-tag')
const TranslationResolvers = require('../schemas/resolvers-translation')
const UserResolvers = require('../schemas/resolvers-user') const UserResolvers = require('../schemas/resolvers-user')
const resolvers = _.merge( const resolvers = _.merge(
...@@ -27,6 +28,7 @@ const resolvers = _.merge( ...@@ -27,6 +28,7 @@ const resolvers = _.merge(
GroupResolvers, GroupResolvers,
SettingResolvers, SettingResolvers,
TagResolvers, TagResolvers,
TranslationResolvers,
UserResolvers, UserResolvers,
DateScalar DateScalar
) )
......
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)
})
}
}
'use strict'
/* global wiki */ /* global wiki */
......
'use strict'
/* global wiki */ /* global wiki */
......
'use strict'
/* global wiki */ /* global wiki */
......
'use strict'
/* global wiki */ /* global wiki */
......
'use strict'
/* global wiki */ /* global wiki */
......
'use strict'
/* global wiki */ /* global wiki */
......
'use strict'
/* global wiki */ /* global wiki */
......
'use strict'
/* global wiki */ /* global wiki */
......
/* global wiki */
module.exports = {
Query: {
translations (obj, args, context, info) {
return wiki.lang.getByNamespace(args.locale, args.namespace)
}
},
Mutation: {},
Translation: {}
}
'use strict'
/* global wiki */ /* global wiki */
......
'use strict'
const gql = require('graphql') const gql = require('graphql')
......
...@@ -114,6 +114,11 @@ type Tag implements Base { ...@@ -114,6 +114,11 @@ type Tag implements Base {
documents: [Document] documents: [Document]
} }
type Translation {
key: String!
value: String!
}
# A User # A User
type User implements Base { type User implements Base {
id: Int! id: Int!
...@@ -142,6 +147,7 @@ type Query { ...@@ -142,6 +147,7 @@ type Query {
rights(id: Int): [Right] rights(id: Int): [Right]
settings(key: String): [Setting] settings(key: String): [Setting]
tags(key: String): [Tag] tags(key: String): [Tag]
translations(locale: String!, namespace: String!): [Translation]
users(id: Int, email: String, provider: String, providerId: String, role: UserRole): [User] users(id: Int, email: String, provider: String, providerId: String, role: UserRole): [User]
} }
......
...@@ -2,31 +2,5 @@ extends ../master.pug ...@@ -2,31 +2,5 @@ extends ../master.pug
block body block body
body body
.login#root(class={ "is-error": flash.length > 0 }) #app.is-fullscreen
.login-container(class={ "is-expanded": hasMultipleStrategies }) login
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
...@@ -2,7 +2,7 @@ extends ./master.pug ...@@ -2,7 +2,7 @@ extends ./master.pug
block body block body
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 include ./common/header.pug
alert alert
main main
......
This diff was suppressed by a .gitattributes entry.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment