浏览代码

feat: theme code injection

Nicolas Giard 6 年之前
父节点
当前提交
faa1f389d9

+ 11 - 245
CHANGELOG.md

@@ -2,255 +2,21 @@
 All notable changes to this project will be documented in this file.
 All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](http://semver.org/).
 This project adheres to [Semantic Versioning](http://semver.org/).
 
 
-## [v1.0.11] - 2017-10-14
+## [2.0.0-beta.12] - 2018-01-27
 ### Added
 ### Added
-- **Localization**: Japanese locale is now available (thanks to @johnnyshields)
-- **Misc**: Added referrer policy header
-
-## Fixed
-- **Authentication**: Fix for name search when using OpenLDAP
-
-## [v1.0.10] - 2017-10-09
-### Added
-- **Misc**: Ability to delete pages from the UI
-
-## [v1.0.9] - 2017-09-14
-### Added
-- **Localization**: Persian (farsi) locale is now available (thanks to @ashkang)
-- **UI**: Support for right-to-left languages
-
-### Fixed
-- **i18n**: Browser locale files not generated properly (ported from dev branch)
-
-## [v1.0.8] - 2017-08-31
-### Changed
-- **Misc**: Updated dependencies
-
-## [v1.0.7] - 2017-08-29
-### Fixed
-- **Authentication**: Azure AD client Id is now referenced correctly ([#219](https://github.com/Requarks/wiki/issues/219))
-- **Git**: Git Branch is now referenced correctly ([#215](https://github.com/Requarks/wiki/issues/215))
-
-## [v1.0.6] - 2017-08-10
-### Fixed
-- **Authentication**: LDAP no longer cause the social login text to appear on the login screen.
-- **Misc**: Browser locales are now generated for non-english languages
-- **Misc**: Renaming or moving a page is now removing the old entry in the All Pages navigation
-
-## [v1.0.5] - 2017-08-06
-### Fixed
-- **Misc**: public access always false if using env var string
-
-## [v1.0.4] - 2017-07-26
-### Fixed
-- **Misc**: Some installations (e.g. Heroku) would not install all dependencies
-
-## [v1.0.3] - 2017-07-23
-### Fixed
-- **Misc**: Revert to npm install (Yarn may cause permissions + hang issues)
-
-## [v1.0.1] - 2017-07-22
-### Changed
-- **Misc**: Switch to Yarn for npm dependencies installation
-
-### Fixed
-- **Misc**: JS/CSS is now loading properly in Safari (macOS/iOS)
-- **Misc**: Process termination handling
-- **Search**: siteRoot is now properly parsed in search results href
-
-## [v1.0.0-beta.13] - 2017-07-09
-### Added
-- **Admin**: Added Host Information section to System Info page
-- **Admin**: Added Color Theme page to modify look and feel
-- **Editor**: Linebreaks are now rendered, can be disabled via config option
-- **Localization**: German locale is now available (thanks to @joetjengerdes)
-- **UI**: Support for color themes, code blocks dark/light + colorize on/off
-
-### Changed
-- **Editor**: TeX and MathML is now rendered server-side to SVG
-- **UI**: Updated icons to Nucleo icon set
-- **Misc**: Updated dependencies
-
-### Fixed
-- **Configuration Wizard**: Git version check is now handled properly when using 2 or 3 version precision
-- **Editor**: Blockquotes are now displayed in their correct color stylings
-- **Misc**: 'Entry does not exist' page now display sub-pages separator correctly
-- **Misc**: Locked dependencies to patch instead of minor version
-- **Misc**: Saving a page no longer crash the search index engine
-
-## [v1.0.0-beta.12] - 2017-06-10
-### Added
-- **Deploy**: Heroku support
-- **Localization**: All UI text elements are now localized
-- **Localization**: Chinese locale is now available (thanks to @choicky)
-- **Localization**: Korean locale is now available (thanks to @junwonpk)
-- **Localization**: Portuguese locale is now available (thanks to @felipeplets)
-- **Localization**: Russian locale is now available (thanks to @efimlosev)
-- **Localization**: Spanish locale is now available (thanks to @MatiasArriola)
-- **Misc**: Copy to clipboard modal when clicking on header anchor
-- **Print**: Optimized layout and colors for print view
-
-### Changed
-- **Misc**: Refactored all client-side code into Vue components
-- **Misc**: Updated dependencies
-- **UI**: Reveal 'Top of Page' only on scroll + icon only
-- **UI**: Updated navigation buttons design
-- **UI**: Updated editor toolbar + page design
-
-### Fixed
-- **Configuration Wizard**: Public option is now saved properly in config file
-- **Configuration Wizard**: Git check no longer fails when unable to remove existing remotes
-- **Editor**: Large size content can now be saved up to 1 MB
-- **Editor**: Editor no longer fails to initialize if it contains unescaped mustache content
-- **Misc**: Page content no longer renders non-highlighted HTML content enclosed in code blocks
-- **Misc**: Empty anchors no longer crash the rendering process
-- **Misc**: Commented headers no longer appear in page contents
-- **Misc**: CJK + Arabic validators are now working properly
-- **Move**: It is now possible to move a page to non-existant sub-directory (or deeper)
-- **Search**: Content is now indexed properly and handles more scenarios
-- **Search**: CJK search terms are no longer stripped
-- **UI**: Markdown is now stripped from page contents items
-- **UI**: Page contents no longer disappear when scrolling down
-
-## [v1.0.0-beta.11] - 2017-04-29
-### Added
-- **Auth**: Azure AD authentication provider is now available
-- **Auth**: Can now specify Read Access by default for all providers (except Local)
-- **Configuration Wizard**: Added Public Access option
-- **Git**: Commits author is now set to current user
-- **Navigation**: All Pages section
-- **UI**: Beatiful new logo!
-- **View**: MathML and TeX math equations support
-
-### Changed
-- **Auth**: Provider Strategies are now only loaded if enabled
-- **Misc**: Server files are now in their own /server path
-- **Misc**: Trailing slashes in URL are now removed
-- **Misc**: Updated dependencies
-- **UI**: Footer is now always at the bottom of the page (but not fixed)
-
-### Fixed
-- **Configuration Wizard**: Git version detection no longer fails on MacOS
-- **Init**: Malformed config file is now being reported correctly
-- **Init**: Git remote is now always updated to current settings
-- **Misc**: CJK (Chinese, Japanese & Korean) characters are now fully supported for pages, content and uploads
-- **UI**: Move dialog is no longer crashing and preventing further actions
-- **UI**: Scrollbar is no longer always shown in code blocks
-- **Search**: Search is now working for guest users when public mode is enabled
-
-## [v1.0.0-beta.10] - 2017-04-08
-### Added
-- **Installation**: Wiki.js can now install via local tarball
-- **Installation**: RAM check during install to prevent crashing due to low memory
-
-### Changed
-- Updated dependencies + snyk policy
+- Added Patreon link in Contribute admin page
+- Added Theme Code Injection feature
+- Added Theme CSS Injection code minification
 
 
 ### Fixed
 ### Fixed
-- **UI**: Code blocks longer than page width are now displayed with scrollbars
-- **Configuration Wizard**: Git version check no longer fails if between 2.7.4 and 2.11.0
-- **Init**: Admin account is no longer attempted to be created during init
-
-## [v1.0.0-beta.9] - 2017-04-05
-### Added
-- Interactive setup
-- **Auth**: GitHub and Slack authentication providers are now available
-- **Auth**: LDAP authentication provider is now available
-- **Logs**: Support for the logging services: Bugsnag, Loggly, Papertrail, Rollbar and Sentry
-- **Config**: Can now use ENV variable to specify DB connection string ($VARNAME as db value in config.yml)
+- Fixed root admin refresh token fail
+- Fixed error page metadata title warning
 
 
 ### Changed
 ### Changed
-- **Native Compilation Removal**: Replaced farmhash with md5
-- **Native Compilation Removal**: Replaced leveldown with memdown
-- **Native Compilation Removal**: Replaced sharp with jimp
-- **Sidebar**: Contents is now Page Contents
-- **Sidebar**: Start is now Top of Page
-- **UI**: Content headers are now showing an anchor icon instead of a #
-- **Dev**: Replaced Gulp with Fuse-box
+- Moved Insert Media button in Markdown editor
 
 
-### Fixed
-- **Auth**: Authentication would fail if email has uppercase chars and provider callback is in lowercase
-- **Markdown**: Fixed potential crash on markdown processing of video links
-- **Search**: Search index should now update upon article creation
-- **Search**: Search results are no longer duplicated upon article update
-- **UI**: Missing icons on login page
-- **UI**: Image alignement center and right should now behave correctly
-- **Uploads**: Error notification when upload is too large for server
-- **Uploads**: Fix uploads and temp-uploads folder permissions on unix-based systems
-
-## [v1.0.0-beta.8] - 2017-02-19
-### Added
-- Automated Upgrade / Re-install feature UI only
-- npm installation improvements
-
-### Fixed
-- wiki executable shortcut on linux
-- Settings page is now displaying the correct current version
-
-## [v1.0.0-beta.7] - 2017-02-14
-### Fixed
-- npm installation fixes
-
-## [v1.0.0-beta.6] - 2017-02-14
-### Added
-- Settings page UI
-- Automated process management
-- npm automatic site installation
-
-## [v1.0-beta.5] - 2017-02-12
-### Added
-- Offline mode (no remote git sync) can now be enabled by setting `git: false` in config.yml
-- Improved search engine (Now using search-index engine instead of MongoDB text search)
-
-### Changed
-- Cache is now flushed when starting / restarting the server
-
-## [v1.0-beta.4] - 2017-02-11
-### Fixed
-- Fixed folder name typo during uploads folder permissions check
-- Fixed SSH authentication for Git
-
-### Changed
-- Removed separate OAuth authentication option. Select basic authentication to use tokens.
-
-## [v1.0-beta.3] - 2017-02-10
-### Added
-- Change log
-- Added .editorconfig, .eslintrc.json and .pug-lintrc.json for code linting
-- Added Create / Authorize User feature
-- Added Delete / De-authorize User feature
-- Added Login as... button to Forbidden page
-
-### Fixed
-- Fixed issue with social accounts with empty name
-- Fixed standard error page styling
-
-### Changed
-- Updated dependencies + snyk policy
-- Conversion to Standard JS compliant code
-- Accounts that are not pre-authorized are no longer added with no rights
-
-## [v1.0-beta.2] - 2017-01-30
-### Added
-- Save own profile under My Account
-
-### Changed
-- Updated dependencies + snyk policy
+## [2.0.0-beta.11] - 2018-01-20
+- First beta release
 
 
-[v1.0.6]: https://github.com/Requarks/wiki/releases/tag/v1.0.6
-[v1.0.5]: https://github.com/Requarks/wiki/releases/tag/v1.0.5
-[v1.0.4]: https://github.com/Requarks/wiki/releases/tag/v1.0.4
-[v1.0.3]: https://github.com/Requarks/wiki/releases/tag/v1.0.3
-[v1.0.1]: https://github.com/Requarks/wiki/releases/tag/v1.0.1
-[v1.0.0-beta.13]: https://github.com/Requarks/wiki/releases/tag/v1.0.0-beta.13
-[v1.0.0-beta.12]: https://github.com/Requarks/wiki/releases/tag/v1.0.0-beta.12
-[v1.0.0-beta.11]: https://github.com/Requarks/wiki/releases/tag/v1.0.0-beta.11
-[v1.0.0-beta.10]: https://github.com/Requarks/wiki/releases/tag/v1.0.0-beta.10
-[v1.0.0-beta.9]: https://github.com/Requarks/wiki/releases/tag/v1.0.0-beta.9
-[v1.0.0-beta.8]: https://github.com/Requarks/wiki/releases/tag/v1.0.0-beta.8
-[v1.0.0-beta.7]: https://github.com/Requarks/wiki/releases/tag/v1.0.0-beta.7
-[v1.0.0-beta.6]: https://github.com/Requarks/wiki/releases/tag/v1.0.0-beta.6
-[v1.0-beta.5]: https://github.com/Requarks/wiki/releases/tag/v1.0-beta.5
-[v1.0-beta.4]: https://github.com/Requarks/wiki/releases/tag/v1.0-beta.4
-[v1.0-beta.3]: https://github.com/Requarks/wiki/releases/tag/v1.0-beta.3
-[v1.0-beta.2]: https://github.com/Requarks/wiki/releases/tag/v1.0-beta.2
+[2.0.0-beta.12]: https://github.com/Requarks/wiki/releases/tag/2.0.0-beta.12
+[2.0.0-beta.11]: https://github.com/Requarks/wiki/releases/tag/2.0.0-beta.11

+ 31 - 19
client/components/admin/admin-theme.vue

@@ -24,7 +24,7 @@
                     outline
                     outline
                     background-color='grey lighten-2'
                     background-color='grey lighten-2'
                     prepend-icon='palette'
                     prepend-icon='palette'
-                    v-model='selectedTheme'
+                    v-model='config.theme'
                     label='Site Theme'
                     label='Site Theme'
                     persistent-hint
                     persistent-hint
                     hint='Themes affect how content pages are displayed. Other site sections (such as the editor or admin area) are not affected.'
                     hint='Themes affect how content pages are displayed. Other site sections (such as the editor or admin area) are not affected.'
@@ -47,41 +47,36 @@
                 v-toolbar(color='primary', dark, dense, flat)
                 v-toolbar(color='primary', dark, dense, flat)
                   v-toolbar-title
                   v-toolbar-title
                     .subheading Code Injection
                     .subheading Code Injection
-                  v-spacer
-                  v-chip(label, color='white', small).primary--text coming soon
                 v-card-text
                 v-card-text
                   v-textarea(
                   v-textarea(
-                    v-model='injectCSS'
+                    v-model='config.injectCSS'
                     label='CSS Override'
                     label='CSS Override'
                     outline
                     outline
                     background-color='grey lighten-1'
                     background-color='grey lighten-1'
                     color='primary'
                     color='primary'
                     persistent-hint
                     persistent-hint
-                    hint='CSS code to inject after system default CSS'
+                    hint='CSS code to inject after system default CSS. Consider using custom themes if you have a large amount of css code. Injecting too much CSS code will result in poor page load performance! CSS will automatically be minified.'
                     auto-grow
                     auto-grow
-                    disabled
                     )
                     )
                   v-textarea.mt-2(
                   v-textarea.mt-2(
-                    v-model='injectHeader'
+                    v-model='config.injectHead'
                     label='Head HTML Injection'
                     label='Head HTML Injection'
                     outline
                     outline
                     background-color='grey lighten-1'
                     background-color='grey lighten-1'
                     color='primary'
                     color='primary'
                     persistent-hint
                     persistent-hint
-                    hint='HTML code to be injected just before the closing head tag'
+                    hint='HTML code to be injected just before the closing head tag. Usually for script tags.'
                     auto-grow
                     auto-grow
-                    disabled
                     )
                     )
                   v-textarea.mt-2(
                   v-textarea.mt-2(
-                    v-model='injectFooter'
+                    v-model='config.injectBody'
                     label='Body HTML Injection'
                     label='Body HTML Injection'
                     outline
                     outline
                     background-color='grey lighten-1'
                     background-color='grey lighten-1'
                     color='primary'
                     color='primary'
                     persistent-hint
                     persistent-hint
-                    hint='HTML code to be injected just before the closing body tag'
+                    hint='HTML code to be injected just before the closing body tag.'
                     auto-grow
                     auto-grow
-                    disabled
                     )
                     )
             v-flex(lg6 xs12)
             v-flex(lg6 xs12)
               v-card
               v-card
@@ -97,6 +92,7 @@
 import _ from 'lodash'
 import _ from 'lodash'
 import { sync } from 'vuex-pathify'
 import { sync } from 'vuex-pathify'
 
 
+import themeConfigQuery from 'gql/admin/theme/theme-query-config.gql'
 import themeSaveMutation from 'gql/admin/theme/theme-mutation-save.gql'
 import themeSaveMutation from 'gql/admin/theme/theme-mutation-save.gql'
 
 
 export default {
 export default {
@@ -106,11 +102,14 @@ export default {
       themes: [
       themes: [
         { text: 'Default', author: 'requarks.io', value: 'default' }
         { text: 'Default', author: 'requarks.io', value: 'default' }
       ],
       ],
-      selectedTheme: 'default',
-      darkModeInitial: false,
-      injectCSS: '',
-      injectHeader: '',
-      injectFooter: ''
+      config: {
+        theme: 'default',
+        darkMode: false,
+        injectCSS: '',
+        injectHead: '',
+        injectBody: ''
+      },
+      darkModeInitial: false
     }
     }
   },
   },
   computed: {
   computed: {
@@ -130,8 +129,11 @@ export default {
         const respRaw = await this.$apollo.mutate({
         const respRaw = await this.$apollo.mutate({
           mutation: themeSaveMutation,
           mutation: themeSaveMutation,
           variables: {
           variables: {
-            theme: this.selectedTheme,
-            darkMode: this.darkMode
+            theme: this.config.theme,
+            darkMode: this.darkMode,
+            injectCSS: this.config.injectCSS,
+            injectHead: this.config.injectHead,
+            injectBody: this.config.injectBody
           }
           }
         })
         })
         const resp = _.get(respRaw, 'data.theming.setConfig.responseResult', {})
         const resp = _.get(respRaw, 'data.theming.setConfig.responseResult', {})
@@ -151,6 +153,16 @@ export default {
       this.$store.commit(`loadingStop`, 'admin-theme-save')
       this.$store.commit(`loadingStop`, 'admin-theme-save')
       this.loading = false
       this.loading = false
     }
     }
+  },
+  apollo: {
+    config: {
+      query: themeConfigQuery,
+      fetchPolicy: 'network-only',
+      update: (data) => data.theming.config,
+      watchLoading (isLoading) {
+        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-theme-refresh')
+      }
+    }
   }
   }
 }
 }
 </script>
 </script>

+ 2 - 2
client/graph/admin/theme/theme-mutation-save.gql

@@ -1,6 +1,6 @@
-mutation($theme: String!, $darkMode: Boolean!) {
+mutation($theme: String!, $darkMode: Boolean!, $injectCSS: String, $injectHead: String, $injectBody: String) {
   theming {
   theming {
-    setConfig(theme: $theme, darkMode: $darkMode) {
+    setConfig(theme: $theme, darkMode: $darkMode, injectCSS: $injectCSS, injectHead: $injectHead, injectBody: $injectBody) {
       responseResult {
       responseResult {
         succeeded
         succeeded
         errorCode
         errorCode

+ 11 - 0
client/graph/admin/theme/theme-query-config.gql

@@ -0,0 +1,11 @@
+query {
+  theming {
+    config {
+      theme
+      darkMode
+      injectCSS
+      injectHead
+      injectBody
+    }
+  }
+}

+ 1 - 1
package.json

@@ -54,6 +54,7 @@
     "cheerio": "1.0.0-rc.2",
     "cheerio": "1.0.0-rc.2",
     "child-process-promise": "2.2.1",
     "child-process-promise": "2.2.1",
     "chokidar": "2.0.4",
     "chokidar": "2.0.4",
+    "clean-css": "4.2.1",
     "compression": "1.7.3",
     "compression": "1.7.3",
     "connect-redis": "3.4.0",
     "connect-redis": "3.4.0",
     "cookie-parser": "1.4.3",
     "cookie-parser": "1.4.3",
@@ -179,7 +180,6 @@
     "@babel/polyfill": "^7.0.0",
     "@babel/polyfill": "^7.0.0",
     "@babel/preset-env": "^7.1.6",
     "@babel/preset-env": "^7.1.6",
     "@panter/vue-i18next": "0.13.0",
     "@panter/vue-i18next": "0.13.0",
-    "@vue/cli": "3.1.3",
     "animated-number-vue": "0.1.3",
     "animated-number-vue": "0.1.3",
     "apollo-cache-inmemory": "1.3.10",
     "apollo-cache-inmemory": "1.3.10",
     "apollo-client": "2.4.6",
     "apollo-client": "2.4.6",

+ 6 - 1
server/controllers/common.js

@@ -149,7 +149,12 @@ router.get('/*', async (req, res, next) => {
     _.set(res.locals, 'pageMeta.title', page.title)
     _.set(res.locals, 'pageMeta.title', page.title)
     _.set(res.locals, 'pageMeta.description', page.description)
     _.set(res.locals, 'pageMeta.description', page.description)
     const sidebar = await WIKI.models.navigation.getTree({ cache: true })
     const sidebar = await WIKI.models.navigation.getTree({ cache: true })
-    res.render('page', { page, sidebar })
+    const injectCode = {
+      css: WIKI.config.theming.injectCSS,
+      head: WIKI.config.theming.injectHead,
+      body: WIKI.config.theming.injectBody
+    }
+    res.render('page', { page, sidebar, injectCode })
   } else if (pageArgs.path === 'home') {
   } else if (pageArgs.path === 'home') {
     _.set(res.locals, 'pageMeta.title', 'Welcome')
     _.set(res.locals, 'pageMeta.title', 'Welcome')
     res.render('welcome')
     res.render('welcome')

+ 18 - 6
server/graph/resolvers/theming.js

@@ -1,4 +1,6 @@
 const graphHelper = require('../../helpers/graph')
 const graphHelper = require('../../helpers/graph')
+const _ = require('lodash')
+const CleanCSS = require('clean-css')
 
 
 /* global WIKI */
 /* global WIKI */
 
 
@@ -18,17 +20,27 @@ module.exports = {
       }]
       }]
     },
     },
     async config(obj, args, context, info) {
     async config(obj, args, context, info) {
-      return {
-        theme: WIKI.config.theming.theme,
-        darkMode: WIKI.config.theming.darkMode
-      }
+      return _.pick(WIKI.config.theming, ['theme', 'darkMode', 'injectCSS', 'injectHead', 'injectBody'])
     }
     }
   },
   },
   ThemingMutation: {
   ThemingMutation: {
     async setConfig(obj, args, context, info) {
     async setConfig(obj, args, context, info) {
       try {
       try {
-        WIKI.config.theming.theme = args.theme
-        WIKI.config.theming.darkMode = args.darkMode
+        if (!_.isEmpty(args.injectCSS)) {
+          args.injectCSS = new CleanCSS({
+            inline: false
+          }).minify(args.injectCSS).styles
+        }
+
+        WIKI.config.theming = {
+          ...WIKI.config.theming,
+          theme: args.theme,
+          darkMode: args.darkMode,
+          injectCSS: args.injectCSS || '',
+          injectHead: args.injectHead || '',
+          injectBody: args.injectBody || ''
+        }
+
         await WIKI.configSvc.saveToDb(['theming'])
         await WIKI.configSvc.saveToDb(['theming'])
 
 
         return {
         return {

+ 8 - 2
server/graph/schemas/theming.graphql

@@ -27,6 +27,9 @@ type ThemingMutation {
   setConfig(
   setConfig(
     theme: String!
     theme: String!
     darkMode: Boolean!
     darkMode: Boolean!
+    injectCSS: String
+    injectHead: String
+    injectBody: String
   ): DefaultResponse @auth(requires: ["manage:theme", "manage:system"])
   ): DefaultResponse @auth(requires: ["manage:theme", "manage:system"])
 }
 }
 
 
@@ -35,8 +38,11 @@ type ThemingMutation {
 # -----------------------------------------------
 # -----------------------------------------------
 
 
 type ThemingConfig {
 type ThemingConfig {
-  theme: String
-  darkMode: Boolean
+  theme: String!
+  darkMode: Boolean!
+  injectCSS: String
+  injectHead: String
+  injectBody: String
 }
 }
 
 
 type ThemingTheme {
 type ThemingTheme {

+ 6 - 0
server/views/page.pug

@@ -1,6 +1,10 @@
 extends master.pug
 extends master.pug
 
 
 block head
 block head
+  if injectCode.css
+    style(type='text/css')!= injectCode.css
+  if injectCode.head
+    != injectCode.head
 
 
 block body
 block body
   #root
   #root
@@ -31,3 +35,5 @@ block body
           else if navItem.kind === 'header'
           else if navItem.kind === 'header'
             v-subheader.pl-4= navItem.label
             v-subheader.pl-4= navItem.label
       template(slot='contents')!= page.render
       template(slot='contents')!= page.render
+  if injectCode.body
+    != injectCode.body