page.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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 curPage = null
  198. if (!args.locale) { args.locale = WIKI.config.lang.code }
  199. if (args.path && !args.parent) {
  200. curPage = await WIKI.models.knex('pageTree').first('parent', 'ancestors').where({
  201. path: args.path,
  202. localeCode: args.locale
  203. })
  204. if (curPage) {
  205. args.parent = curPage.parent || 0
  206. } else {
  207. return []
  208. }
  209. }
  210. const results = await WIKI.models.knex('pageTree').where(builder => {
  211. builder.where('localeCode', args.locale)
  212. switch (args.mode) {
  213. case 'FOLDERS':
  214. builder.andWhere('isFolder', true)
  215. break
  216. case 'PAGES':
  217. builder.andWhereNotNull('pageId')
  218. break
  219. }
  220. if (!args.parent || args.parent < 1) {
  221. builder.whereNull('parent')
  222. } else {
  223. builder.where('parent', args.parent)
  224. if (args.includeAncestors && curPage && curPage.ancestors.length > 0) {
  225. builder.orWhereIn('id', _.isString(curPage.ancestors) ? JSON.parse(curPage.ancestors) : curPage.ancestors)
  226. }
  227. }
  228. }).orderBy([{ column: 'isFolder', order: 'desc' }, 'title'])
  229. return results.filter(r => {
  230. return WIKI.auth.checkAccess(context.req.user, ['read:pages'], {
  231. path: r.path,
  232. locale: r.localeCode
  233. })
  234. }).map(r => ({
  235. ...r,
  236. parent: r.parent || 0,
  237. locale: r.localeCode
  238. }))
  239. },
  240. /**
  241. * FETCH PAGE LINKS
  242. */
  243. async links (obj, args, context, info) {
  244. let results;
  245. if (WIKI.config.db.type === 'mysql' || WIKI.config.db.type === 'mariadb' || WIKI.config.db.type === 'sqlite') {
  246. results = await WIKI.models.knex('pages')
  247. .column({ id: 'pages.id' }, { path: 'pages.path' }, 'title', { link: 'pageLinks.path' }, { locale: 'pageLinks.localeCode' })
  248. .leftJoin('pageLinks', 'pages.id', 'pageLinks.pageId')
  249. .where({
  250. 'pages.localeCode': args.locale
  251. })
  252. .unionAll(
  253. WIKI.models.knex('pageLinks')
  254. .column({ id: 'pages.id' }, { path: 'pages.path' }, 'title', { link: 'pageLinks.path' }, { locale: 'pageLinks.localeCode' })
  255. .leftJoin('pages', 'pageLinks.pageId', 'pages.id')
  256. .where({
  257. 'pages.localeCode': args.locale
  258. })
  259. )
  260. } else {
  261. results = await WIKI.models.knex('pages')
  262. .column({ id: 'pages.id' }, { path: 'pages.path' }, 'title', { link: 'pageLinks.path' }, { locale: 'pageLinks.localeCode' })
  263. .fullOuterJoin('pageLinks', 'pages.id', 'pageLinks.pageId')
  264. .where({
  265. 'pages.localeCode': args.locale
  266. })
  267. }
  268. return _.reduce(results, (result, val) => {
  269. // -> Check if user has access to source and linked page
  270. if (
  271. !WIKI.auth.checkAccess(context.req.user, ['read:pages'], { path: val.path, locale: args.locale }) ||
  272. !WIKI.auth.checkAccess(context.req.user, ['read:pages'], { path: val.link, locale: val.locale })
  273. ) {
  274. return result
  275. }
  276. const existingEntry = _.findIndex(result, ['id', val.id])
  277. if (existingEntry >= 0) {
  278. if (val.link) {
  279. result[existingEntry].links.push(`${val.locale}/${val.link}`)
  280. }
  281. } else {
  282. result.push({
  283. id: val.id,
  284. title: val.title,
  285. path: `${args.locale}/${val.path}`,
  286. links: val.link ? [`${val.locale}/${val.link}`] : []
  287. })
  288. }
  289. return result
  290. }, [])
  291. },
  292. /**
  293. * CHECK FOR EDITING CONFLICT
  294. */
  295. async checkConflicts (obj, args, context, info) {
  296. let page = await WIKI.models.pages.query().select('path', 'localeCode', 'updatedAt').findById(args.id)
  297. if (page) {
  298. if (WIKI.auth.checkAccess(context.req.user, ['write:pages', 'manage:pages'], {
  299. path: page.path,
  300. locale: page.localeCode
  301. })) {
  302. return page.updatedAt > args.checkoutDate
  303. } else {
  304. throw new WIKI.Error.PageUpdateForbidden()
  305. }
  306. } else {
  307. throw new WIKI.Error.PageNotFound()
  308. }
  309. },
  310. /**
  311. * FETCH LATEST VERSION FOR CONFLICT COMPARISON
  312. */
  313. async conflictLatest (obj, args, context, info) {
  314. let page = await WIKI.models.pages.getPageFromDb(args.id)
  315. if (page) {
  316. if (WIKI.auth.checkAccess(context.req.user, ['write:pages', 'manage:pages'], {
  317. path: page.path,
  318. locale: page.localeCode
  319. })) {
  320. return {
  321. ...page,
  322. tags: page.tags.map(t => t.tag),
  323. locale: page.localeCode
  324. }
  325. } else {
  326. throw new WIKI.Error.PageViewForbidden()
  327. }
  328. } else {
  329. throw new WIKI.Error.PageNotFound()
  330. }
  331. }
  332. },
  333. PageMutation: {
  334. /**
  335. * CREATE PAGE
  336. */
  337. async create(obj, args, context) {
  338. try {
  339. const page = await WIKI.models.pages.createPage({
  340. ...args,
  341. user: context.req.user
  342. })
  343. return {
  344. responseResult: graphHelper.generateSuccess('Page created successfully.'),
  345. page
  346. }
  347. } catch (err) {
  348. return graphHelper.generateError(err)
  349. }
  350. },
  351. /**
  352. * UPDATE PAGE
  353. */
  354. async update(obj, args, context) {
  355. try {
  356. const page = await WIKI.models.pages.updatePage({
  357. ...args,
  358. user: context.req.user
  359. })
  360. return {
  361. responseResult: graphHelper.generateSuccess('Page has been updated.'),
  362. page
  363. }
  364. } catch (err) {
  365. return graphHelper.generateError(err)
  366. }
  367. },
  368. /**
  369. * MOVE PAGE
  370. */
  371. async move(obj, args, context) {
  372. try {
  373. await WIKI.models.pages.movePage({
  374. ...args,
  375. user: context.req.user
  376. })
  377. return {
  378. responseResult: graphHelper.generateSuccess('Page has been moved.')
  379. }
  380. } catch (err) {
  381. return graphHelper.generateError(err)
  382. }
  383. },
  384. /**
  385. * DELETE PAGE
  386. */
  387. async delete(obj, args, context) {
  388. try {
  389. await WIKI.models.pages.deletePage({
  390. ...args,
  391. user: context.req.user
  392. })
  393. return {
  394. responseResult: graphHelper.generateSuccess('Page has been deleted.')
  395. }
  396. } catch (err) {
  397. return graphHelper.generateError(err)
  398. }
  399. },
  400. /**
  401. * DELETE TAG
  402. */
  403. async deleteTag (obj, args, context) {
  404. try {
  405. const tagToDel = await WIKI.models.tags.query().findById(args.id)
  406. if (tagToDel) {
  407. await tagToDel.$relatedQuery('pages').unrelate()
  408. await WIKI.models.tags.query().deleteById(args.id)
  409. } else {
  410. throw new Error('This tag does not exist.')
  411. }
  412. return {
  413. responseResult: graphHelper.generateSuccess('Tag has been deleted.')
  414. }
  415. } catch (err) {
  416. return graphHelper.generateError(err)
  417. }
  418. },
  419. /**
  420. * UPDATE TAG
  421. */
  422. async updateTag (obj, args, context) {
  423. try {
  424. const affectedRows = await WIKI.models.tags.query()
  425. .findById(args.id)
  426. .patch({
  427. tag: _.trim(args.tag).toLowerCase(),
  428. title: _.trim(args.title)
  429. })
  430. if (affectedRows < 1) {
  431. throw new Error('This tag does not exist.')
  432. }
  433. return {
  434. responseResult: graphHelper.generateSuccess('Tag has been updated successfully.')
  435. }
  436. } catch (err) {
  437. return graphHelper.generateError(err)
  438. }
  439. },
  440. /**
  441. * FLUSH PAGE CACHE
  442. */
  443. async flushCache(obj, args, context) {
  444. try {
  445. await WIKI.models.pages.flushCache()
  446. WIKI.events.outbound.emit('flushCache')
  447. return {
  448. responseResult: graphHelper.generateSuccess('Pages Cache has been flushed successfully.')
  449. }
  450. } catch (err) {
  451. return graphHelper.generateError(err)
  452. }
  453. },
  454. /**
  455. * MIGRATE ALL PAGES FROM SOURCE LOCALE TO TARGET LOCALE
  456. */
  457. async migrateToLocale(obj, args, context) {
  458. try {
  459. const count = await WIKI.models.pages.migrateToLocale(args)
  460. return {
  461. responseResult: graphHelper.generateSuccess('Migrated content to target locale successfully.'),
  462. count
  463. }
  464. } catch (err) {
  465. return graphHelper.generateError(err)
  466. }
  467. },
  468. /**
  469. * REBUILD TREE
  470. */
  471. async rebuildTree(obj, args, context) {
  472. try {
  473. await WIKI.models.pages.rebuildTree()
  474. return {
  475. responseResult: graphHelper.generateSuccess('Page tree rebuilt successfully.')
  476. }
  477. } catch (err) {
  478. return graphHelper.generateError(err)
  479. }
  480. },
  481. /**
  482. * RENDER PAGE
  483. */
  484. async render (obj, args, context) {
  485. try {
  486. const page = await WIKI.models.pages.query().findById(args.id)
  487. if (!page) {
  488. throw new WIKI.Error.PageNotFound()
  489. }
  490. await WIKI.models.pages.renderPage(page)
  491. return {
  492. responseResult: graphHelper.generateSuccess('Page rendered successfully.')
  493. }
  494. } catch (err) {
  495. return graphHelper.generateError(err)
  496. }
  497. },
  498. /**
  499. * RESTORE PAGE VERSION
  500. */
  501. async restore (obj, args, context) {
  502. try {
  503. const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.pageId)
  504. if (!page) {
  505. throw new WIKI.Error.PageNotFound()
  506. }
  507. if (!WIKI.auth.checkAccess(context.req.user, ['write:pages'], {
  508. path: page.path,
  509. locale: page.localeCode
  510. })) {
  511. throw new WIKI.Error.PageRestoreForbidden()
  512. }
  513. const targetVersion = await WIKI.models.pageHistory.getVersion({ pageId: args.pageId, versionId: args.versionId })
  514. if (!targetVersion) {
  515. throw new WIKI.Error.PageNotFound()
  516. }
  517. await WIKI.models.pages.updatePage({
  518. ...targetVersion,
  519. id: targetVersion.pageId,
  520. user: context.req.user,
  521. action: 'restored'
  522. })
  523. return {
  524. responseResult: graphHelper.generateSuccess('Page version restored successfully.')
  525. }
  526. } catch (err) {
  527. return graphHelper.generateError(err)
  528. }
  529. }
  530. },
  531. Page: {
  532. // comments(pg) {
  533. // return pg.$relatedQuery('comments')
  534. // }
  535. }
  536. }