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.

531 lines
15 KiB

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