page.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  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.tags && args.tags.length > 0) {
  99. queryBuilder.whereIn('tags.tag', args.tags)
  100. }
  101. const orderDir = args.orderByDirection === 'DESC' ? 'desc' : 'asc'
  102. switch (args.orderBy) {
  103. case 'CREATED':
  104. queryBuilder.orderBy('createdAt', orderDir)
  105. break
  106. case 'PATH':
  107. queryBuilder.orderBy('path', orderDir)
  108. break
  109. case 'TITLE':
  110. queryBuilder.orderBy('title', orderDir)
  111. break
  112. case 'UPDATED':
  113. queryBuilder.orderBy('updatedAt', orderDir)
  114. break
  115. default:
  116. queryBuilder.orderBy('pages.id', orderDir)
  117. break
  118. }
  119. })
  120. results = _.filter(results, r => {
  121. return WIKI.auth.checkAccess(context.req.user, ['read:pages'], {
  122. path: r.path,
  123. locale: r.locale
  124. })
  125. }).map(r => ({
  126. ...r,
  127. tags: _.map(r.tags, 'tag')
  128. }))
  129. if (args.tags && args.tags.length > 0) {
  130. results = _.filter(results, r => _.every(args.tags, t => _.includes(r.tags, t)))
  131. }
  132. return results
  133. },
  134. /**
  135. * FETCH SINGLE PAGE
  136. */
  137. async single (obj, args, context, info) {
  138. let page = await WIKI.models.pages.getPageFromDb(args.id)
  139. if (page) {
  140. if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
  141. path: page.path,
  142. locale: page.localeCode
  143. })) {
  144. return {
  145. ...page,
  146. locale: page.localeCode,
  147. editor: page.editorKey
  148. }
  149. } else {
  150. throw new WIKI.Error.PageViewForbidden()
  151. }
  152. } else {
  153. throw new WIKI.Error.PageNotFound()
  154. }
  155. },
  156. /**
  157. * FETCH TAGS
  158. */
  159. async tags (obj, args, context, info) {
  160. return WIKI.models.tags.query().orderBy('tag', 'asc')
  161. },
  162. /**
  163. * SEARCH TAGS
  164. */
  165. async searchTags (obj, args, context, info) {
  166. const results = await WIKI.models.tags.query()
  167. .column('tag')
  168. .where(builder => {
  169. builder.andWhere(builderSub => {
  170. if (WIKI.config.db.type === 'postgres') {
  171. builderSub.where('tag', 'ILIKE', `%${args.query}%`)
  172. } else {
  173. builderSub.where('tag', 'LIKE', `%${args.query}%`)
  174. }
  175. })
  176. })
  177. .limit(5)
  178. return results.map(r => r.tag)
  179. },
  180. /**
  181. * FETCH PAGE TREE
  182. */
  183. async tree (obj, args, context, info) {
  184. let results = []
  185. let conds = {
  186. localeCode: args.locale
  187. }
  188. if (args.parent) {
  189. conds.parent = (args.parent < 1) ? null : args.parent
  190. } else if (args.path) {
  191. // conds.parent = (args.parent < 1) ? null : args.parent
  192. }
  193. switch (args.mode) {
  194. case 'FOLDERS':
  195. conds.isFolder = true
  196. results = await WIKI.models.knex('pageTree').where(conds)
  197. break
  198. case 'PAGES':
  199. await WIKI.models.knex('pageTree').where(conds).andWhereNotNull('pageId')
  200. break
  201. default:
  202. results = await WIKI.models.knex('pageTree').where(conds)
  203. break
  204. }
  205. return results.filter(r => {
  206. return WIKI.auth.checkAccess(context.req.user, ['read:pages'], {
  207. path: r.path,
  208. locale: r.localeCode
  209. })
  210. }).map(r => ({
  211. ...r,
  212. parent: r.parent || 0,
  213. locale: r.localeCode
  214. }))
  215. },
  216. /**
  217. * FETCH PAGE LINKS
  218. */
  219. async links (obj, args, context, info) {
  220. let results = []
  221. results = await WIKI.models.knex('pages')
  222. .column({ id: 'pages.id' }, { path: 'pages.path' }, 'title', { link: 'pageLinks.path' }, { locale: 'pageLinks.localeCode' })
  223. .fullOuterJoin('pageLinks', 'pages.id', 'pageLinks.pageId')
  224. .where({
  225. 'pages.localeCode': args.locale
  226. })
  227. return _.reduce(results, (result, val) => {
  228. // -> Check if user has access to source and linked page
  229. if (
  230. !WIKI.auth.checkAccess(context.req.user, ['read:pages'], { path: val.path, locale: args.locale }) ||
  231. !WIKI.auth.checkAccess(context.req.user, ['read:pages'], { path: val.link, locale: val.locale })
  232. ) {
  233. return result
  234. }
  235. const existingEntry = _.findIndex(result, ['id', val.id])
  236. if (existingEntry >= 0) {
  237. if (val.link) {
  238. result[existingEntry].links.push(`${val.locale}/${val.link}`)
  239. }
  240. } else {
  241. result.push({
  242. id: val.id,
  243. title: val.title,
  244. path: `${args.locale}/${val.path}`,
  245. links: val.link ? [`${val.locale}/${val.link}`] : []
  246. })
  247. }
  248. return result
  249. }, [])
  250. },
  251. /**
  252. * CHECK FOR EDITING CONFLICT
  253. */
  254. async checkConflicts (obj, args, context, info) {
  255. let page = await WIKI.models.pages.query().select('path', 'localeCode', 'updatedAt').findById(args.id)
  256. if (page) {
  257. if (WIKI.auth.checkAccess(context.req.user, ['write:pages', 'manage:pages'], {
  258. path: page.path,
  259. locale: page.localeCode
  260. })) {
  261. return page.updatedAt !== args.checkoutDate
  262. } else {
  263. throw new WIKI.Error.PageUpdateForbidden()
  264. }
  265. } else {
  266. throw new WIKI.Error.PageNotFound()
  267. }
  268. }
  269. },
  270. PageMutation: {
  271. /**
  272. * CREATE PAGE
  273. */
  274. async create(obj, args, context) {
  275. try {
  276. const page = await WIKI.models.pages.createPage({
  277. ...args,
  278. user: context.req.user
  279. })
  280. return {
  281. responseResult: graphHelper.generateSuccess('Page created successfully.'),
  282. page
  283. }
  284. } catch (err) {
  285. return graphHelper.generateError(err)
  286. }
  287. },
  288. /**
  289. * UPDATE PAGE
  290. */
  291. async update(obj, args, context) {
  292. try {
  293. const page = await WIKI.models.pages.updatePage({
  294. ...args,
  295. user: context.req.user
  296. })
  297. return {
  298. responseResult: graphHelper.generateSuccess('Page has been updated.'),
  299. page
  300. }
  301. } catch (err) {
  302. return graphHelper.generateError(err)
  303. }
  304. },
  305. /**
  306. * MOVE PAGE
  307. */
  308. async move(obj, args, context) {
  309. try {
  310. await WIKI.models.pages.movePage({
  311. ...args,
  312. user: context.req.user
  313. })
  314. return {
  315. responseResult: graphHelper.generateSuccess('Page has been moved.')
  316. }
  317. } catch (err) {
  318. return graphHelper.generateError(err)
  319. }
  320. },
  321. /**
  322. * DELETE PAGE
  323. */
  324. async delete(obj, args, context) {
  325. try {
  326. await WIKI.models.pages.deletePage({
  327. ...args,
  328. user: context.req.user
  329. })
  330. return {
  331. responseResult: graphHelper.generateSuccess('Page has been deleted.')
  332. }
  333. } catch (err) {
  334. return graphHelper.generateError(err)
  335. }
  336. },
  337. /**
  338. * DELETE TAG
  339. */
  340. async deleteTag (obj, args, context) {
  341. try {
  342. const tagToDel = await WIKI.models.tags.query().findById(args.id)
  343. if (tagToDel) {
  344. await tagToDel.$relatedQuery('pages').unrelate()
  345. await WIKI.models.tags.query().deleteById(args.id)
  346. } else {
  347. throw new Error('This tag does not exist.')
  348. }
  349. return {
  350. responseResult: graphHelper.generateSuccess('Tag has been deleted.')
  351. }
  352. } catch (err) {
  353. return graphHelper.generateError(err)
  354. }
  355. },
  356. /**
  357. * UPDATE TAG
  358. */
  359. async updateTag (obj, args, context) {
  360. try {
  361. const affectedRows = await WIKI.models.tags.query()
  362. .findById(args.id)
  363. .patch({
  364. tag: args.tag,
  365. title: args.title
  366. })
  367. if (affectedRows < 1) {
  368. throw new Error('This tag does not exist.')
  369. }
  370. return {
  371. responseResult: graphHelper.generateSuccess('Tag has been updated successfully.')
  372. }
  373. } catch (err) {
  374. return graphHelper.generateError(err)
  375. }
  376. },
  377. /**
  378. * FLUSH PAGE CACHE
  379. */
  380. async flushCache(obj, args, context) {
  381. try {
  382. await WIKI.models.pages.flushCache()
  383. return {
  384. responseResult: graphHelper.generateSuccess('Pages Cache has been flushed successfully.')
  385. }
  386. } catch (err) {
  387. return graphHelper.generateError(err)
  388. }
  389. },
  390. /**
  391. * MIGRATE ALL PAGES FROM SOURCE LOCALE TO TARGET LOCALE
  392. */
  393. async migrateToLocale(obj, args, context) {
  394. try {
  395. const count = await WIKI.models.pages.migrateToLocale(args)
  396. return {
  397. responseResult: graphHelper.generateSuccess('Migrated content to target locale successfully.'),
  398. count
  399. }
  400. } catch (err) {
  401. return graphHelper.generateError(err)
  402. }
  403. },
  404. /**
  405. * REBUILD TREE
  406. */
  407. async rebuildTree(obj, args, context) {
  408. try {
  409. await WIKI.models.pages.rebuildTree()
  410. return {
  411. responseResult: graphHelper.generateSuccess('Page tree rebuilt successfully.')
  412. }
  413. } catch (err) {
  414. return graphHelper.generateError(err)
  415. }
  416. },
  417. /**
  418. * RENDER PAGE
  419. */
  420. async render (obj, args, context) {
  421. try {
  422. const page = await WIKI.models.pages.query().findById(args.id)
  423. if (!page) {
  424. throw new WIKI.Error.PageNotFound()
  425. }
  426. await WIKI.models.pages.renderPage(page)
  427. return {
  428. responseResult: graphHelper.generateSuccess('Page rendered successfully.')
  429. }
  430. } catch (err) {
  431. return graphHelper.generateError(err)
  432. }
  433. },
  434. /**
  435. * RESTORE PAGE VERSION
  436. */
  437. async restore (obj, args, context) {
  438. try {
  439. const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.pageId)
  440. if (!page) {
  441. throw new WIKI.Error.PageNotFound()
  442. }
  443. if (!WIKI.auth.checkAccess(context.req.user, ['write:pages'], {
  444. path: page.path,
  445. locale: page.localeCode
  446. })) {
  447. throw new WIKI.Error.PageRestoreForbidden()
  448. }
  449. const targetVersion = await WIKI.models.pageHistory.getVersion({ pageId: args.pageId, versionId: args.versionId })
  450. if (!targetVersion) {
  451. throw new WIKI.Error.PageNotFound()
  452. }
  453. await WIKI.models.pages.updatePage({
  454. ...targetVersion,
  455. id: targetVersion.pageId,
  456. user: context.req.user,
  457. action: 'restored'
  458. })
  459. return {
  460. responseResult: graphHelper.generateSuccess('Page version restored successfully.')
  461. }
  462. } catch (err) {
  463. return graphHelper.generateError(err)
  464. }
  465. }
  466. },
  467. Page: {
  468. // comments(pg) {
  469. // return pg.$relatedQuery('comments')
  470. // }
  471. }
  472. }