diff --git a/server/db/migrations-sqlite/2.0.0-beta.1.js b/server/db/migrations-sqlite/2.0.0-beta.1.js index d354d237..2b6d7467 100644 --- a/server/db/migrations-sqlite/2.0.0-beta.1.js +++ b/server/db/migrations-sqlite/2.0.0-beta.1.js @@ -63,7 +63,7 @@ exports.up = knex => { }) // LOCALES ----------------------------- .createTable('locales', table => { - table.string('code', 2).notNullable().primary() + table.string('code', 5).notNullable().primary() table.json('strings') table.boolean('isRTL').notNullable().defaultTo(false) table.string('name').notNullable() @@ -100,7 +100,7 @@ exports.up = knex => { table.integer('pageId').unsigned().references('id').inTable('pages') table.string('editorKey').references('key').inTable('editors') - table.string('localeCode', 2).references('code').inTable('locales') + table.string('localeCode', 5).references('code').inTable('locales') table.integer('authorId').unsigned().references('id').inTable('users') }) // PAGES ------------------------------- @@ -123,7 +123,7 @@ exports.up = knex => { table.string('updatedAt').notNullable() table.string('editorKey').references('key').inTable('editors') - table.string('localeCode', 2).references('code').inTable('locales') + table.string('localeCode', 5).references('code').inTable('locales') table.integer('authorId').unsigned().references('id').inTable('users') table.integer('creatorId').unsigned().references('id').inTable('users') }) @@ -139,7 +139,7 @@ exports.up = knex => { table.integer('parent').unsigned().references('id').inTable('pageTree') table.integer('pageId').unsigned().references('id').inTable('pages') - table.string('localeCode', 2).references('code').inTable('locales') + table.string('localeCode', 5).references('code').inTable('locales') }) // RENDERERS --------------------------- .createTable('renderers', table => { @@ -204,7 +204,7 @@ exports.up = knex => { table.string('updatedAt').notNullable() table.string('providerKey').references('key').inTable('authentication').notNullable().defaultTo('local') - table.string('localeCode', 2).references('code').inTable('locales').notNullable().defaultTo('en') + table.string('localeCode', 5).references('code').inTable('locales').notNullable().defaultTo('en') table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown') }) // ===================================== diff --git a/server/db/migrations-sqlite/2.0.0-beta.11.js b/server/db/migrations-sqlite/2.0.0-beta.11.js index ef4580ac..e2b2e2de 100644 --- a/server/db/migrations-sqlite/2.0.0-beta.11.js +++ b/server/db/migrations-sqlite/2.0.0-beta.11.js @@ -18,7 +18,7 @@ exports.up = knex => { table.integer('pageId').unsigned() table.string('editorKey').references('key').inTable('editors') - table.string('localeCode', 2).references('code').inTable('locales') + table.string('localeCode', 5).references('code').inTable('locales') table.integer('authorId').unsigned().references('id').inTable('users') }) .raw(`INSERT INTO pageHistory SELECT id,path,hash,title,description,isPrivate,isPublished,publishStartDate,publishEndDate,content,contentType,createdAt,'updated' AS action,pageId,editorKey,localeCode,authorId FROM pageHistory_old;`) @@ -44,7 +44,7 @@ exports.down = knex => { table.integer('pageId').unsigned().references('id').inTable('pages') table.string('editorKey').references('key').inTable('editors') - table.string('localeCode', 2).references('code').inTable('locales') + table.string('localeCode', 5).references('code').inTable('locales') table.integer('authorId').unsigned().references('id').inTable('users') }) .raw('INSERT INTO pageHistory SELECT id,path,hash,title,description,isPrivate,isPublished,publishStartDate,publishEndDate,content,contentType,createdAt,NULL as pageId,editorKey,localeCode,authorId FROM pageHistory_old;') diff --git a/server/db/migrations-sqlite/2.0.0-beta.293.js b/server/db/migrations-sqlite/2.0.0-beta.293.js index 85fb8666..2524b79c 100644 --- a/server/db/migrations-sqlite/2.0.0-beta.293.js +++ b/server/db/migrations-sqlite/2.0.0-beta.293.js @@ -2,8 +2,12 @@ exports.up = knex => { return knex.schema .createTable('pageLinks', table => { table.increments('id').primary() - table.integer('sourcePageId').unsigned().references('id').inTable('pages').onDelete('CASCADE') - table.integer('targetPageId').unsigned().references('id').inTable('pages').onDelete('CASCADE') + table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE') + table.string('path').notNullable() + table.string('localeCode', 5).notNullable() + }) + .table('pageLinks', table => { + table.index(['path', 'localeCode']) }) } diff --git a/server/db/migrations/2.0.0-beta.293.js b/server/db/migrations/2.0.0-beta.293.js index dd8339f6..94b0273a 100644 --- a/server/db/migrations/2.0.0-beta.293.js +++ b/server/db/migrations/2.0.0-beta.293.js @@ -8,8 +8,12 @@ exports.up = knex => { .createTable('pageLinks', table => { if (dbCompat.charset) { table.charset('utf8mb4') } table.increments('id').primary() - table.integer('sourcePageId').unsigned().references('id').inTable('pages').onDelete('CASCADE') - table.integer('targetPageId').unsigned().references('id').inTable('pages').onDelete('CASCADE') + table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE') + table.string('path').notNullable() + table.string('localeCode', 5).notNullable() + }) + .table('pageLinks', table => { + table.index(['path', 'localeCode']) }) } diff --git a/server/models/pageLinks.js b/server/models/pageLinks.js new file mode 100644 index 00000000..980642f1 --- /dev/null +++ b/server/models/pageLinks.js @@ -0,0 +1,34 @@ +const Model = require('objection').Model + +/** + * Users model + */ +module.exports = class PageLink extends Model { + static get tableName() { return 'pageLinks' } + + static get jsonSchema () { + return { + type: 'object', + required: ['path', 'localeCode'], + + properties: { + id: {type: 'integer'}, + path: {type: 'string'}, + localeCode: {type: 'string'} + } + } + } + + static get relationMappings() { + return { + page: { + relation: Model.BelongsToOneRelation, + modelClass: require('./pages'), + join: { + from: 'pageLinks.pageId', + to: 'pages.id' + } + } + } + } +} diff --git a/server/models/pages.js b/server/models/pages.js index 95644b52..45a162cd 100644 --- a/server/models/pages.js +++ b/server/models/pages.js @@ -64,15 +64,11 @@ module.exports = class Page extends Model { } }, links: { - relation: Model.ManyToManyRelation, - modelClass: Page, + relation: Model.HasManyRelation, + modelClass: require('./pageLinks'), join: { from: 'pages.id', - through: { - from: 'pageLinks.sourcePageId', - to: 'pageLinks.targetPageId' - }, - to: 'pages.id' + to: 'pageLinks.pageId' } }, author: { diff --git a/server/modules/rendering/html-core/renderer.js b/server/modules/rendering/html-core/renderer.js index 3ee04bd7..c76fe6f5 100644 --- a/server/modules/rendering/html-core/renderer.js +++ b/server/modules/rendering/html-core/renderer.js @@ -31,8 +31,8 @@ module.exports = { $('a').each((i, elm) => { let href = $(elm).attr('href') - // -> Ignore empty links - if (!href || href.length < 1) { + // -> Ignore empty / anchor links + if (!href || href.length < 1 || href.indexOf('#') === 0) { return } @@ -79,9 +79,11 @@ module.exports = { // Detect internal link states // -------------------------------- + const pastLinks = await this.page.$relatedQuery('links') + if (internalRefs.length > 0) { // -> Find matching pages - const results = await WIKI.models.pages.query().column('path', 'localeCode').where(builder => { + const results = await WIKI.models.pages.query().column('id', 'path', 'localeCode').where(builder => { internalRefs.forEach((ref, idx) => { if (idx < 1) { builder.where(ref) @@ -106,6 +108,38 @@ module.exports = { $(elm).addClass(`is-invalid-page`) } }) + + // -> Add missing links + const missingLinks = _.differenceWith(internalRefs, pastLinks, (nLink, pLink) => { + return nLink.localeCode === pLink.localeCode && nLink.path === pLink.path + }) + if (missingLinks.length > 0) { + if (WIKI.config.db.type === 'postgres') { + await WIKI.models.pageLinks.query().insert(missingLinks.map(lnk => ({ + pageId: this.page.id, + path: lnk.path, + localeCode: lnk.localeCode + }))) + } else { + for (const lnk of missingLinks) { + await WIKI.models.pageLinks.query().insert({ + pageId: this.page.id, + path: lnk.path, + localeCode: lnk.localeCode + }) + } + } + } + } + + // -> Remove outdated links + if (pastLinks) { + const outdatedLinks = _.differenceWith(pastLinks, internalRefs, (nLink, pLink) => { + return nLink.localeCode === pLink.localeCode && nLink.path === pLink.path + }) + if (outdatedLinks.length > 0) { + await WIKI.models.pageLinks.query().delete().whereIn('id', _.map(outdatedLinks, 'id')) + } } return $.html('body').replace('', '').replace('', '')