page.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. const _ = require('lodash')
  2. const graphHelper = require('../../helpers/graph')
  3. /* global WIKI */
  4. module.exports = {
  5. Query: {
  6. async pages() { return {} }
  7. },
  8. Mutation: {
  9. async pages() { return {} }
  10. },
  11. PageQuery: {
  12. /**
  13. * PAGE HISTORY
  14. */
  15. async history(obj, args, context, info) {
  16. const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.id)
  17. if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
  18. path: page.path,
  19. locale: page.localeCode
  20. })) {
  21. return WIKI.models.pageHistory.getHistory({
  22. pageId: args.id,
  23. offsetPage: args.offsetPage || 0,
  24. offsetSize: args.offsetSize || 100
  25. })
  26. } else {
  27. throw new WIKI.Error.PageHistoryForbidden()
  28. }
  29. },
  30. /**
  31. * PAGE VERSION
  32. */
  33. async version(obj, args, context, info) {
  34. const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.pageId)
  35. if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
  36. path: page.path,
  37. locale: page.localeCode
  38. })) {
  39. return WIKI.models.pageHistory.getVersion({
  40. pageId: args.pageId,
  41. versionId: args.versionId
  42. })
  43. } else {
  44. throw new WIKI.Error.PageHistoryForbidden()
  45. }
  46. },
  47. /**
  48. * SEARCH PAGES
  49. */
  50. async search (obj, args, context) {
  51. if (WIKI.data.searchEngine) {
  52. const resp = await WIKI.data.searchEngine.query(args.query, args)
  53. return {
  54. ...resp,
  55. results: _.filter(resp.results, r => {
  56. return WIKI.auth.checkAccess(context.req.user, ['read:pages'], {
  57. path: r.path,
  58. locale: r.locale
  59. })
  60. })
  61. }
  62. } else {
  63. return {
  64. results: [],
  65. suggestions: [],
  66. totalHits: 0
  67. }
  68. }
  69. },
  70. /**
  71. * LIST PAGES
  72. */
  73. async list (obj, args, context, info) {
  74. let results = await WIKI.models.pages.query().column([
  75. 'pages.id',
  76. 'path',
  77. { locale: 'localeCode' },
  78. 'title',
  79. 'description',
  80. 'isPublished',
  81. 'isPrivate',
  82. 'privateNS',
  83. 'contentType',
  84. 'createdAt',
  85. 'updatedAt'
  86. ])
  87. .withGraphJoined('tags')
  88. .modifyGraph('tags', builder => {
  89. builder.select('tag')
  90. })
  91. .modify(queryBuilder => {
  92. if (args.limit) {
  93. queryBuilder.limit(args.limit)
  94. }
  95. if (args.locale) {
  96. queryBuilder.where('localeCode', args.locale)
  97. }
  98. if (args.creatorId && args.authorId && args.creatorId > 0 && args.authorId > 0) {
  99. queryBuilder.where(function () {
  100. this.where('creatorId', args.creatorId).orWhere('authorId', args.authorId)
  101. })
  102. } else {
  103. if (args.creatorId && args.creatorId > 0) {
  104. queryBuilder.where('creatorId', args.creatorId)
  105. }
  106. if (args.authorId && args.authorId > 0) {
  107. queryBuilder.where('authorId', args.authorId)
  108. }
  109. }
  110. if (args.tags && args.tags.length > 0) {
  111. queryBuilder.whereIn('tags.tag', args.tags.map(t => _.trim(t).toLowerCase()))
  112. }
  113. const orderDir = args.orderByDirection === 'DESC' ? 'desc' : 'asc'
  114. switch (args.orderBy) {
  115. case 'CREATED':
  116. queryBuilder.orderBy('createdAt', orderDir)
  117. break
  118. case 'PATH':
  119. queryBuilder.orderBy('path', orderDir)
  120. break
  121. case 'TITLE':
  122. queryBuilder.orderBy('title', orderDir)
  123. break
  124. case 'UPDATED':
  125. queryBuilder.orderBy('updatedAt', orderDir)
  126. break
  127. default:
  128. queryBuilder.orderBy('pages.id', orderDir)
  129. break
  130. }
  131. })
  132. results = _.filter(results, r => {
  133. return WIKI.auth.checkAccess(context.req.user, ['read:pages'], {
  134. path: r.path,
  135. locale: r.locale
  136. })
  137. }).map(r => ({
  138. ...r,
  139. tags: _.map(r.tags, 'tag')
  140. }))
  141. if (args.tags && args.tags.length > 0) {
  142. results = _.filter(results, r => _.every(args.tags, t => _.includes(r.tags, t)))
  143. }
  144. return results
  145. },
  146. /**
  147. * FETCH SINGLE PAGE
  148. */
  149. async single (obj, args, context, info) {
  150. let page = await WIKI.models.pages.getPageFromDb(args.id)
  151. if (page) {
  152. if (WIKI.auth.checkAccess(context.req.user, ['manage:pages', 'delete:pages'], {
  153. path: page.path,
  154. locale: page.localeCode
  155. })) {
  156. return {
  157. ...page,
  158. locale: page.localeCode,
  159. editor: page.editorKey
  160. }
  161. } else {
  162. throw new WIKI.Error.PageViewForbidden()
  163. }
  164. } else {
  165. throw new WIKI.Error.PageNotFound()
  166. }
  167. },
  168. /**
  169. * FETCH TAGS
  170. */
  171. async tags (obj, args, context, info) {
  172. return WIKI.models.tags.query().orderBy('tag', 'asc')
  173. },
  174. /**
  175. * SEARCH TAGS
  176. */
  177. async searchTags (obj, args, context, info) {
  178. const query = _.trim(args.query)
  179. const results = await WIKI.models.tags.query()
  180. .column('tag')
  181. .where(builder => {
  182. builder.andWhere(builderSub => {
  183. if (WIKI.config.db.type === 'postgres') {
  184. builderSub.where('tag', 'ILIKE', `%${query}%`)
  185. } else {
  186. builderSub.where('tag', 'LIKE', `%${query}%`)
  187. }
  188. })
  189. })
  190. .limit(5)
  191. return results.map(r => r.tag)
  192. },
  193. /**
  194. * FETCH PAGE TREE
  195. */
  196. async tree (obj, args, context, info) {
  197. let results = []
  198. let conds = {
  199. localeCode: args.locale
  200. }
  201. if (args.parent) {
  202. conds.parent = (args.parent < 1) ? null : args.parent
  203. } else if (args.path) {
  204. // conds.parent = (args.parent < 1) ? null : args.parent
  205. }
  206. switch (args.mode) {
  207. case 'FOLDERS':
  208. conds.isFolder = true
  209. results = await WIKI.models.knex('pageTree').where(conds)
  210. break
  211. case 'PAGES':
  212. await WIKI.models.knex('pageTree').where(conds).andWhereNotNull('pageId')
  213. break
  214. default:
  215. results = await WIKI.models.knex('pageTree').where(conds)
  216. break
  217. }
  218. return results.filter(r => {
  219. return WIKI.auth.checkAccess(context.req.user, ['read:pages'], {
  220. path: r.path,
  221. locale: r.localeCode
  222. })
  223. }).map(r => ({
  224. ...r,
  225. parent: r.parent || 0,
  226. locale: r.localeCode
  227. }))
  228. },
  229. /**
  230. * FETCH PAGE LINKS
  231. */
  232. async links (obj, args, context, info) {
  233. let results = []
  234. results = await WIKI.models.knex('pages')
  235. .column({ id: 'pages.id' }, { path: 'pages.path' }, 'title', { link: 'pageLinks.path' }, { locale: 'pageLinks.localeCode' })
  236. .fullOuterJoin('pageLinks', 'pages.id', 'pageLinks.pageId')
  237. .where({
  238. 'pages.localeCode': args.locale
  239. })
  240. return _.reduce(results, (result, val) => {
  241. // -> Check if user has access to source and linked page
  242. if (
  243. !WIKI.auth.checkAccess(context.req.user, ['read:pages'], { path: val.path, locale: args.locale }) ||
  244. !WIKI.auth.checkAccess(context.req.user, ['read:pages'], { path: val.link, locale: val.locale })
  245. ) {
  246. return result
  247. }
  248. const existingEntry = _.findIndex(result, ['id', val.id])
  249. if (existingEntry >= 0) {
  250. if (val.link) {
  251. result[existingEntry].links.push(`${val.locale}/${val.link}`)
  252. }
  253. } else {
  254. result.push({
  255. id: val.id,
  256. title: val.title,
  257. path: `${args.locale}/${val.path}`,
  258. links: val.link ? [`${val.locale}/${val.link}`] : []
  259. })
  260. }
  261. return result
  262. }, [])
  263. },
  264. /**
  265. * CHECK FOR EDITING CONFLICT
  266. */
  267. async checkConflicts (obj, args, context, info) {
  268. let page = await WIKI.models.pages.query().select('path', 'localeCode', 'updatedAt').findById(args.id)
  269. if (page) {
  270. if (WIKI.auth.checkAccess(context.req.user, ['write:pages', 'manage:pages'], {
  271. path: page.path,
  272. locale: page.localeCode
  273. })) {
  274. return page.updatedAt > args.checkoutDate
  275. } else {
  276. throw new WIKI.Error.PageUpdateForbidden()
  277. }
  278. } else {
  279. throw new WIKI.Error.PageNotFound()
  280. }
  281. },
  282. /**
  283. * FETCH LATEST VERSION FOR CONFLICT COMPARISON
  284. */
  285. async conflictLatest (obj, args, context, info) {
  286. let page = await WIKI.models.pages.getPageFromDb(args.id)
  287. if (page) {
  288. if (WIKI.auth.checkAccess(context.req.user, ['write:pages', 'manage:pages'], {
  289. path: page.path,
  290. locale: page.localeCode
  291. })) {
  292. return {
  293. ...page,
  294. tags: page.tags.map(t => t.tag),
  295. locale: page.localeCode
  296. }
  297. } else {
  298. throw new WIKI.Error.PageViewForbidden()
  299. }
  300. } else {
  301. throw new WIKI.Error.PageNotFound()
  302. }
  303. }
  304. },
  305. PageMutation: {
  306. /**
  307. * CREATE PAGE
  308. */
  309. async create(obj, args, context) {
  310. try {
  311. const page = await WIKI.models.pages.createPage({
  312. ...args,
  313. user: context.req.user
  314. })
  315. return {
  316. responseResult: graphHelper.generateSuccess('Page created successfully.'),
  317. page
  318. }
  319. } catch (err) {
  320. return graphHelper.generateError(err)
  321. }
  322. },
  323. /**
  324. * UPDATE PAGE
  325. */
  326. async update(obj, args, context) {
  327. try {
  328. const page = await WIKI.models.pages.updatePage({
  329. ...args,
  330. user: context.req.user
  331. })
  332. return {
  333. responseResult: graphHelper.generateSuccess('Page has been updated.'),
  334. page
  335. }
  336. } catch (err) {
  337. return graphHelper.generateError(err)
  338. }
  339. },
  340. /**
  341. * MOVE PAGE
  342. */
  343. async move(obj, args, context) {
  344. try {
  345. await WIKI.models.pages.movePage({
  346. ...args,
  347. user: context.req.user
  348. })
  349. return {
  350. responseResult: graphHelper.generateSuccess('Page has been moved.')
  351. }
  352. } catch (err) {
  353. return graphHelper.generateError(err)
  354. }
  355. },
  356. /**
  357. * DELETE PAGE
  358. */
  359. async delete(obj, args, context) {
  360. try {
  361. await WIKI.models.pages.deletePage({
  362. ...args,
  363. user: context.req.user
  364. })
  365. return {
  366. responseResult: graphHelper.generateSuccess('Page has been deleted.')
  367. }
  368. } catch (err) {
  369. return graphHelper.generateError(err)
  370. }
  371. },
  372. /**
  373. * DELETE TAG
  374. */
  375. async deleteTag (obj, args, context) {
  376. try {
  377. const tagToDel = await WIKI.models.tags.query().findById(args.id)
  378. if (tagToDel) {
  379. await tagToDel.$relatedQuery('pages').unrelate()
  380. await WIKI.models.tags.query().deleteById(args.id)
  381. } else {
  382. throw new Error('This tag does not exist.')
  383. }
  384. return {
  385. responseResult: graphHelper.generateSuccess('Tag has been deleted.')
  386. }
  387. } catch (err) {
  388. return graphHelper.generateError(err)
  389. }
  390. },
  391. /**
  392. * UPDATE TAG
  393. */
  394. async updateTag (obj, args, context) {
  395. try {
  396. const affectedRows = await WIKI.models.tags.query()
  397. .findById(args.id)
  398. .patch({
  399. tag: _.trim(args.tag).toLowerCase(),
  400. title: _.trim(args.title)
  401. })
  402. if (affectedRows < 1) {
  403. throw new Error('This tag does not exist.')
  404. }
  405. return {
  406. responseResult: graphHelper.generateSuccess('Tag has been updated successfully.')
  407. }
  408. } catch (err) {
  409. return graphHelper.generateError(err)
  410. }
  411. },
  412. /**
  413. * FLUSH PAGE CACHE
  414. */
  415. async flushCache(obj, args, context) {
  416. try {
  417. await WIKI.models.pages.flushCache()
  418. return {
  419. responseResult: graphHelper.generateSuccess('Pages Cache has been flushed successfully.')
  420. }
  421. } catch (err) {
  422. return graphHelper.generateError(err)
  423. }
  424. },
  425. /**
  426. * MIGRATE ALL PAGES FROM SOURCE LOCALE TO TARGET LOCALE
  427. */
  428. async migrateToLocale(obj, args, context) {
  429. try {
  430. const count = await WIKI.models.pages.migrateToLocale(args)
  431. return {
  432. responseResult: graphHelper.generateSuccess('Migrated content to target locale successfully.'),
  433. count
  434. }
  435. } catch (err) {
  436. return graphHelper.generateError(err)
  437. }
  438. },
  439. /**
  440. * REBUILD TREE
  441. */
  442. async rebuildTree(obj, args, context) {
  443. try {
  444. await WIKI.models.pages.rebuildTree()
  445. return {
  446. responseResult: graphHelper.generateSuccess('Page tree rebuilt successfully.')
  447. }
  448. } catch (err) {
  449. return graphHelper.generateError(err)
  450. }
  451. },
  452. /**
  453. * RENDER PAGE
  454. */
  455. async render (obj, args, context) {
  456. try {
  457. const page = await WIKI.models.pages.query().findById(args.id)
  458. if (!page) {
  459. throw new WIKI.Error.PageNotFound()
  460. }
  461. await WIKI.models.pages.renderPage(page)
  462. return {
  463. responseResult: graphHelper.generateSuccess('Page rendered successfully.')
  464. }
  465. } catch (err) {
  466. return graphHelper.generateError(err)
  467. }
  468. },
  469. /**
  470. * RESTORE PAGE VERSION
  471. */
  472. async restore (obj, args, context) {
  473. try {
  474. const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.pageId)
  475. if (!page) {
  476. throw new WIKI.Error.PageNotFound()
  477. }
  478. if (!WIKI.auth.checkAccess(context.req.user, ['write:pages'], {
  479. path: page.path,
  480. locale: page.localeCode
  481. })) {
  482. throw new WIKI.Error.PageRestoreForbidden()
  483. }
  484. const targetVersion = await WIKI.models.pageHistory.getVersion({ pageId: args.pageId, versionId: args.versionId })
  485. if (!targetVersion) {
  486. throw new WIKI.Error.PageNotFound()
  487. }
  488. await WIKI.models.pages.updatePage({
  489. ...targetVersion,
  490. id: targetVersion.pageId,
  491. user: context.req.user,
  492. action: 'restored'
  493. })
  494. return {
  495. responseResult: graphHelper.generateSuccess('Page version restored successfully.')
  496. }
  497. } catch (err) {
  498. return graphHelper.generateError(err)
  499. }
  500. }
  501. },
  502. Page: {
  503. // comments(pg) {
  504. // return pg.$relatedQuery('comments')
  505. // }
  506. }
  507. }