pageHistory.mjs 5.9 KB

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