diff --git a/client/client-app.js b/client/client-app.js index 9e94b937..f592f2d7 100644 --- a/client/client-app.js +++ b/client/client-app.js @@ -161,14 +161,15 @@ Vue.component('admin', () => import(/* webpackChunkName: "admin" */ './component Vue.component('editor', () => import(/* webpackPrefetch: -100, webpackChunkName: "editor" */ './components/editor.vue')) Vue.component('history', () => import(/* webpackChunkName: "history" */ './components/history.vue')) Vue.component('login', () => import(/* webpackPrefetch: true, webpackChunkName: "login" */ './components/login.vue')) -Vue.component('nav-footer', () => import(/* webpackMode: "eager" */ './components/common/nav-footer.vue')) Vue.component('nav-header', () => import(/* webpackMode: "eager" */ './components/common/nav-header.vue')) -Vue.component('nav-sidebar', () => import(/* webpackMode: "eager" */ './components/common/nav-sidebar.vue')) -Vue.component('page', () => import(/* webpackChunkName: "theme-page" */ './themes/' + process.env.CURRENT_THEME + '/components/app.vue')) Vue.component('page-selector', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/page-selector.vue')) Vue.component('profile', () => import(/* webpackChunkName: "profile" */ './components/profile.vue')) Vue.component('v-card-chin', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-chin.vue')) +Vue.component('nav-footer', () => import(/* webpackChunkName: "theme-page" */ './themes/' + process.env.CURRENT_THEME + '/components/nav-footer.vue')) +Vue.component('nav-sidebar', () => import(/* webpackChunkName: "theme-page" */ './themes/' + process.env.CURRENT_THEME + '/components/nav-sidebar.vue')) +Vue.component('page', () => import(/* webpackChunkName: "theme-page" */ './themes/' + process.env.CURRENT_THEME + '/components/page.vue')) + let bootstrap = () => { // ==================================== // Notifications @@ -202,6 +203,12 @@ let bootstrap = () => { window.boot.notify('vue') + // ==================================== + // Load theme-specific code + // ==================================== + + import(/* webpackChunkName: "theme-page" */ './themes/' + process.env.CURRENT_THEME + '/js/app.js') + // ==================================== // Load Icons // ==================================== diff --git a/client/components/common/nav-header.vue b/client/components/common/nav-header.vue index 626ecfe6..f4977932 100644 --- a/client/components/common/nav-header.vue +++ b/client/components/common/nav-header.vue @@ -17,7 +17,7 @@ @keyup.enter='searchEnter' ) v-menu(open-on-hover, offset-y, bottom, left, min-width='250') - v-toolbar-side-icon(slot='activator') + v-toolbar-side-icon.btn-animate-app(slot='activator') v-icon view_module v-list(dense, :light='!$vuetify.dark').py-0 v-list-tile(avatar, href='/') @@ -84,12 +84,12 @@ ) v-icon(color='grey') search v-tooltip(bottom) - v-btn(icon, href='/a', slot='activator') + v-btn.btn-animate-rotate(icon, href='/a', slot='activator') v-icon(color='grey') settings span Admin v-menu(offset-y, min-width='300') v-tooltip(bottom, slot='activator') - v-btn(icon, slot='activator') + v-btn.btn-animate-grow(icon, slot='activator', outline, color='grey darken-3') v-icon(color='grey') account_circle span Account v-list.py-0(:light='!$vuetify.dark') @@ -212,4 +212,5 @@ export default { .navHeaderLoading { // To avoid search bar jumping width: 22px; } + diff --git a/client/components/editor.vue b/client/components/editor.vue index 1458bbbf..1dc575a9 100644 --- a/client/components/editor.vue +++ b/client/components/editor.vue @@ -142,6 +142,10 @@ export default { initContent: { type: String, default: null + }, + pageId: { + type: Number, + default: 0 } }, data() { @@ -158,6 +162,7 @@ export default { notificationState: sync('notification@isActive') }, created() { + this.$store.commit('page/SET_ID', this.pageId) this.$store.commit('page/SET_DESCRIPTION', this.description) this.$store.commit('page/SET_IS_PUBLISHED', this.isPublished) this.$store.commit('page/SET_LOCALE', this.locale) @@ -212,16 +217,16 @@ export default { mutation: createPageMutation, variables: { content: this.$store.get('editor/content'), - description: this.$store.get('editor/description'), + description: this.$store.get('page/description'), editor: 'markdown', - locale: this.$store.get('editor/locale'), + locale: this.$store.get('page/locale'), isPrivate: false, - isPublished: this.$store.get('editor/isPublished'), - path: this.$store.get('editor/path'), - publishEndDate: this.$store.get('editor/publishEndDate'), - publishStartDate: this.$store.get('editor/publishStartDate'), - tags: this.$store.get('editor/tags'), - title: this.$store.get('editor/title') + isPublished: this.$store.get('page/isPublished'), + path: this.$store.get('page/path'), + publishEndDate: this.$store.get('page/publishEndDate'), + publishStartDate: this.$store.get('page/publishStartDate'), + tags: this.$store.get('page/tags'), + title: this.$store.get('page/title') } }) resp = _.get(resp, 'data.pages.create', {}) @@ -244,18 +249,18 @@ export default { let resp = await this.$apollo.mutate({ mutation: updatePageMutation, variables: { - id: this.$store.get('editor/id'), + id: this.$store.get('page/id'), content: this.$store.get('editor/content'), - description: this.$store.get('editor/description'), + description: this.$store.get('page/description'), editor: 'markdown', - locale: this.$store.get('editor/locale'), + locale: this.$store.get('page/locale'), isPrivate: false, - isPublished: this.$store.get('editor/isPublished'), - path: this.$store.get('editor/path'), - publishEndDate: this.$store.get('editor/publishEndDate'), - publishStartDate: this.$store.get('editor/publishStartDate'), - tags: this.$store.get('editor/tags'), - title: this.$store.get('editor/title') + isPublished: this.$store.get('page/isPublished'), + path: this.$store.get('page/path'), + publishEndDate: this.$store.get('page/publishEndDate'), + publishStartDate: this.$store.get('page/publishStartDate'), + tags: this.$store.get('page/tags'), + title: this.$store.get('page/title') } }) resp = _.get(resp, 'data.pages.update', {}) diff --git a/client/components/editor/editor-markdown.vue b/client/components/editor/editor-markdown.vue index 214eb3e1..09e56fb5 100644 --- a/client/components/editor/editor-markdown.vue +++ b/client/components/editor/editor-markdown.vue @@ -206,7 +206,7 @@ export default { self.$parent.save() }) - cm.setSize(null, 'calc(100vh - 100px)') + cm.setSize(null, 'calc(100vh - 112px)') cm.setOption('extraKeys', keyBindings) cm.on('cursorActivity', cm => { this.toolbarSync(cm) @@ -270,7 +270,7 @@ export default { background-color: darken(mc('grey', '900'), 4.5%); flex: 1 1 50%; display: block; - height: calc(100vh - 96px); + height: calc(100vh - 112px); position: relative; &-title { @@ -300,7 +300,7 @@ export default { flex: 1 1 50%; background-color: mc('grey', '100'); position: relative; - height: calc(100vh - 100px); + height: calc(100vh - 112px); overflow: hidden; @include until($tablet) { @@ -321,7 +321,7 @@ export default { } &-content { - height: calc(100vh - 100px); + height: calc(100vh - 112px); overflow-y: scroll; padding: 30px 1rem 1rem 1rem; width: calc(100% + 1rem + 17px) diff --git a/client/components/editor/editor-modal-properties.vue b/client/components/editor/editor-modal-properties.vue index 8be86fb4..15b3a716 100644 --- a/client/components/editor/editor-modal-properties.vue +++ b/client/components/editor/editor-modal-properties.vue @@ -207,14 +207,14 @@ export default { }, computed: { mode: get('editor/mode'), - title: sync('editor/title'), - description: sync('editor/description'), - locale: sync('editor/locale'), - tags: sync('editor/tags'), - path: sync('editor/path'), - isPublished: sync('editor/isPublished'), - publishStartDate: sync('editor/publishStartDate'), - publishEndDate: sync('editor/publishEndDate') + title: sync('page/title'), + description: sync('page/description'), + locale: sync('page/locale'), + tags: sync('page/tags'), + path: sync('page/path'), + isPublished: sync('page/isPublished'), + publishStartDate: sync('page/publishStartDate'), + publishEndDate: sync('page/publishEndDate') }, mounted() { this.isShown = true diff --git a/client/scss/components/v-btn.scss b/client/scss/components/v-btn.scss index 41caa1be..277a6ecd 100644 --- a/client/scss/components/v-btn.scss +++ b/client/scss/components/v-btn.scss @@ -1,3 +1,44 @@ .v-btn.is-icon { min-width: auto; } + +.btn-animate-rotate { + i { + transition: all 4s ease; + transform: rotate(0deg); + } + &:hover i { + transform: rotate(360deg); + } +} + +.btn-animate-grow { + i { + transition: all 2s ease; + transform: scale(1); + } + &:hover i { + transform: scale(1.25); + } +} + +.btn-animate-edit { + i { + transition: all .7s cubic-bezier(0.68, -0.55, 0.265, 1.55); + transform: rotate(0deg); + } + &:hover i { + transform: rotate(-45deg); + } +} + +.btn-animate-app { + i { + transition: all .6s ease; + transform: translate3d(0,0,0); + transform-style: preserve-3d; + } + &:hover i { + transform: scale(.7) rotateX(-180deg); + } +} diff --git a/client/store/editor.js b/client/store/editor.js index 1aa8677f..5af3d0b7 100644 --- a/client/store/editor.js +++ b/client/store/editor.js @@ -1,17 +1,8 @@ import { make } from 'vuex-pathify' const state = { - id: 0, content: '', - description: '', - isPublished: true, - locale: 'en', - mode: 'create', - path: '', - publishEndDate: '', - publishStartDate: '', - tags: [], - title: '' + mode: 'create' } export default { diff --git a/client/components/common/nav-footer.vue b/client/themes/default/components/nav-footer.vue similarity index 100% rename from client/components/common/nav-footer.vue rename to client/themes/default/components/nav-footer.vue diff --git a/client/components/common/nav-sidebar.vue b/client/themes/default/components/nav-sidebar.vue similarity index 100% rename from client/components/common/nav-sidebar.vue rename to client/themes/default/components/nav-sidebar.vue diff --git a/client/themes/default/components/app.vue b/client/themes/default/components/page.vue similarity index 98% rename from client/themes/default/components/app.vue rename to client/themes/default/components/page.vue index a0a35f6e..a6376c1c 100644 --- a/client/themes/default/components/app.vue +++ b/client/themes/default/components/page.vue @@ -47,7 +47,7 @@ .caption.grey--text.text--darken-1 {{ updatedAt | moment('calendar') }} v-spacer v-tooltip(left) - v-btn(icon, slot='activator', :href='"/e/" + path') + v-btn.btn-animate-edit(icon, slot='activator', :href='"/e/" + path') v-icon(color='grey') edit span Edit Page v-divider diff --git a/client/themes/default/scss/app.scss b/client/themes/default/scss/app.scss index 44123a3c..392e7bbf 100644 --- a/client/themes/default/scss/app.scss +++ b/client/themes/default/scss/app.scss @@ -87,16 +87,28 @@ } code { - background-color: transparent; + background-color: rgba(mc('pink', '500'), .1); + padding: 0 5px; + color: mc('pink', '800'); font-family: 'Source Code Pro', monospace; font-weight: normal; font-size: 1rem; + box-shadow: none; &::before, &::after { display: none; } } + ol, ul { + padding: 1rem 24px 0 24px; + list-style-position: inside; + + li + li { + margin-top: .5rem; + } + } + .prismjs{ border: none; border-radius: 5px; @@ -106,6 +118,9 @@ margin: 1rem 24px; > code { + background-color: transparent; + padding: 0; + color: #FFF; box-shadow: initial; display: block; font-size: .85rem; diff --git a/server/core/db.js b/server/core/db.js index f753d356..20ab41f8 100644 --- a/server/core/db.js +++ b/server/core/db.js @@ -37,6 +37,15 @@ module.exports = { case 'mariadb': case 'mysql': dbClient = 'mysql2' + + // Fix mysql boolean handling... + dbConfig.typeCast = (field, next) => { + if (field.type === 'TINY' && field.length === 1) { + let value = field.string() + return value ? (value === '1') : null + } + return next() + } break case 'mssql': dbClient = 'mssql' diff --git a/server/graph/resolvers/system.js b/server/graph/resolvers/system.js index db2bad53..8b099376 100644 --- a/server/graph/resolvers/system.js +++ b/server/graph/resolvers/system.js @@ -4,6 +4,7 @@ const getos = Promise.promisify(require('getos')) const os = require('os') const filesize = require('filesize') const path = require('path') +const fs = require('fs-extra') /* global WIKI */ @@ -76,6 +77,10 @@ module.exports = { const osInfo = await getos() osLabel = `${os.type()} - ${osInfo.dist} (${osInfo.codename || os.platform()}) ${osInfo.release || os.release()} ${os.arch()}` } + const isDockerized = await fs.pathExists('/.dockerenv') + if (isDockerized) { + osLabel = `${osLabel} (Docker Container)` + } return osLabel }, hostname() { diff --git a/server/models/pageHistory.js b/server/models/pageHistory.js index 935f2638..cc653d0d 100644 --- a/server/models/pageHistory.js +++ b/server/models/pageHistory.js @@ -23,6 +23,7 @@ module.exports = class PageHistory extends Model { publishStartDate: {type: 'string'}, publishEndDate: {type: 'string'}, content: {type: 'string'}, + contentType: {type: 'string'}, createdAt: {type: 'string'} } @@ -87,6 +88,7 @@ module.exports = class PageHistory extends Model { pageId: opts.id, authorId: opts.authorId, content: opts.content, + contentType: opts.contentType, description: opts.description, editorKey: opts.editorKey, hash: opts.hash, diff --git a/server/views/editor.pug b/server/views/editor.pug index 7cf1c5fd..a927be11 100644 --- a/server/views/editor.pug +++ b/server/views/editor.pug @@ -4,6 +4,7 @@ block body #root v-app editor( + :page-id=page.id locale=page.localeCode path=page.path title=page.title diff --git a/server/views/page.pug b/server/views/page.pug index ee32eeed..6743b53f 100644 --- a/server/views/page.pug +++ b/server/views/page.pug @@ -14,6 +14,6 @@ block body updated-at=page.updatedAt author-name=page.authorName :author-id=page.authorId - is-published=page.isPublished + :is-published=page.isPublished ) template(slot='contents')!= page.render