You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
6.1 KiB

  1. const Model = require('objection').Model
  2. const _ = require('lodash')
  3. const { DateTime, Duration } = require('luxon')
  4. /* global WIKI */
  5. /**
  6. * Page History model
  7. */
  8. module.exports = class PageHistory extends Model {
  9. static get tableName() { return 'pageHistory' }
  10. static get jsonSchema () {
  11. return {
  12. type: 'object',
  13. required: ['path', 'title'],
  14. properties: {
  15. id: {type: 'integer'},
  16. path: {type: 'string'},
  17. hash: {type: 'string'},
  18. title: {type: 'string'},
  19. description: {type: 'string'},
  20. isPublished: {type: 'boolean'},
  21. publishStartDate: {type: 'string'},
  22. publishEndDate: {type: 'string'},
  23. content: {type: 'string'},
  24. contentType: {type: 'string'},
  25. createdAt: {type: 'string'}
  26. }
  27. }
  28. }
  29. static get relationMappings() {
  30. return {
  31. tags: {
  32. relation: Model.ManyToManyRelation,
  33. modelClass: require('./tags'),
  34. join: {
  35. from: 'pageHistory.id',
  36. through: {
  37. from: 'pageHistoryTags.pageId',
  38. to: 'pageHistoryTags.tagId'
  39. },
  40. to: 'tags.id'
  41. }
  42. },
  43. page: {
  44. relation: Model.BelongsToOneRelation,
  45. modelClass: require('./pages'),
  46. join: {
  47. from: 'pageHistory.pageId',
  48. to: 'pages.id'
  49. }
  50. },
  51. author: {
  52. relation: Model.BelongsToOneRelation,
  53. modelClass: require('./users'),
  54. join: {
  55. from: 'pageHistory.authorId',
  56. to: 'users.id'
  57. }
  58. },
  59. editor: {
  60. relation: Model.BelongsToOneRelation,
  61. modelClass: require('./editors'),
  62. join: {
  63. from: 'pageHistory.editorKey',
  64. to: 'editors.key'
  65. }
  66. },
  67. locale: {
  68. relation: Model.BelongsToOneRelation,
  69. modelClass: require('./locales'),
  70. join: {
  71. from: 'pageHistory.localeCode',
  72. to: 'locales.code'
  73. }
  74. }
  75. }
  76. }
  77. $beforeInsert() {
  78. this.createdAt = new Date().toISOString()
  79. }
  80. /**
  81. * Create Page Version
  82. */
  83. static async addVersion(opts) {
  84. await WIKI.models.pageHistory.query().insert({
  85. pageId: opts.id,
  86. authorId: opts.authorId,
  87. content: opts.content,
  88. contentType: opts.contentType,
  89. description: opts.description,
  90. editorKey: opts.editorKey,
  91. hash: opts.hash,
  92. isPrivate: (opts.isPrivate === true || opts.isPrivate === 1),
  93. isPublished: (opts.isPublished === true || opts.isPublished === 1),
  94. localeCode: opts.localeCode,
  95. path: opts.path,
  96. publishEndDate: opts.publishEndDate || '',
  97. publishStartDate: opts.publishStartDate || '',
  98. title: opts.title,
  99. action: opts.action || 'updated',
  100. versionDate: opts.versionDate
  101. })
  102. }
  103. /**
  104. * Get Page Version
  105. */
  106. static async getVersion({ pageId, versionId }) {
  107. const version = await WIKI.models.pageHistory.query()
  108. .column([
  109. 'pageHistory.path',
  110. 'pageHistory.title',
  111. 'pageHistory.description',
  112. 'pageHistory.isPrivate',
  113. 'pageHistory.isPublished',
  114. 'pageHistory.publishStartDate',
  115. 'pageHistory.publishEndDate',
  116. 'pageHistory.content',
  117. 'pageHistory.contentType',
  118. 'pageHistory.createdAt',
  119. 'pageHistory.action',
  120. 'pageHistory.authorId',
  121. 'pageHistory.pageId',
  122. 'pageHistory.versionDate',
  123. {
  124. versionId: 'pageHistory.id',
  125. editor: 'pageHistory.editorKey',
  126. locale: 'pageHistory.localeCode',
  127. authorName: 'author.name'
  128. }
  129. ])
  130. .joinRelated('author')
  131. .where({
  132. 'pageHistory.id': versionId,
  133. 'pageHistory.pageId': pageId
  134. }).first()
  135. if (version) {
  136. return {
  137. ...version,
  138. updatedAt: version.createdAt || null,
  139. tags: []
  140. }
  141. } else {
  142. return null
  143. }
  144. }
  145. /**
  146. * Get History Trail of a Page
  147. */
  148. static async getHistory({ pageId, offsetPage = 0, offsetSize = 100 }) {
  149. const history = await WIKI.models.pageHistory.query()
  150. .column([
  151. 'pageHistory.id',
  152. 'pageHistory.path',
  153. 'pageHistory.authorId',
  154. 'pageHistory.action',
  155. 'pageHistory.versionDate',
  156. {
  157. authorName: 'author.name'
  158. }
  159. ])
  160. .joinRelated('author')
  161. .where({
  162. 'pageHistory.pageId': pageId
  163. })
  164. .orderBy('pageHistory.versionDate', 'desc')
  165. .page(offsetPage, offsetSize)
  166. let prevPh = null
  167. const upperLimit = (offsetPage + 1) * offsetSize
  168. if (history.total >= upperLimit) {
  169. prevPh = await WIKI.models.pageHistory.query()
  170. .column([
  171. 'pageHistory.id',
  172. 'pageHistory.path',
  173. 'pageHistory.authorId',
  174. 'pageHistory.action',
  175. 'pageHistory.versionDate',
  176. {
  177. authorName: 'author.name'
  178. }
  179. ])
  180. .joinRelated('author')
  181. .where({
  182. 'pageHistory.pageId': pageId
  183. })
  184. .orderBy('pageHistory.versionDate', 'desc')
  185. .offset((offsetPage + 1) * offsetSize)
  186. .limit(1)
  187. .first()
  188. }
  189. return {
  190. trail: _.reduce(_.reverse(history.results), (res, ph) => {
  191. let actionType = 'edit'
  192. let valueBefore = null
  193. let valueAfter = null
  194. if (!prevPh && history.total < upperLimit) {
  195. actionType = 'initial'
  196. } else if (_.get(prevPh, 'path', '') !== ph.path) {
  197. actionType = 'move'
  198. valueBefore = _.get(prevPh, 'path', '')
  199. valueAfter = ph.path
  200. }
  201. res.unshift({
  202. versionId: ph.id,
  203. authorId: ph.authorId,
  204. authorName: ph.authorName,
  205. actionType,
  206. valueBefore,
  207. valueAfter,
  208. versionDate: ph.versionDate
  209. })
  210. prevPh = ph
  211. return res
  212. }, []),
  213. total: history.total
  214. }
  215. }
  216. /**
  217. * Purge history older than X
  218. *
  219. * @param {String} olderThan ISO 8601 Duration
  220. */
  221. static async purge (olderThan) {
  222. const dur = Duration.fromISO(olderThan)
  223. const olderThanISO = DateTime.utc().minus(dur)
  224. await WIKI.models.pageHistory.query().where('versionDate', '<', olderThanISO.toISO()).del()
  225. }
  226. }