diff --git a/client/components/admin/admin-rendering.vue b/client/components/admin/admin-rendering.vue
index 8e3c2e3e..cdca6214 100644
--- a/client/components/admin/admin-rendering.vue
+++ b/client/components/admin/admin-rendering.vue
@@ -6,141 +6,45 @@
.subheading.grey--text Configure how content is rendered
v-layout.mt-3(row wrap)
v-flex(lg3 xs12)
- v-card
- v-toolbar(
- color='primary'
- dense
- flat
- dark
+ v-toolbar(
+ color='primary'
+ dense
+ flat
+ dark
+ )
+ v-icon.mr-2 line_weight
+ .subheading Pipeline
+ v-expansion-panel.adm-rendering-pipeline
+ v-expansion-panel-content(
+ hide-actions
+ v-for='core in cores'
+ :key='core.key'
)
- v-icon.mr-2 line_weight
- .subheading Pipeline
- v-toolbar(
- color='blue'
- dense
- dark
- )
- v-icon.mr-2 arrow_downward
- .body-2 Markdown
- v-spacer
- v-btn(
- icon
- @click=''
+ v-toolbar(
+ slot='header'
+ color='blue'
+ dense
+ dark
+ flat
)
- v-icon add
- v-list(two-line, dense)
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') crop_free
- v-list-tile-content
- v-list-tile-title Core
- v-list-tile-sub-title Basic Markdown parser
- v-list-tile-avatar
- v-icon(color='green', small) lens
- v-divider.my-0
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') tag_faces
- v-list-tile-content
- v-list-tile-title Emoji
- v-list-tile-sub-title Convert tags to emojis
- v-list-tile-avatar
- v-icon(color='green', small) lens
- v-divider.my-0
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') list
- v-list-tile-content
- v-list-tile-title Task Lists
- v-list-tile-sub-title Parse task lists to checkboxes
- v-list-tile-avatar
- v-icon(color='green', small) lens
- v-divider.my-0
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') multiline_chart
- v-list-tile-content
- v-list-tile-title PlantUML
- v-list-tile-sub-title Generate diagrams from PlantUML syntax
- v-list-tile-avatar
- v-icon(color='green', small) lens
- v-divider.my-0
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') merge_type
- v-list-tile-content
- v-list-tile-title Mermaid
- v-list-tile-sub-title Generate flowcharts from Mermaid syntax
- v-list-tile-avatar
- v-icon(color='green', small) lens
- v-divider.my-0
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') functions
- v-list-tile-content
- v-list-tile-title Mathjax Pre-Processor
- v-list-tile-sub-title Parse Mathjax content from Markdown
- v-list-tile-avatar
- v-icon(color='red', small) trip_origin
-
- v-toolbar(
- color='blue'
- dense
- dark
- )
- v-icon.mr-2 arrow_downward
- .body-2 HTML
- v-spacer
- v-btn(
- icon
- @click=''
- )
- v-icon add
- v-list(two-line, dense)
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') subscriptions
- v-list-tile-content
- v-list-tile-title Video Players
- v-list-tile-sub-title Embed video players such as Youtube, Vimeo, etc.
- v-list-tile-avatar
- v-icon(color='green', small) lens
- v-divider.my-0
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') subtitles
- v-list-tile-content
- v-list-tile-title Asciinema
- v-list-tile-sub-title Embed asciinema players from compatible links
- v-list-tile-avatar
- v-icon(color='green', small) lens
- v-divider.my-0
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') volume_up
- v-list-tile-content
- v-list-tile-title Audio Players
- v-list-tile-sub-title Embed audio players for audio content
- v-list-tile-avatar
- v-icon(color='green', small) lens
- v-divider.my-0
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') insert_comment
- v-list-tile-content
- v-list-tile-title Blockquotes
- v-list-tile-sub-title Process styled blockquotes
- v-list-tile-avatar
- v-icon(color='green', small) lens
- v-divider.my-0
- v-list-tile(avatar)
- v-list-tile-avatar
- v-icon(color='grey') functions
- v-list-tile-content
- v-list-tile-title Mathjax Processor
- v-list-tile-sub-title TeX/MathML Math Equations Parser
- v-list-tile-avatar
- v-icon(color='red', small) trip_origin
+ .body-2 {{core.input}}
+ v-icon.mx-2 arrow_forward
+ .caption {{core.output}}
+ v-list(two-line, dense)
+ v-list-tile(
+ avatar
+ v-for='rdr in core.children'
+ :key='rdr.key'
+ )
+ v-list-tile-avatar
+ v-icon(color='grey') {{rdr.icon}}
+ v-list-tile-content
+ v-list-tile-title {{rdr.title}}
+ v-list-tile-sub-title {{rdr.description}}
+ v-list-tile-avatar
+ v-icon(color='green', small, v-if='rdr.isEnabled') lens
+ v-icon(color='red', small, v-else) trip_origin
+ v-divider.my-0
v-flex(lg9 xs12)
v-card
@@ -201,16 +105,45 @@
diff --git a/client/components/setup.vue b/client/components/setup.vue
index 40a21f84..8b261d05 100644
--- a/client/components/setup.vue
+++ b/client/components/setup.vue
@@ -14,22 +14,14 @@
small Wiki.js Installation Wizard
v-divider
v-stepper-step(step='2' :complete='state > 2')
- | System Check
- small Checking your system for compatibility
- v-divider
- v-stepper-step(step='3' :complete='state > 3')
- | General Information
- small Site Title, Language and Access
- v-divider
- v-stepper-step(step='4' :complete='state > 4')
| Administration Account
small Create the admin account
v-divider(v-if='this.conf.upgrade')
- v-stepper-step(step='5' :complete='state > 5', v-if='this.conf.upgrade')
+ v-stepper-step(step='3' :complete='state > 3', v-if='this.conf.upgrade')
| Upgrade from Wiki.js 1.x
small Migrate your existing installation
v-divider
- v-stepper-step(:step='this.conf.upgrade ? 6 : 5' :complete='this.conf.upgrade ? state > 5 : state > 6')
+ v-stepper-step(:step='this.conf.upgrade ? 4 : 3' :complete='this.conf.upgrade ? state > 3 : state > 4')
| Final Steps
small Finalizing your installation
@@ -68,126 +60,13 @@
)
v-divider
.pt-3.text-xs-center
- v-btn(color='primary', @click='proceedToSyscheck', :disabled='loading') Start
-
- //- ==============================================
- //- SYSTEM CHECK
- //- ==============================================
-
- v-stepper-content(step='2')
- v-card.text-xs-center(flat)
- svg.icons.is-64: use(xlink:href='#nc-metrics')
- .subheading System Check
- v-container
- v-layout(row, align-center, v-if='loading')
- v-progress-circular(v-if='loading', indeterminate, color='blue')
- .body-2.blue--text.ml-3 Checking your system for compatibility...
- v-alert(type='success', outline, :value='!loading && syscheck.ok') Looks good! No issues so far.
- v-alert(type='error', outline, :value='!loading && !syscheck.ok') {{ syscheck.error }}
- v-list.mt-3(two-line, v-if='!loading && syscheck.ok', dense)
- template(v-for='(rs, n) in syscheck.results')
- v-divider(v-if='n > 0', inset)
- v-list-tile
- v-list-tile-avatar(color='green lighten-5')
- v-icon(color='green') check
- v-list-tile-content
- v-list-tile-title {{rs.title}}
- v-list-tile-sub-title {{rs.subtitle}}
- v-divider
- .pt-3.text-xs-center
- v-btn(@click='proceedToWelcome', :disabled='loading') Back
- v-btn(color='primary', @click='proceedToSyscheck', v-if='!loading && !syscheck.ok') Check Again
- v-btn(color='red', dark, @click='proceedToGeneral', v-if='!loading && !syscheck.ok') Continue Anyway
- v-btn(color='primary', @click='proceedToGeneral', v-if='loading || syscheck.ok', :disabled='loading') Continue
-
- //- ==============================================
- //- GENERAL
- //- ==============================================
-
- v-stepper-content(step='3')
- v-card.text-xs-center(flat)
- svg.icons.is-64: use(xlink:href='#nc-webpage')
- .subheading General Information
- v-form
- v-container
- v-layout(row, wrap)
- v-flex(xs12, sm6).pr-3
- v-text-field(
- outline
- background-color='grey lighten-2'
- v-model='conf.title',
- label='Site Title',
- :counter='255',
- persistent-hint,
- hint='The site title will appear in the top left corner on every page and within the window title bar.',
- v-validate='{ required: true, min: 2 }',
- data-vv-name='siteTitle',
- data-vv-as='Site Title',
- data-vv-scope='general',
- :error-messages='errors.collect(`siteTitle`)'
- )
- v-flex.pr-3(xs12, sm6)
- v-text-field(
- outline
- background-color='grey lighten-2'
- v-model='conf.port',
- label='Server Port',
- persistent-hint,
- hint='The port on which Wiki.js will listen to. Usually port 80 if connecting directly, or a random port (e.g. 3000) if using a web server in front of it. Set $(PORT) to use the PORT environment variable.',
- v-validate='{ required: true }',
- data-vv-name='port',
- data-vv-as='Port',
- data-vv-scope='general',
- :error-messages='errors.collect(`port`)'
- )
- v-layout(row, wrap).mt-3
- v-flex(xs12, sm6).pr-3
- v-text-field(
- outline
- background-color='grey lighten-2'
- v-model='conf.pathContent',
- label='Content Data Path',
- persistent-hint,
- hint='The path where content is stored (markdown files, uploads, etc.)',
- v-validate='{ required: true, min: 2 }',
- data-vv-name='pathContent',
- data-vv-as='Content Data Path',
- data-vv-scope='general',
- :error-messages='errors.collect(`pathContent`)'
- )
- v-flex(xs12, sm6)
- v-text-field(
- outline
- background-color='grey lighten-2'
- v-model='conf.pathData',
- label='Temporary Data Path',
- persistent-hint,
- hint='The path where temporary data is stored (cache, thumbnails, temporary upload files, etc.)',
- v-validate='{ required: true, min: 2 }',
- data-vv-name='pathData',
- data-vv-as='Temporary Data Path',
- data-vv-scope='general',
- :error-messages='errors.collect(`pathData`)'
- )
- v-layout(row, wrap).mt-3
- v-flex(xs12)
- v-checkbox(
- color='primary',
- v-model='conf.public',
- label='Public Access',
- persistent-hint,
- hint='Should the site be accessible (read only) without login.'
- )
- v-divider
- .pt-3.text-xs-center
- v-btn(@click='proceedToSyscheck', :disabled='loading') Back
- v-btn(color='primary', @click='proceedToAdmin', :disabled='loading') Continue
+ v-btn(color='primary', @click='proceedToAdmin', :disabled='loading') Start
//- ==============================================
//- ADMINISTRATOR ACCOUNT
//- ==============================================
- v-stepper-content(step='4')
+ v-stepper-content(step='2')
v-card.text-xs-center(flat)
svg.icons.is-64: use(xlink:href='#nc-man-black')
.subheading Administrator Account
@@ -245,14 +124,14 @@
:error-messages='errors.collect(`adminPasswordConfirm`)'
)
.pt-3.text-xs-center
- v-btn(@click='proceedToGeneral', :disabled='loading') Back
+ v-btn(@click='proceedToWelcome', :disabled='loading') Back
v-btn(color='primary', @click='proceedToUpgrade', :disabled='loading') Continue
//- ==============================================
//- UPGRADE FROM 1.x
//- ==============================================
- v-stepper-content(step='5', v-if='conf.upgrade')
+ v-stepper-content(step='3', v-if='conf.upgrade')
v-card.text-xs-center(flat)
svg.icons.is-64: use(xlink:href='#nc-spaceship')
.subheading Upgrade from Wiki.js 1.x
@@ -283,7 +162,7 @@
//- FINAL
//- ==============================================
- v-stepper-content(:step='conf.upgrade ? 6 : 5')
+ v-stepper-content(:step='conf.upgrade ? 4 : 3')
v-card.text-xs-center(flat)
template(v-if='loading')
.mt-3(style='width: 64px; display:inline-block;')
@@ -340,11 +219,6 @@ export default {
return {
loading: false,
state: 1,
- syscheck: {
- ok: false,
- error: '',
- results: []
- },
final: {
ok: false,
error: '',
@@ -354,13 +228,7 @@ export default {
adminEmail: '',
adminPassword: '',
adminPasswordConfirm: '',
- lang: siteConfig.lang || 'en',
- pathData: './data',
- pathContent: './content',
- port: siteConfig.port || 80,
- public: (siteConfig.public === true),
telemetry: true,
- title: siteConfig.title || 'Wiki',
upgrade: false,
upgMongo: 'mongodb://'
},
@@ -373,73 +241,43 @@ export default {
this.state = 1
this.loading = false
},
- proceedToSyscheck () {
- let self = this
- this.state = 2
- this.loading = true
- this.syscheck = {
- ok: false,
- error: '',
- results: []
- }
-
- _.delay(() => {
- axios.post('/syscheck', self.conf).then(resp => {
- if (resp.data.ok === true) {
- self.syscheck.ok = true
- self.syscheck.results = resp.data.results
- } else {
- self.syscheck.ok = false
- self.syscheck.error = resp.data.error
- }
- self.loading = false
- self.$nextTick()
- }).catch(err => {
- window.alert(err.message)
- })
- }, 1000)
- },
- proceedToGeneral () {
- this.state = 3
- this.loading = false
- },
async proceedToAdmin () {
- if (this.state < 4) {
+ if (this.state < 2) {
const validationSuccess = await this.$validator.validateAll('general')
if (!validationSuccess) {
- this.state = 3
+ this.state = 1
return
}
}
- this.state = 4
+ this.state = 2
this.loading = false
},
async proceedToUpgrade () {
- if (this.state < 5) {
+ if (this.state < 3) {
const validationSuccess = await this.$validator.validateAll('admin')
if (!validationSuccess || this.conf.adminPassword !== this.conf.adminPasswordConfirm) {
- this.state = 4
+ this.state = 2
return
}
}
if (this.conf.upgrade) {
- this.state = 5
+ this.state = 3
this.loading = false
} else {
this.proceedToFinal()
}
},
async proceedToFinal () {
- if (this.conf.upgrade && this.state < 6) {
+ if (this.conf.upgrade && this.state < 4) {
const validationSuccess = await this.$validator.validateAll('upgrade')
if (!validationSuccess) {
- this.state = 5
+ this.state = 3
return
}
}
- this.state = this.conf.upgrade ? 6 : 5
+ this.state = this.conf.upgrade ? 4 : 3
this.loading = true
this.final = {
ok: false,
diff --git a/client/graph/admin/rendering/rendering-mutation-save-renderers.gql b/client/graph/admin/rendering/rendering-mutation-save-renderers.gql
new file mode 100644
index 00000000..8a717785
--- /dev/null
+++ b/client/graph/admin/rendering/rendering-mutation-save-renderers.gql
@@ -0,0 +1,12 @@
+mutation($renderers: [RendererInput]) {
+ rendering {
+ updateRenderers(renderers: $renderers) {
+ responseResult {
+ succeeded
+ errorCode
+ slug
+ message
+ }
+ }
+ }
+}
diff --git a/client/graph/admin/rendering/rendering-query-renderers.gql b/client/graph/admin/rendering/rendering-query-renderers.gql
new file mode 100644
index 00000000..b117b7ab
--- /dev/null
+++ b/client/graph/admin/rendering/rendering-query-renderers.gql
@@ -0,0 +1,18 @@
+{
+ rendering {
+ renderers {
+ isEnabled
+ key
+ title
+ description
+ icon
+ dependsOn
+ input
+ output
+ config {
+ key
+ value
+ }
+ }
+ }
+}
diff --git a/server/core/kernel.js b/server/core/kernel.js
index 3c389c52..44647146 100644
--- a/server/core/kernel.js
+++ b/server/core/kernel.js
@@ -51,6 +51,7 @@ module.exports = {
async postBootMaster() {
await WIKI.models.authentication.refreshStrategiesFromDisk()
await WIKI.models.editors.refreshEditorsFromDisk()
+ await WIKI.models.renderers.refreshRenderersFromDisk()
await WIKI.models.storage.refreshTargetsFromDisk()
await WIKI.auth.activateStrategies()
diff --git a/server/db/migrations/2.0.0.js b/server/db/migrations/2.0.0.js
index 380e7e48..e441cad1 100644
--- a/server/db/migrations/2.0.0.js
+++ b/server/db/migrations/2.0.0.js
@@ -96,7 +96,7 @@ exports.up = knex => {
table.string('createdAt').notNullable()
table.string('updatedAt').notNullable()
})
- // STORAGE -----------------------------
+ // RENDERERS ---------------------------
.createTable('renderers', table => {
table.increments('id').primary()
table.string('key').notNullable().unique()
@@ -199,14 +199,19 @@ exports.up = knex => {
exports.down = knex => {
return knex.schema
.dropTableIfExists('userGroups')
+ .dropTableIfExists('pageHistoryTags')
+ .dropTableIfExists('pageHistory')
.dropTableIfExists('pageTags')
.dropTableIfExists('assets')
.dropTableIfExists('assetFolders')
.dropTableIfExists('comments')
+ .dropTableIfExists('editors')
.dropTableIfExists('groups')
.dropTableIfExists('locales')
.dropTableIfExists('pages')
+ .dropTableIfExists('renderers')
.dropTableIfExists('settings')
+ .dropTableIfExists('storage')
.dropTableIfExists('tags')
.dropTableIfExists('users')
}
diff --git a/server/graph/resolvers/rendering.js b/server/graph/resolvers/rendering.js
new file mode 100644
index 00000000..8917fba4
--- /dev/null
+++ b/server/graph/resolvers/rendering.js
@@ -0,0 +1,58 @@
+const _ = require('lodash')
+const graphHelper = require('../../helpers/graph')
+
+/* global WIKI */
+
+module.exports = {
+ Query: {
+ async rendering() { return {} }
+ },
+ Mutation: {
+ async rendering() { return {} }
+ },
+ RenderingQuery: {
+ async renderers(obj, args, context, info) {
+ let renderers = await WIKI.models.renderers.getRenderers()
+ renderers = renderers.map(rdr => {
+ const rendererInfo = _.find(WIKI.data.renderers, ['key', rdr.key]) || {}
+ return {
+ ...rendererInfo,
+ ...rdr,
+ config: _.sortBy(_.transform(rdr.config, (res, value, key) => {
+ const configData = _.get(rendererInfo.props, key, {})
+ res.push({
+ key,
+ value: JSON.stringify({
+ ...configData,
+ value
+ })
+ })
+ }, []), 'key')
+ }
+ })
+ if (args.filter) { renderers = graphHelper.filter(renderers, args.filter) }
+ if (args.orderBy) { renderers = graphHelper.orderBy(renderers, args.orderBy) }
+ return renderers
+ }
+ },
+ RenderingMutation: {
+ async updateRenderers(obj, args, context) {
+ try {
+ for (let rdr of args.renderers) {
+ await WIKI.models.storage.query().patch({
+ isEnabled: rdr.isEnabled,
+ config: _.reduce(rdr.config, (result, value, key) => {
+ _.set(result, `${value.key}`, value.value)
+ return result
+ }, {})
+ }).where('key', rdr.key)
+ }
+ return {
+ responseResult: graphHelper.generateSuccess('Renderers updated successfully')
+ }
+ } catch (err) {
+ return graphHelper.generateError(err)
+ }
+ }
+ }
+}
diff --git a/server/graph/schemas/rendering.graphql b/server/graph/schemas/rendering.graphql
new file mode 100644
index 00000000..13d5eb0f
--- /dev/null
+++ b/server/graph/schemas/rendering.graphql
@@ -0,0 +1,55 @@
+# ===============================================
+# RENDERING
+# ===============================================
+
+extend type Query {
+ rendering: RenderingQuery
+}
+
+extend type Mutation {
+ rendering: RenderingMutation
+}
+
+# -----------------------------------------------
+# QUERIES
+# -----------------------------------------------
+
+type RenderingQuery {
+ renderers(
+ filter: String
+ orderBy: String
+ ): [Renderer]
+}
+
+# -----------------------------------------------
+# MUTATIONS
+# -----------------------------------------------
+
+type RenderingMutation {
+ updateRenderers(
+ renderers: [RendererInput]
+ ): DefaultResponse
+}
+
+# -----------------------------------------------
+# TYPES
+# -----------------------------------------------
+
+type Renderer {
+ isEnabled: Boolean!
+ key: String!
+ title: String!
+ description: String
+ icon: String
+ dependsOn: String
+ input: String
+ output: String
+ config: [KeyValuePair]
+}
+
+input RendererInput {
+ isEnabled: Boolean!
+ key: String!
+ mode: String!
+ config: [KeyValuePairInput]
+}
diff --git a/server/models/renderers.js b/server/models/renderers.js
new file mode 100644
index 00000000..96b9d524
--- /dev/null
+++ b/server/models/renderers.js
@@ -0,0 +1,108 @@
+const Model = require('objection').Model
+const path = require('path')
+const fs = require('fs-extra')
+const _ = require('lodash')
+const yaml = require('js-yaml')
+const commonHelper = require('../helpers/common')
+
+/* global WIKI */
+
+/**
+ * Renderer model
+ */
+module.exports = class Renderer extends Model {
+ static get tableName() { return 'renderers' }
+
+ static get jsonSchema () {
+ return {
+ type: 'object',
+ required: ['key', 'isEnabled'],
+
+ properties: {
+ id: {type: 'integer'},
+ key: {type: 'string'},
+ isEnabled: {type: 'boolean'},
+ config: {type: 'object'}
+ }
+ }
+ }
+
+ static async getRenderers() {
+ return WIKI.models.renderers.query()
+ }
+
+ static async refreshRenderersFromDisk() {
+ let trx
+ try {
+ const dbRenderers = await WIKI.models.renderers.query()
+
+ // -> Fetch definitions from disk
+ const rendererDirs = await fs.readdir(path.join(WIKI.SERVERPATH, 'modules/renderer'))
+ let diskRenderers = []
+ for (let dir of rendererDirs) {
+ const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/renderer', dir, 'definition.yml'), 'utf8')
+ diskRenderers.push(yaml.safeLoad(def))
+ }
+ WIKI.data.renderers = diskRenderers.map(renderer => ({
+ ...renderer,
+ props: commonHelper.parseModuleProps(renderer.props)
+ }))
+
+ // -> Insert new Renderers
+ let newRenderers = []
+ for (let renderer of WIKI.data.renderers) {
+ if (!_.some(dbRenderers, ['key', renderer.key])) {
+ newRenderers.push({
+ key: renderer.key,
+ isEnabled: _.get(renderer, 'enabledDefault', true),
+ config: _.transform(renderer.props, (result, value, key) => {
+ _.set(result, key, value.default)
+ return result
+ }, {})
+ })
+ } else {
+ const rendererConfig = _.get(_.find(dbRenderers, ['key', renderer.key]), 'config', {})
+ await WIKI.models.renderers.query().patch({
+ config: _.transform(renderer.props, (result, value, key) => {
+ if (!_.has(result, key)) {
+ _.set(result, key, value.default)
+ }
+ return result
+ }, rendererConfig)
+ }).where('key', renderer.key)
+ }
+ }
+ if (newRenderers.length > 0) {
+ trx = await WIKI.models.Objection.transaction.start(WIKI.models.knex)
+ for (let renderer of newRenderers) {
+ await WIKI.models.renderers.query(trx).insert(renderer)
+ }
+ await trx.commit()
+ WIKI.logger.info(`Loaded ${newRenderers.length} new renderers: [ OK ]`)
+ } else {
+ WIKI.logger.info(`No new renderers found: [ SKIPPED ]`)
+ }
+ } catch (err) {
+ WIKI.logger.error(`Failed to scan or load new renderers: [ FAILED ]`)
+ WIKI.logger.error(err)
+ if (trx) {
+ trx.rollback()
+ }
+ }
+ }
+
+ static async pageEvent({ event, page }) {
+ const targets = await WIKI.models.storage.query().where('isEnabled', true)
+ if (targets && targets.length > 0) {
+ _.forEach(targets, target => {
+ WIKI.queue.job.syncStorage.add({
+ event,
+ target,
+ page
+ }, {
+ removeOnComplete: true
+ })
+ })
+ }
+ }
+}
diff --git a/server/modules/renderer/html-asciinema/definition.yml b/server/modules/renderer/html-asciinema/definition.yml
index 0454c0ea..f1f78ff7 100644
--- a/server/modules/renderer/html-asciinema/definition.yml
+++ b/server/modules/renderer/html-asciinema/definition.yml
@@ -1,7 +1,8 @@
-key: markdownAbbr
-title: Abbreviations
-description: Parse abbreviations into abbr tags
+key: htmlAsciinema
+title: Asciinema
+description: Embed asciinema players from compatible links
author: requarks.io
-dependsOn:
- - markdownCore
+icon: subtitles
+enabledDefault: false
+dependsOn: htmlCore
props: {}
diff --git a/server/modules/renderer/html-blockquotes/definition.yml b/server/modules/renderer/html-blockquotes/definition.yml
index 0454c0ea..e299688e 100644
--- a/server/modules/renderer/html-blockquotes/definition.yml
+++ b/server/modules/renderer/html-blockquotes/definition.yml
@@ -1,7 +1,8 @@
-key: markdownAbbr
-title: Abbreviations
-description: Parse abbreviations into abbr tags
+key: htmlBlockquotes
+title: Blockquotes
+description: Embed audio players for audio content
author: requarks.io
-dependsOn:
- - markdownCore
+icon: insert_comment
+enabledDefault: true
+dependsOn: htmlCore
props: {}
diff --git a/server/modules/renderer/html-core/definition.yml b/server/modules/renderer/html-core/definition.yml
new file mode 100644
index 00000000..e7108d52
--- /dev/null
+++ b/server/modules/renderer/html-core/definition.yml
@@ -0,0 +1,8 @@
+key: htmlCore
+title: Core
+description: Basic HTML Parser
+author: requarks.io
+input: html
+output: html
+icon: crop_free
+props: {}
diff --git a/server/modules/renderer/html-core/renderer.js b/server/modules/renderer/html-core/renderer.js
new file mode 100644
index 00000000..4ba52ba2
--- /dev/null
+++ b/server/modules/renderer/html-core/renderer.js
@@ -0,0 +1 @@
+module.exports = {}
diff --git a/server/modules/renderer/html-mathjax/definition.yml b/server/modules/renderer/html-mathjax/definition.yml
index 0454c0ea..e892cb09 100644
--- a/server/modules/renderer/html-mathjax/definition.yml
+++ b/server/modules/renderer/html-mathjax/definition.yml
@@ -1,7 +1,8 @@
-key: markdownAbbr
-title: Abbreviations
-description: Parse abbreviations into abbr tags
+key: htmlMathjax
+title: Mathjax Processor
+description: TeX/MathML Math Equations Parser
author: requarks.io
-dependsOn:
- - markdownCore
+icon: functions
+enabledDefault: false
+dependsOn: htmlCore
props: {}
diff --git a/server/modules/renderer/html-mediaplayers/definition.yml b/server/modules/renderer/html-mediaplayers/definition.yml
index 0454c0ea..85119dc5 100644
--- a/server/modules/renderer/html-mediaplayers/definition.yml
+++ b/server/modules/renderer/html-mediaplayers/definition.yml
@@ -1,7 +1,8 @@
-key: markdownAbbr
-title: Abbreviations
-description: Parse abbreviations into abbr tags
+key: htmlMedia
+title: Media Players
+description: Embed players such as Youtube, Vimeo, Soundcloud, etc.
author: requarks.io
-dependsOn:
- - markdownCore
+icon: subscriptions
+enabledDefault: false
+dependsOn: htmlCore
props: {}
diff --git a/server/modules/renderer/html-security/definition.yml b/server/modules/renderer/html-security/definition.yml
index 0454c0ea..7171c9c6 100644
--- a/server/modules/renderer/html-security/definition.yml
+++ b/server/modules/renderer/html-security/definition.yml
@@ -1,7 +1,8 @@
-key: markdownAbbr
-title: Abbreviations
-description: Parse abbreviations into abbr tags
+key: htmlSecurity
+title: Security
+description: Filter and strips potentially dangerous content
author: requarks.io
-dependsOn:
- - markdownCore
+icon: whatshot
+enabledDefault: true
+dependsOn: htmlCore
props: {}
diff --git a/server/modules/renderer/markdown-abbr/definition.yml b/server/modules/renderer/markdown-abbr/definition.yml
index 0454c0ea..ceab9fc7 100644
--- a/server/modules/renderer/markdown-abbr/definition.yml
+++ b/server/modules/renderer/markdown-abbr/definition.yml
@@ -2,6 +2,7 @@ key: markdownAbbr
title: Abbreviations
description: Parse abbreviations into abbr tags
author: requarks.io
-dependsOn:
- - markdownCore
+icon: text_format
+enabledDefault: true
+dependsOn: markdownCore
props: {}
diff --git a/server/modules/renderer/markdown-core/definition.yml b/server/modules/renderer/markdown-core/definition.yml
index 6c2cb483..87939d42 100644
--- a/server/modules/renderer/markdown-core/definition.yml
+++ b/server/modules/renderer/markdown-core/definition.yml
@@ -2,7 +2,9 @@ key: markdownCore
title: Core
description: Basic Markdown Parser
author: requarks.io
-dependsOn: []
+input: markdown
+output: html
+icon: crop_free
props:
linkify:
type: Boolean
diff --git a/server/modules/renderer/markdown-emoji/definition.yml b/server/modules/renderer/markdown-emoji/definition.yml
index 3220194b..a816adb4 100644
--- a/server/modules/renderer/markdown-emoji/definition.yml
+++ b/server/modules/renderer/markdown-emoji/definition.yml
@@ -2,6 +2,7 @@ key: markdownEmoji
title: Emoji
description: Convert tags to emojis
author: requarks.io
-dependsOn:
- - markdownCore
+icon: tag_faces
+enabledDefault: true
+dependsOn: markdownCore
props: {}
diff --git a/server/modules/renderer/markdown-expandtabs/definition.yml b/server/modules/renderer/markdown-expandtabs/definition.yml
index 92f7cada..3775f1fa 100644
--- a/server/modules/renderer/markdown-expandtabs/definition.yml
+++ b/server/modules/renderer/markdown-expandtabs/definition.yml
@@ -2,6 +2,7 @@ key: markdownExpandtabs
title: Expand Tabs
description: Replace tabs with spaces in code blocks
author: requarks.io
-dependsOn:
- - markdownCore
+icon: space_bar
+enabledDefault: true
+dependsOn: markdownCore
props: {}
diff --git a/server/modules/renderer/markdown-footnotes/definition.yml b/server/modules/renderer/markdown-footnotes/definition.yml
index 44913865..8f1a162c 100644
--- a/server/modules/renderer/markdown-footnotes/definition.yml
+++ b/server/modules/renderer/markdown-footnotes/definition.yml
@@ -2,6 +2,7 @@ key: markdownFootnotes
title: Footnotes
description: Parse footnotes references
author: requarks.io
-dependsOn:
- - markdownCore
+icon: low_priority
+enabledDefault: true
+dependsOn: markdownCore
props: {}
diff --git a/server/modules/renderer/markdown-mathjax/definition.yml b/server/modules/renderer/markdown-mathjax/definition.yml
index dd7b1c84..b6e2437a 100644
--- a/server/modules/renderer/markdown-mathjax/definition.yml
+++ b/server/modules/renderer/markdown-mathjax/definition.yml
@@ -2,6 +2,7 @@ key: markdownMathjax
title: Mathjax Pre-Processor
description: Pre-parse TeX blocks for Mathjax
author: requarks.io
-dependsOn:
- - markdownCore
+icon: functions
+enabledDefault: false
+dependsOn: markdownCore
props: {}
diff --git a/server/modules/renderer/markdown-mermaid/definition.yml b/server/modules/renderer/markdown-mermaid/definition.yml
index 573e6e96..205505b6 100644
--- a/server/modules/renderer/markdown-mermaid/definition.yml
+++ b/server/modules/renderer/markdown-mermaid/definition.yml
@@ -2,6 +2,7 @@ key: markdownMermaid
title: Mermaid
description: Generate flowcharts from Mermaid syntax
author: requarks.io
-dependsOn:
- - markdownCore
+icon: merge_type
+enabledDefault: false
+dependsOn: markdownCore
props: {}
diff --git a/server/modules/renderer/markdown-plantuml/definition.yml b/server/modules/renderer/markdown-plantuml/definition.yml
index 0a24302d..5f36cc29 100644
--- a/server/modules/renderer/markdown-plantuml/definition.yml
+++ b/server/modules/renderer/markdown-plantuml/definition.yml
@@ -2,6 +2,7 @@ key: markdownPlantuml
title: PlantUML
description: Generate diagrams from PlantUML syntax
author: requarks.io
-dependsOn:
- - markdownCore
+icon: multiline_chart
+enabledDefault: false
+dependsOn: markdownCore
props: {}
diff --git a/server/modules/renderer/markdown-tasklists/definition.yml b/server/modules/renderer/markdown-tasklists/definition.yml
index 27c5410f..7d8212da 100644
--- a/server/modules/renderer/markdown-tasklists/definition.yml
+++ b/server/modules/renderer/markdown-tasklists/definition.yml
@@ -2,6 +2,7 @@ key: markdownTasklists
title: Task Lists
description: Parse task lists to checkboxes
author: requarks.io
-dependsOn:
- - markdownCore
+icon: list
+enabledDefault: true
+dependsOn: markdownCore
props: {}
diff --git a/server/setup.js b/server/setup.js
index d39c0b57..ea056ebd 100644
--- a/server/setup.js
+++ b/server/setup.js
@@ -24,7 +24,6 @@ module.exports = () => {
const yaml = require('js-yaml')
const _ = require('lodash')
const cfgHelper = require('./helpers/config')
- const filesize = require('filesize.js')
const crypto = Promise.promisifyAll(require('crypto'))
// ----------------------------------------
@@ -76,174 +75,6 @@ module.exports = () => {
})
})
- /**
- * Perform basic system checks
- */
- app.post('/syscheck', (req, res) => {
- WIKI.telemetry.enabled = (req.body.telemetry === true)
- WIKI.telemetry.sendEvent('setup', 'start')
-
- Promise.mapSeries([
- () => {
- const semver = require('semver')
- if (!semver.satisfies(semver.clean(process.version), '>=8.9.0')) {
- throw new Error('Node.js version is too old. Minimum is 8.9.0.')
- }
- return {
- title: 'Node.js ' + process.version + ' detected.',
- subtitle: ' Minimum is 8.9.0.'
- }
- },
- () => {
- return Promise.try(() => {
- require('crypto')
- }).catch(err => {
- throw new Error('Crypto Node.js module is not available.')
- }).return({
- title: 'Node.js Crypto module is available.',
- subtitle: 'Crypto module is required.'
- })
- },
- () => {
- const exec = require('child_process').exec
- const semver = require('semver')
- return new Promise((resolve, reject) => {
- exec('git --version', (err, stdout, stderr) => {
- if (err || stdout.length < 3) {
- reject(new Error('Git is not installed or not reachable from PATH.'))
- }
- let gitver = _.head(stdout.match(/[\d]+\.[\d]+(\.[\d]+)?/gi))
- if (!gitver || !semver.satisfies(semver.clean(gitver), '>=2.7.4')) {
- reject(new Error('Git version is too old. Minimum is 2.7.4.'))
- }
- resolve({
- title: 'Git ' + gitver + ' detected.',
- subtitle: 'Minimum is 2.7.4.'
- })
- })
- })
- },
- () => {
- const os = require('os')
- if (os.totalmem() < 1000 * 1000 * 768) {
- throw new Error('Not enough memory. Minimum is 768 MB.')
- }
- return {
- title: filesize(os.totalmem()) + ' of system memory available.',
- subtitle: 'Minimum is 768 MB.'
- }
- },
- () => {
- let fs = require('fs')
- return Promise.try(() => {
- fs.accessSync(path.join(WIKI.ROOTPATH, 'config.yml'), (fs.constants || fs).W_OK)
- }).catch(err => {
- throw new Error('config.yml file is not writable by Node.js process or was not created properly.')
- }).return({
- title: 'config.yml is writable by the setup process.',
- subtitle: 'Setup will write to this file.'
- })
- }
- ], test => test()).then(results => {
- res.json({ ok: true, results })
- }).catch(err => {
- res.json({ ok: false, error: err.message })
- })
- })
-
- /**
- * Check the Git connection
- */
- app.post('/gitcheck', (req, res) => {
- WIKI.telemetry.sendEvent('setup', 'gitcheck')
-
- const exec = require('execa')
- const url = require('url')
-
- const dataDir = path.resolve(WIKI.ROOTPATH, cfgHelper.parseConfigValue(req.body.pathData))
- const gitDir = path.resolve(WIKI.ROOTPATH, cfgHelper.parseConfigValue(req.body.pathRepo))
-
- let gitRemoteUrl = ''
-
- if (req.body.gitUseRemote === true) {
- let urlObj = url.parse(cfgHelper.parseConfigValue(req.body.gitUrl))
- if (req.body.gitAuthType === 'basic') {
- urlObj.auth = req.body.gitAuthUser + ':' + req.body.gitAuthPass
- }
- gitRemoteUrl = url.format(urlObj)
- }
-
- Promise.mapSeries([
- () => {
- return fs.ensureDir(dataDir).then(() => 'Data directory path is valid.')
- },
- () => {
- return fs.ensureDir(gitDir).then(() => 'Git directory path is valid.')
- },
- () => {
- return exec.stdout('git', ['init'], { cwd: gitDir }).then(result => {
- return 'Local git repository has been initialized.'
- })
- },
- () => {
- if (req.body.gitUseRemote === false) { return false }
- return exec.stdout('git', ['config', '--local', 'user.name', 'Wiki'], { cwd: gitDir }).then(result => {
- return 'Git Signature Name has been set successfully.'
- })
- },
- () => {
- if (req.body.gitUseRemote === false) { return false }
- return exec.stdout('git', ['config', '--local', 'user.email', req.body.gitServerEmail], { cwd: gitDir }).then(result => {
- return 'Git Signature Name has been set successfully.'
- })
- },
- () => {
- if (req.body.gitUseRemote === false) { return false }
- return exec.stdout('git', ['config', '--local', '--bool', 'http.sslVerify', req.body.gitAuthSSL], { cwd: gitDir }).then(result => {
- return 'Git SSL Verify flag has been set successfully.'
- })
- },
- () => {
- if (req.body.gitUseRemote === false) { return false }
- if (_.includes(['sshenv', 'sshdb'], req.body.gitAuthType)) {
- req.body.gitAuthSSHKey = path.join(dataDir, 'ssh/key.pem')
- }
- if (_.startsWith(req.body.gitAuthType, 'ssh')) {
- return exec.stdout('git', ['config', '--local', 'core.sshCommand', 'ssh -i "' + req.body.gitAuthSSHKey + '" -o StrictHostKeyChecking=no'], { cwd: gitDir }).then(result => {
- return 'Git SSH Private Key path has been set successfully.'
- })
- } else {
- return false
- }
- },
- () => {
- if (req.body.gitUseRemote === false) { return false }
- return exec.stdout('git', ['remote', 'rm', 'origin'], { cwd: gitDir }).catch(err => {
- if (_.includes(err.message, 'No such remote') || _.includes(err.message, 'Could not remove')) {
- return true
- } else {
- throw err
- }
- }).then(() => {
- return exec.stdout('git', ['remote', 'add', 'origin', gitRemoteUrl], { cwd: gitDir }).then(result => {
- return 'Git Remote was added successfully.'
- })
- })
- },
- () => {
- if (req.body.gitUseRemote === false) { return false }
- return exec.stdout('git', ['pull', 'origin', req.body.gitBranch], { cwd: gitDir }).then(result => {
- return 'Git Pull operation successful.'
- })
- }
- ], step => { return step() }).then(results => {
- return res.json({ ok: true, results: _.without(results, false) })
- }).catch(err => {
- let errMsg = (err.stderr) ? err.stderr.replace(/(error:|warning:|fatal:)/gi, '').replace(/ \s+/g, ' ') : err.message
- res.json({ ok: false, error: errMsg })
- })
- })
-
/**
* Finalize
*/
@@ -263,13 +94,6 @@ module.exports = () => {
let confRaw = await fs.readFileAsync(path.join(WIKI.ROOTPATH, 'config.yml'), 'utf8')
let conf = yaml.safeLoad(confRaw)
- conf.port = req.body.port
- conf.paths.data = req.body.pathData
- conf.paths.content = req.body.pathContent
-
- confRaw = yaml.safeDump(conf)
- await fs.writeFileAsync(path.join(WIKI.ROOTPATH, 'config.yml'), confRaw)
-
// Create directory structure
await fs.ensureDir(conf.paths.data)
await fs.ensureDir(path.join(conf.paths.data, 'cache'))
@@ -283,16 +107,13 @@ module.exports = () => {
_.set(WIKI.config, 'lang.autoUpdate', true)
_.set(WIKI.config, 'lang.namespacing', false)
_.set(WIKI.config, 'lang.namespaces', [])
- _.set(WIKI.config, 'paths.content', req.body.pathContent)
- _.set(WIKI.config, 'paths.data', req.body.pathData)
- _.set(WIKI.config, 'port', req.body.port)
- _.set(WIKI.config, 'public', req.body.public === 'true')
+ _.set(WIKI.config, 'public', false)
_.set(WIKI.config, 'sessionSecret', (await crypto.randomBytesAsync(32)).toString('hex'))
_.set(WIKI.config, 'telemetry.isEnabled', req.body.telemetry === 'true')
_.set(WIKI.config, 'telemetry.clientId', WIKI.telemetry.cid)
_.set(WIKI.config, 'theming.theme', 'default')
_.set(WIKI.config, 'theming.darkMode', false)
- _.set(WIKI.config, 'title', req.body.title)
+ _.set(WIKI.config, 'title', 'Wiki.js')
// Save config to DB
WIKI.logger.info('Persisting config to DB...')
@@ -325,6 +146,9 @@ module.exports = () => {
await WIKI.models.editors.refreshEditorsFromDisk()
await WIKI.models.editors.query().patch({ isEnabled: true }).where('key', 'markdown')
+ // Load renderers
+ await WIKI.models.renderers.refreshRenderersFromDisk()
+
// Load storage targets
await WIKI.models.storage.refreshTargetsFromDisk()
@@ -367,7 +191,7 @@ module.exports = () => {
WIKI.logger.info('Setup is complete!')
res.json({
ok: true,
- redirectPath: WIKI.config.site.path,
+ redirectPath: '/',
redirectPort: WIKI.config.port
}).end()