erudit 3.0.0-dev.2 → 3.0.0-dev.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/app/app.vue +195 -172
  2. package/app/assets/icons/cameo-add.svg +3 -0
  3. package/app/assets/icons/diamond.svg +3 -0
  4. package/app/assets/icons/graduation.svg +3 -0
  5. package/app/components/Avatar.vue +118 -0
  6. package/app/components/EruditLink.vue +17 -0
  7. package/app/components/Loading.vue +23 -23
  8. package/app/components/SiteAside.vue +393 -382
  9. package/app/components/SiteMain.vue +32 -35
  10. package/app/components/ads/Ads.vue +33 -0
  11. package/app/components/ads/AdsBannerAside.vue +61 -0
  12. package/app/components/ads/AdsBannerBottom.vue +22 -0
  13. package/app/components/ads/AdsProviderCustom.vue +35 -0
  14. package/app/components/ads/AdsProviderYandex.vue +91 -0
  15. package/app/components/ads/AdsReplacer.vue +73 -0
  16. package/app/components/aside/AsideListItem.vue +91 -74
  17. package/app/components/aside/AsideMajor.vue +56 -56
  18. package/app/components/aside/AsideMinor.vue +97 -71
  19. package/app/components/aside/major/PaneContentScroll.vue +23 -23
  20. package/app/components/aside/major/PaneSwitch.vue +54 -54
  21. package/app/components/aside/major/PaneSwitchButton.vue +63 -63
  22. package/app/components/aside/major/SiteInfo.vue +85 -85
  23. package/app/components/aside/major/panes/Language.vue +79 -79
  24. package/app/components/aside/major/panes/Pages.vue +50 -34
  25. package/app/components/aside/major/panes/Search.vue +11 -11
  26. package/app/components/aside/major/panes/nav/Nav.vue +92 -91
  27. package/app/components/aside/major/panes/nav/NavBook.vue +95 -86
  28. package/app/components/aside/major/panes/nav/NavBookLoading.vue +24 -24
  29. package/app/components/aside/major/panes/nav/NavGlobal.vue +16 -16
  30. package/app/components/aside/major/panes/nav/fnav/FNav.vue +105 -105
  31. package/app/components/aside/major/panes/nav/fnav/FNavBook.vue +32 -32
  32. package/app/components/aside/major/panes/nav/fnav/FNavFlags.vue +40 -40
  33. package/app/components/aside/major/panes/nav/fnav/FNavFolder.vue +60 -60
  34. package/app/components/aside/major/panes/nav/fnav/FNavItem.vue +34 -34
  35. package/app/components/aside/major/panes/nav/fnav/FNavSeparator.vue +80 -80
  36. package/app/components/aside/major/panes/nav/fnav/FNavTopic.vue +24 -24
  37. package/app/components/aside/major/panes/other/ItemContent.vue +29 -29
  38. package/app/components/aside/major/panes/other/ItemGenerator.vue +13 -15
  39. package/app/components/aside/major/panes/other/ItemTheme.vue +54 -54
  40. package/app/components/aside/major/panes/other/Other.vue +16 -16
  41. package/app/components/aside/minor/{Contribute.vue → AsideMinorContribute.vue} +175 -145
  42. package/app/components/aside/minor/AsideMinorNews.vue +11 -11
  43. package/app/components/aside/minor/AsideMinorPane.vue +15 -15
  44. package/app/components/aside/minor/AsideMinorTopLink.vue +66 -67
  45. package/app/components/aside/minor/content/AsideMinorContent.vue +89 -92
  46. package/app/components/aside/minor/contributor/AsideMinorContributor.vue +78 -0
  47. package/app/components/aside/minor/contributor/BookContribution.vue +64 -0
  48. package/app/components/aside/minor/topic/AsideMinorTopic.vue +31 -32
  49. package/app/components/aside/minor/topic/TopicContributors.vue +183 -177
  50. package/app/components/aside/minor/topic/TopicNav.vue +49 -49
  51. package/app/components/aside/minor/topic/TopicToc.vue +212 -203
  52. package/app/components/aside/minor/topic/TopicTocItem.vue +19 -31
  53. package/app/components/aside/utils/AsideOverlayPane.vue +40 -40
  54. package/app/components/bitran/BitranContent.vue +90 -63
  55. package/app/components/bitran/RenderWrapper.vue +10 -10
  56. package/app/components/contributor/ContributorListItem.vue +43 -35
  57. package/app/components/main/MainActionButton.vue +51 -0
  58. package/app/components/main/MainBitranContent.vue +55 -0
  59. package/app/components/main/{utils/Breadcrumb.vue → MainBreadcrumb.vue} +63 -75
  60. package/app/components/main/MainDescription.vue +24 -0
  61. package/app/components/main/MainSection.vue +58 -0
  62. package/app/components/main/MainTitle.vue +76 -0
  63. package/app/components/main/cameo/MainCameo.vue +135 -0
  64. package/app/components/main/cameo/MainCameoData.vue +232 -0
  65. package/app/components/main/content/ContentBreadcrumb.vue +28 -0
  66. package/app/components/main/{utils → content}/ContentDecoration.vue +29 -29
  67. package/app/components/main/{utils → content}/ContentPopover.vue +188 -176
  68. package/app/components/main/{utils → content}/ContentPopovers.vue +111 -105
  69. package/app/components/main/{utils → content}/ContentReferences.vue +70 -70
  70. package/app/components/main/{utils → content}/reference/ReferenceGroup.vue +38 -38
  71. package/app/components/main/{utils → content}/reference/ReferenceItem.vue +70 -68
  72. package/app/components/main/{utils → content}/reference/ReferenceSource.vue +120 -116
  73. package/app/components/main/topic/MainTopic.vue +76 -79
  74. package/app/components/main/topic/TopicPartSwitch.vue +119 -118
  75. package/app/components/preview/Preview.vue +186 -177
  76. package/app/components/preview/PreviewDisplay.vue +139 -139
  77. package/app/components/preview/PreviewFooterAction.vue +73 -73
  78. package/app/components/preview/PreviewLoading.vue +14 -14
  79. package/app/components/preview/PreviewScreen.vue +141 -99
  80. package/app/components/preview/display/Alert.vue +50 -50
  81. package/app/components/preview/display/Custom.vue +18 -18
  82. package/app/components/preview/display/GenericLink.vue +48 -48
  83. package/app/components/preview/display/PageLink.vue +22 -20
  84. package/app/components/preview/display/Unique.vue +46 -49
  85. package/app/components/sponsor/SponsorTier1.vue +89 -0
  86. package/app/components/sponsor/SponsorTier2.vue +109 -0
  87. package/app/components/transition/Fade.vue +19 -19
  88. package/app/components/tree/TreeContainer.vue +11 -11
  89. package/app/components/tree/TreeItem.vue +93 -89
  90. package/app/composables/adsAllowed.ts +11 -0
  91. package/app/composables/asset.ts +12 -0
  92. package/app/composables/bitran.ts +108 -127
  93. package/app/composables/bitranLocation.ts +7 -7
  94. package/app/composables/contentData.ts +38 -36
  95. package/app/composables/contentPage.ts +168 -156
  96. package/app/composables/contentRoute.ts +45 -45
  97. package/app/composables/darkMagic.ts +24 -24
  98. package/app/composables/externalApi.ts +69 -63
  99. package/app/composables/favicon.ts +8 -8
  100. package/app/composables/formatText.ts +99 -86
  101. package/app/composables/head.ts +24 -0
  102. package/app/composables/majorPane.ts +62 -60
  103. package/app/composables/phrases.ts +77 -80
  104. package/app/composables/theme.ts +29 -29
  105. package/app/composables/url.ts +43 -33
  106. package/app/pages/_test/preview.vue +110 -110
  107. package/app/pages/article/[...articleId].vue +3 -3
  108. package/app/pages/book/[...bookId].vue +42 -47
  109. package/app/pages/contributor/[contributorId].vue +227 -0
  110. package/app/pages/contributors.vue +184 -0
  111. package/app/pages/group/[...groupId].vue +53 -65
  112. package/app/pages/index.vue +32 -32
  113. package/app/pages/practice/[...practice].vue +3 -3
  114. package/app/pages/sponsors.vue +95 -0
  115. package/app/pages/summary/[...summaryId].vue +3 -3
  116. package/app/plugins/analytics.ts +95 -0
  117. package/app/plugins/prerender.server.ts +34 -0
  118. package/app/public/favicon/article.svg +5 -5
  119. package/app/public/favicon/default.svg +3 -3
  120. package/app/public/favicon/practice.svg +3 -3
  121. package/app/public/favicon/summary.svg +4 -4
  122. package/app/public/logotype.svg +2 -2
  123. package/app/scripts/_immediate.js +9 -9
  124. package/app/scripts/aside/index.ts +59 -59
  125. package/app/scripts/aside/major/nav.ts +26 -26
  126. package/app/scripts/aside/minor/state.ts +37 -37
  127. package/app/scripts/flag.ts +28 -28
  128. package/app/scripts/og.ts +28 -27
  129. package/app/scripts/preview/build.ts +76 -73
  130. package/app/scripts/preview/data/alert.ts +19 -19
  131. package/app/scripts/preview/data/custom.ts +8 -8
  132. package/app/scripts/preview/data/genericLink.ts +24 -24
  133. package/app/scripts/preview/data/pageLink.ts +23 -20
  134. package/app/scripts/preview/data/unique.ts +69 -70
  135. package/app/scripts/preview/data.ts +24 -24
  136. package/app/scripts/preview/display.ts +37 -37
  137. package/app/scripts/preview/footer.ts +9 -9
  138. package/app/scripts/preview/request.ts +51 -51
  139. package/app/scripts/preview/state.ts +63 -63
  140. package/app/styles/_immediate.css +7 -7
  141. package/app/styles/_util.scss +43 -43
  142. package/app/styles/_utils.scss +44 -44
  143. package/app/styles/app.scss +91 -91
  144. package/app/styles/def/_bp.scss +27 -27
  145. package/app/styles/def/_size.scss +7 -7
  146. package/app/styles/def/_z.scss +5 -5
  147. package/app/styles/normalize.scss +49 -63
  148. package/app/styles/partials/_darkMagic.scss +5 -5
  149. package/app/styles/partials/_fnav.scss +15 -15
  150. package/app/styles/partials/_preview.scss +5 -5
  151. package/bin/erudit.mjs +2 -0
  152. package/const.ts +3 -0
  153. package/globalPath.ts +21 -21
  154. package/globals/bitran.ts +1 -47
  155. package/globals/cameo.ts +5 -0
  156. package/globals/content.ts +27 -22
  157. package/globals/contributor.ts +5 -5
  158. package/globals/erudit.ts +5 -5
  159. package/globals/register.ts +23 -18
  160. package/globals/sponsor.ts +17 -0
  161. package/languages/en.ts +108 -95
  162. package/languages/ru.ts +112 -99
  163. package/module/bitran.ts +66 -34
  164. package/module/config.ts +35 -34
  165. package/module/imports.ts +74 -46
  166. package/module/index.ts +47 -47
  167. package/module/logger.ts +10 -10
  168. package/module/paths.ts +22 -22
  169. package/module/restart.ts +61 -61
  170. package/nuxt.config.ts +126 -112
  171. package/package.json +22 -15
  172. package/server/api/aside/major/nav/bookIds.ts +5 -5
  173. package/server/api/aside/major/nav/bookNav/[...bookId].ts +17 -20
  174. package/server/api/aside/major/nav/global.ts +7 -7
  175. package/server/api/aside/minor/book/[...bookId].ts +18 -0
  176. package/server/api/aside/minor/contributor/[contributorId].ts +18 -0
  177. package/server/api/aside/minor/group/[...groupId].ts +18 -0
  178. package/server/api/aside/minor/news.ts +7 -7
  179. package/server/api/aside/minor/topic.ts +36 -0
  180. package/server/api/bitran/content/[...location].ts +13 -0
  181. package/server/api/bitran/toc/[...location].ts +9 -0
  182. package/server/api/cameo/data/[cameoId].ts +42 -0
  183. package/server/api/cameo/ids.ts +5 -0
  184. package/server/api/content/data.ts +75 -72
  185. package/server/api/contributor/count.ts +6 -6
  186. package/server/api/contributor/list.ts +44 -0
  187. package/server/api/contributor/page/[contributorId].ts +14 -0
  188. package/server/api/fake/content.ts +11 -11
  189. package/server/api/fake/shared/languages.ts +12 -12
  190. package/server/api/language/functions.ts +12 -12
  191. package/server/api/language/phrase/[phraseId].ts +19 -19
  192. package/server/api/language/phraseIds.ts +8 -8
  193. package/server/api/prerender/assets/cameo.ts +14 -0
  194. package/server/api/prerender/assets/contributor.ts +12 -0
  195. package/server/api/prerender/assets/sponsor.ts +13 -0
  196. package/server/api/prerender/cameos.ts +12 -0
  197. package/server/api/prerender/language.ts +9 -0
  198. package/server/api/preview/page/[...parts].ts +78 -51
  199. package/server/api/preview/unique/{[location].ts → [...location].ts} +48 -55
  200. package/server/api/problem/generator/[...path].ts +26 -0
  201. package/server/api/sponsor/cameo/data/[sponsorId].ts +51 -0
  202. package/server/api/sponsor/cameo/ids.ts +5 -0
  203. package/server/api/sponsor/count.ts +5 -0
  204. package/server/api/sponsor/list.ts +36 -0
  205. package/server/plugin/bitran/content.ts +252 -187
  206. package/server/plugin/bitran/{products → elements}/include.ts +229 -230
  207. package/server/plugin/bitran/location.ts +43 -25
  208. package/server/plugin/bitran/toc.ts +94 -83
  209. package/server/plugin/bitran/transpiler.ts +18 -46
  210. package/server/plugin/build/close.ts +12 -10
  211. package/server/plugin/build/jobs/content/builderArgs.ts +8 -8
  212. package/server/plugin/build/jobs/content/generic.ts +191 -176
  213. package/server/plugin/build/jobs/content/parse.ts +113 -100
  214. package/server/plugin/build/jobs/content/path.ts +6 -6
  215. package/server/plugin/build/jobs/content/type/book.ts +9 -9
  216. package/server/plugin/build/jobs/content/type/group.ts +37 -37
  217. package/server/plugin/build/jobs/content/type/topic.ts +36 -36
  218. package/server/plugin/build/jobs/contributors.ts +69 -66
  219. package/server/plugin/build/jobs/language.ts +36 -36
  220. package/server/plugin/build/jobs/nav.ts +345 -210
  221. package/server/plugin/build/process.ts +34 -25
  222. package/server/plugin/build/rebuild.ts +68 -55
  223. package/server/plugin/build/setup.ts +19 -21
  224. package/server/plugin/content/context.ts +119 -112
  225. package/server/plugin/db/entities/Book.ts +7 -7
  226. package/server/plugin/db/entities/Content.ts +45 -49
  227. package/server/plugin/db/entities/Contribution.ts +10 -10
  228. package/server/plugin/db/entities/Contributor.ts +25 -16
  229. package/server/plugin/db/entities/File.ts +10 -0
  230. package/server/plugin/db/entities/Group.ts +14 -14
  231. package/server/plugin/db/entities/Hash.ts +15 -15
  232. package/server/plugin/db/entities/Topic.ts +20 -20
  233. package/server/plugin/db/entities/Unique.ts +21 -21
  234. package/server/plugin/db/reset.ts +12 -0
  235. package/server/plugin/db/setup.ts +49 -34
  236. package/server/plugin/global.ts +18 -18
  237. package/server/plugin/importer.ts +16 -12
  238. package/server/plugin/index.ts +9 -9
  239. package/server/plugin/logger.ts +23 -23
  240. package/server/plugin/nav/node.ts +27 -26
  241. package/server/plugin/nav/utils.ts +179 -129
  242. package/server/plugin/repository/asideMinor.ts +51 -0
  243. package/server/plugin/repository/book.ts +39 -21
  244. package/server/plugin/repository/cameo.ts +16 -0
  245. package/server/plugin/repository/content.ts +240 -238
  246. package/server/plugin/repository/contentId.ts +40 -0
  247. package/server/plugin/repository/contributor.ts +129 -8
  248. package/server/plugin/repository/file.ts +10 -0
  249. package/server/plugin/repository/frontNav.ts +145 -148
  250. package/server/plugin/repository/topic.ts +35 -32
  251. package/server/plugin/sponsor/build.ts +82 -0
  252. package/server/plugin/sponsor/index.ts +5 -0
  253. package/server/plugin/sponsor/repository.ts +56 -0
  254. package/server/routes/asset/[...assetPath].ts +34 -0
  255. package/server/routes/robots.txt.ts +9 -0
  256. package/server/routes/sitemap.xml.ts +103 -0
  257. package/server/tsconfig.json +9 -9
  258. package/shared/aside/minor.ts +55 -50
  259. package/shared/asset.ts +17 -15
  260. package/shared/bitran/content.ts +9 -0
  261. package/shared/bitran/contentId.ts +56 -0
  262. package/shared/bitran/toc.ts +8 -8
  263. package/shared/breadcrumb.ts +7 -0
  264. package/shared/content/bookId.ts +12 -0
  265. package/shared/content/context.ts +9 -9
  266. package/shared/content/data/base.ts +32 -32
  267. package/shared/content/data/index.ts +5 -5
  268. package/shared/content/data/type/book.ts +5 -5
  269. package/shared/content/data/type/group.ts +6 -6
  270. package/shared/content/data/type/topic.ts +11 -11
  271. package/shared/content/previousNext.ts +9 -9
  272. package/shared/contributor.ts +34 -5
  273. package/shared/frontNav.ts +41 -41
  274. package/shared/icons.ts +38 -38
  275. package/shared/image.ts +5 -5
  276. package/shared/link.ts +28 -25
  277. package/shared/popover.ts +8 -0
  278. package/shared/types/language.ts +84 -75
  279. package/shared/utils/objectsEqual.ts +4 -4
  280. package/shared/utils/stringColor.ts +9 -9
  281. package/test/contentId.test.ts +91 -0
  282. package/test/utils/url.test.ts +99 -0
  283. package/tsconfig.json +8 -8
  284. package/utils/contentPath.ts +67 -0
  285. package/utils/ext.ts +41 -0
  286. package/utils/stress.ts +9 -9
  287. package/utils/url.ts +23 -0
  288. package/app/components/ads/BannerTemplate.vue +0 -51
  289. package/app/components/ads/BottomBanner.vue +0 -45
  290. package/app/components/ads/LeftBanner.vue +0 -50
  291. package/app/components/aside/minor/AsideMinorContributor.vue +0 -5
  292. package/app/components/contributor/ContributorAvatar.vue +0 -43
  293. package/app/components/main/utils/ContentDescription.vue +0 -19
  294. package/app/components/main/utils/ContentFlag.vue +0 -15
  295. package/app/components/main/utils/ContentSection.vue +0 -45
  296. package/app/components/main/utils/ContentTitle.vue +0 -39
  297. package/app/composables/bitranContent.ts +0 -37
  298. package/app/pages/members.vue +0 -6
  299. package/app/public/user.svg +0 -10
  300. package/app/scripts/aside/minor/topic.ts +0 -3
  301. package/app/styles/default.scss +0 -83
  302. package/server/api/aside/minor/path.ts +0 -78
  303. package/server/api/bitran/content/[location].ts +0 -7
  304. package/server/api/bitran/toc/[location].ts +0 -7
  305. package/server/plugin/bitran/products/link.ts +0 -116
  306. package/server/plugin/bitran/setup.ts +0 -9
  307. package/server/plugin/content/absoluteId.ts +0 -94
  308. package/shared/bitran/context.ts +0 -8
  309. package/shared/bitran/default.ts +0 -46
  310. package/shared/bitran/link/Link.vue +0 -167
  311. package/shared/bitran/link/factory.ts +0 -24
  312. package/shared/bitran/link/icon.svg +0 -3
  313. package/shared/bitran/link/languages/en.ts +0 -7
  314. package/shared/bitran/link/languages/ru.ts +0 -7
  315. package/shared/bitran/link/renderer.ts +0 -21
  316. package/shared/bitran/link/shared.ts +0 -17
  317. package/shared/bitran/link/target.ts +0 -134
  318. package/shared/bitran/link/transpiler.ts +0 -10
  319. package/shared/bitran/location.ts +0 -166
  320. package/test/bitran/link/target.test.ts +0 -141
  321. package/test/bitran/location.test.ts +0 -143
@@ -1,238 +1,240 @@
1
- import type { ContentType } from 'erudit-cog/schema';
2
- import { Like } from 'typeorm';
3
-
4
- import { ERUDIT_SERVER } from '@server/global';
5
- import { DbContent } from '@server/db/entities/Content';
6
- import { DbContribution } from '@server/db/entities/Contribution';
7
-
8
- import { getContentContext } from '@server/content/context';
9
- import type { ContentGenericData } from '@shared/content/data/base';
10
- import { getIdsUp, getPreviousNextNav } from '../nav/utils';
11
- import { createContentLink, createTopicPartLink } from '@erudit/shared/link';
12
- import { getTopicPart } from './topic';
13
- import type { PreviousNextItem } from '@erudit/shared/content/previousNext';
14
- import type { NavNode } from '../nav/node';
15
- import { toAbsoluteContentId } from '../content/absoluteId';
16
-
17
- import type { ContentContributor } from '@erudit/shared/contributor';
18
- import { DbContributor } from '../db/entities/Contributor';
19
-
20
- export async function getContentGenericData(
21
- contentId: string,
22
- ): Promise<ContentGenericData> {
23
- const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
24
- where: { contentId },
25
- });
26
-
27
- if (!dbContent)
28
- throw createError({
29
- statusCode: 404,
30
- message: `Content item "${contentId}" not found!`,
31
- });
32
-
33
- const previousNext = await getPreviousNext(contentId);
34
- const decoration = await getContentDecoration(contentId);
35
- const flags = await getContentFlags(contentId);
36
-
37
- const contentPage: ContentGenericData = {
38
- contentId,
39
- type: dbContent.type,
40
- title: dbContent.title || undefined,
41
- description: dbContent.description || undefined,
42
- flags,
43
- previousNext,
44
- decoration,
45
- context: await getContentContext(contentId),
46
- seo: dbContent.seo,
47
- ogImage: dbContent.ogImage,
48
- dependencies: dbContent.dependencies
49
- ? await getContentDependencies(contentId, dbContent.dependencies)
50
- : undefined,
51
- references: dbContent.references,
52
- };
53
-
54
- return contentPage;
55
- }
56
-
57
- export async function getPreviousNext(contentId: string) {
58
- const { previousNav, nextNav } = await getPreviousNextNav(contentId);
59
-
60
- async function getItemData(navNode: NavNode): Promise<PreviousNextItem> {
61
- const title = await getContentTitle(navNode.id);
62
-
63
- const link = await (async () => {
64
- if (navNode.type === 'topic')
65
- return createTopicPartLink(
66
- await getTopicPart(navNode.id),
67
- navNode.id,
68
- );
69
-
70
- return createContentLink(navNode.type, navNode.id);
71
- })();
72
-
73
- return {
74
- title,
75
- link,
76
- };
77
- }
78
-
79
- return {
80
- previous: previousNav ? await getItemData(previousNav) : undefined,
81
- next: nextNav ? await getItemData(nextNav) : undefined,
82
- };
83
- }
84
-
85
- export async function getContentDecoration(contentId: string) {
86
- const idsUp = await getIdsUp(contentId);
87
-
88
- for (const id of idsUp) {
89
- const decoration = (
90
- await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
91
- select: ['decoration'],
92
- where: { contentId: id },
93
- })
94
- )?.decoration;
95
-
96
- if (decoration) return decoration;
97
- }
98
-
99
- return undefined;
100
- }
101
-
102
- export async function getContentFlags(contentId: string) {
103
- const idsUp = await getIdsUp(contentId);
104
- const flags: any = {};
105
-
106
- for (const id of idsUp.reverse()) {
107
- const dbFlags = (
108
- await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
109
- select: ['flags'],
110
- where: { contentId: id },
111
- })
112
- )?.flags;
113
-
114
- if (dbFlags)
115
- for (const [flagName, flagValue] of Object.entries(dbFlags))
116
- flags[flagName] = flagValue;
117
- }
118
-
119
- for (const [flagName, flagValue] of Object.entries(flags))
120
- if (flagValue === false) delete flags[flagName];
121
-
122
- return flags;
123
- }
124
-
125
- export async function getContentDependencies(
126
- contentId: string,
127
- strDependencies: string[],
128
- ) {
129
- const dependencyIds = strDependencies.map((rawDependency) =>
130
- toAbsoluteContentId(rawDependency, contentId),
131
- );
132
- const dependencies: Record<string, string> = {};
133
-
134
- for (const dependencyId of dependencyIds)
135
- dependencies[await getContentLink(dependencyId)] =
136
- await getContentTitle(dependencyId);
137
-
138
- return dependencies;
139
- }
140
-
141
- export async function getContentTitle(contentId: string) {
142
- const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
143
- select: ['title'],
144
- where: { contentId },
145
- });
146
-
147
- return dbContent?.title || contentId.split('/').pop()!;
148
- }
149
-
150
- export async function getContentLink(contentId: string) {
151
- const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
152
- select: ['type'],
153
- where: { contentId },
154
- });
155
-
156
- if (!dbContent)
157
- throw createError({
158
- statusCode: 404,
159
- statusText: `Missing "${contentId}" content item!`,
160
- });
161
-
162
- if (dbContent.type !== 'topic')
163
- return createContentLink(dbContent.type, contentId);
164
-
165
- const topicPart = await getTopicPart(contentId);
166
-
167
- return createTopicPartLink(topicPart, contentId);
168
- }
169
-
170
- export async function getFullContentId(contentId: string) {
171
- const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
172
- select: ['fullId'],
173
- where: { contentId },
174
- });
175
-
176
- return dbContent?.fullId;
177
- }
178
-
179
- export async function getContentContributors(contentId: string) {
180
- const contributorIds = await (async () => {
181
- const idHash: Record<string, true> = {};
182
-
183
- (
184
- await ERUDIT_SERVER.DB.manager.find(DbContribution, {
185
- select: ['contributorId'],
186
- where: { contentId },
187
- })
188
- ).forEach(
189
- (contribution) => (idHash[contribution.contributorId] = true),
190
- );
191
-
192
- const type: ContentType = (await ERUDIT_SERVER.DB.manager.findOne(
193
- DbContent,
194
- {
195
- select: ['type'],
196
- where: { contentId },
197
- },
198
- ))!.type;
199
-
200
- if (type === 'book' || type === 'group') {
201
- // Collecting contributors for all sub-content items
202
- (
203
- await ERUDIT_SERVER.DB.manager.find(DbContribution, {
204
- select: ['contributorId'],
205
- where: { contentId: Like(`${contentId}/%`) },
206
- })
207
- ).forEach(
208
- (contribution) => (idHash[contribution.contributorId] = true),
209
- );
210
- }
211
-
212
- return Object.keys(idHash);
213
- })();
214
-
215
- const contentContributors: ContentContributor[] = [];
216
-
217
- for (const contributorId of contributorIds) {
218
- const dbContributor = (await ERUDIT_SERVER.DB.manager.findOne(
219
- DbContributor,
220
- {
221
- select: ['contributorId', 'displayName', 'avatar'],
222
- where: { contributorId },
223
- },
224
- ))!;
225
-
226
- const contentContributor: ContentContributor = { contributorId };
227
-
228
- if (dbContributor.displayName)
229
- contentContributor.displayName = dbContributor.displayName;
230
-
231
- if (dbContributor.avatar)
232
- contentContributor.avatar = dbContributor.avatar;
233
-
234
- contentContributors.push(contentContributor);
235
- }
236
-
237
- return contentContributors.length ? contentContributors : undefined;
238
- }
1
+ import type { ContentType } from '@erudit-js/cog/schema';
2
+ import { Like } from 'typeorm';
3
+
4
+ import { ERUDIT_SERVER } from '@server/global';
5
+ import { DbContent } from '@server/db/entities/Content';
6
+ import { DbContribution } from '@server/db/entities/Contribution';
7
+ import { getContentContext } from '@server/content/context';
8
+ import { DbContributor } from '@server/db/entities/Contributor';
9
+ import { getIdsUp, getPreviousNextNav } from '@server/nav/utils';
10
+ import {
11
+ getShortContentId,
12
+ resolveClientContentId,
13
+ } from '@server/repository/contentId';
14
+ import { getTopicPart } from './topic';
15
+ import type { NavNode } from '../nav/node';
16
+
17
+ import { createContentLink, createTopicPartLink } from '@erudit/shared/link';
18
+ import type { PreviousNextItem } from '@erudit/shared/content/previousNext';
19
+ import type { ContentContributor } from '@erudit/shared/contributor';
20
+ import type { ContentGenericData } from '@shared/content/data/base';
21
+
22
+ export async function getContentGenericData(
23
+ contentId: string,
24
+ ): Promise<ContentGenericData> {
25
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
26
+ where: { contentId },
27
+ });
28
+
29
+ if (!dbContent) {
30
+ throw createError({
31
+ statusCode: 404,
32
+ message: `Content item "${contentId}" not found!`,
33
+ });
34
+ }
35
+
36
+ const previousNext = await getPreviousNext(contentId);
37
+ const decoration = await getContentDecoration(contentId);
38
+ const flags = await getContentFlags(contentId);
39
+
40
+ const contentPage: ContentGenericData = {
41
+ contentId,
42
+ type: dbContent.type,
43
+ title: dbContent.title || undefined,
44
+ description: dbContent.description || undefined,
45
+ flags,
46
+ previousNext,
47
+ decoration,
48
+ context: await getContentContext(contentId),
49
+ seo: dbContent.seo,
50
+ ogImage: dbContent.ogImage,
51
+ dependencies: dbContent.dependencies
52
+ ? await getContentDependencies(contentId, dbContent.dependencies)
53
+ : undefined,
54
+ references: dbContent.references,
55
+ };
56
+
57
+ return contentPage;
58
+ }
59
+
60
+ export async function getPreviousNext(contentId: string) {
61
+ const { previousNav, nextNav } = await getPreviousNextNav(contentId);
62
+
63
+ async function getItemData(navNode: NavNode): Promise<PreviousNextItem> {
64
+ const title = await getContentTitle(navNode.fullId);
65
+
66
+ const link = await (async () => {
67
+ if (navNode.type === 'topic')
68
+ return createTopicPartLink(
69
+ await getTopicPart(navNode.fullId),
70
+ navNode.shortId,
71
+ );
72
+
73
+ return createContentLink(navNode.type, navNode.shortId);
74
+ })();
75
+
76
+ return {
77
+ title,
78
+ link,
79
+ };
80
+ }
81
+
82
+ return {
83
+ previous: previousNav ? await getItemData(previousNav) : undefined,
84
+ next: nextNav ? await getItemData(nextNav) : undefined,
85
+ };
86
+ }
87
+
88
+ export async function getContentDecoration(contentId: string) {
89
+ const idsUp = await getIdsUp(contentId);
90
+
91
+ for (const id of idsUp) {
92
+ const decoration = (
93
+ await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
94
+ select: ['decoration'],
95
+ where: { contentId: id },
96
+ })
97
+ )?.decoration;
98
+
99
+ if (decoration) return decoration;
100
+ }
101
+
102
+ return undefined;
103
+ }
104
+
105
+ export async function getContentFlags(contentId: string) {
106
+ const idsUp = await getIdsUp(contentId);
107
+ const flags: any = {};
108
+
109
+ for (const id of idsUp.reverse()) {
110
+ const dbFlags = (
111
+ await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
112
+ select: ['flags'],
113
+ where: { contentId: id },
114
+ })
115
+ )?.flags;
116
+
117
+ if (dbFlags)
118
+ for (const [flagName, flagValue] of Object.entries(dbFlags))
119
+ flags[flagName] = flagValue;
120
+ }
121
+
122
+ for (const [flagName, flagValue] of Object.entries(flags))
123
+ if (flagValue === false) delete flags[flagName];
124
+
125
+ return flags;
126
+ }
127
+
128
+ export async function getContentDependencies(
129
+ contentId: string,
130
+ strDependencies: string[],
131
+ ) {
132
+ const dependencyIds: string[] = [];
133
+ for (const rawDependency of strDependencies) {
134
+ dependencyIds.push(
135
+ resolveClientContentId(rawDependency, contentId, 'full'),
136
+ );
137
+ }
138
+
139
+ const dependencies: Record<string, string> = {};
140
+
141
+ for (const dependencyId of dependencyIds)
142
+ dependencies[await getContentLink(dependencyId)] =
143
+ await getContentTitle(dependencyId);
144
+
145
+ return dependencies;
146
+ }
147
+
148
+ export async function getContentTitle(contentId: string) {
149
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
150
+ select: ['title'],
151
+ where: { contentId },
152
+ });
153
+
154
+ return dbContent?.title || contentId.split('/').pop()!;
155
+ }
156
+
157
+ export async function getContentLink(contentId: string) {
158
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
159
+ select: ['type'],
160
+ where: { contentId },
161
+ });
162
+
163
+ if (!dbContent) {
164
+ throw createError({
165
+ statusCode: 404,
166
+ statusText: `Missing "${contentId}" content item!`,
167
+ });
168
+ }
169
+
170
+ const shortContentId = getShortContentId(contentId);
171
+
172
+ if (dbContent.type !== 'topic') {
173
+ return createContentLink(dbContent.type, shortContentId);
174
+ }
175
+
176
+ const topicPart = await getTopicPart(contentId);
177
+
178
+ return createTopicPartLink(topicPart, shortContentId);
179
+ }
180
+
181
+ export async function getContentContributors(contentId: string) {
182
+ const contributorIds = await (async () => {
183
+ const idHash: Record<string, true> = {};
184
+
185
+ (
186
+ await ERUDIT_SERVER.DB.manager.find(DbContribution, {
187
+ select: ['contributorId'],
188
+ where: { contentId },
189
+ })
190
+ ).forEach(
191
+ (contribution) => (idHash[contribution.contributorId] = true),
192
+ );
193
+
194
+ const type: ContentType = (await ERUDIT_SERVER.DB.manager.findOne(
195
+ DbContent,
196
+ {
197
+ select: ['type'],
198
+ where: { contentId },
199
+ },
200
+ ))!.type;
201
+
202
+ if (type === 'book' || type === 'group') {
203
+ // Collecting contributors for all sub-content items
204
+ (
205
+ await ERUDIT_SERVER.DB.manager.find(DbContribution, {
206
+ select: ['contributorId'],
207
+ where: { contentId: Like(`${contentId}/%`) },
208
+ })
209
+ ).forEach(
210
+ (contribution) => (idHash[contribution.contributorId] = true),
211
+ );
212
+ }
213
+
214
+ return Object.keys(idHash);
215
+ })();
216
+
217
+ const contentContributors: ContentContributor[] = [];
218
+
219
+ for (const contributorId of contributorIds) {
220
+ const dbContributor = (await ERUDIT_SERVER.DB.manager.findOne(
221
+ DbContributor,
222
+ {
223
+ select: ['contributorId', 'displayName', 'avatar'],
224
+ where: { contributorId },
225
+ },
226
+ ))!;
227
+
228
+ const contentContributor: ContentContributor = { contributorId };
229
+
230
+ if (dbContributor.displayName)
231
+ contentContributor.displayName = dbContributor.displayName;
232
+
233
+ if (dbContributor.avatar)
234
+ contentContributor.avatar = dbContributor.avatar;
235
+
236
+ contentContributors.push(contentContributor);
237
+ }
238
+
239
+ return contentContributors.length ? contentContributors : undefined;
240
+ }
@@ -0,0 +1,40 @@
1
+ import { getNavBookIds, getNavNode } from '@server/nav/utils';
2
+ import { toAbsoluteContentPath } from '@shared/bitran/contentId';
3
+
4
+ export function getFullContentId(mixedContentId: string): string {
5
+ const navNode = getNavNode(mixedContentId);
6
+ return navNode.fullId;
7
+ }
8
+
9
+ export function getShortContentId(mixedContentId: string): string {
10
+ const navNode = getNavNode(mixedContentId);
11
+ return navNode.shortId;
12
+ }
13
+
14
+ export function serverAbsolutizeContentPath(
15
+ relativePath: string,
16
+ contextPath: string,
17
+ ) {
18
+ const absolutePath = toAbsoluteContentPath(
19
+ relativePath,
20
+ contextPath,
21
+ getNavBookIds('full'),
22
+ );
23
+
24
+ return absolutePath;
25
+ }
26
+
27
+ export function resolveClientContentId(
28
+ clientContentId: string,
29
+ contextContentId: string,
30
+ mode: 'full' | 'short',
31
+ ): string {
32
+ const absoluteContentId = serverAbsolutizeContentPath(
33
+ clientContentId,
34
+ contextContentId,
35
+ );
36
+
37
+ return mode === 'full'
38
+ ? getFullContentId(absoluteContentId)
39
+ : getShortContentId(absoluteContentId);
40
+ }
@@ -1,8 +1,129 @@
1
- import { DbContributor } from '@server/db/entities/Contributor';
2
- import { ERUDIT_SERVER } from '@server/global';
3
-
4
- export async function contributorExists(contributorId: string) {
5
- return await ERUDIT_SERVER.DB.manager.exists(DbContributor, {
6
- where: { contributorId },
7
- });
8
- }
1
+ import { DbContributor } from '@server/db/entities/Contributor';
2
+ import { DbContribution } from '@server/db/entities/Contribution';
3
+ import { ERUDIT_SERVER } from '@server/global';
4
+ import { getNavBookOf, getNavNode } from '@server/nav/utils';
5
+ import { getBookTitleFor } from '@server/repository/book';
6
+ import { getContentLink, getContentTitle } from '@server/repository/content';
7
+
8
+ import type { Contribution, PageContributor } from '@shared/contributor';
9
+
10
+ export async function contributorExists(contributorId: string) {
11
+ return await ERUDIT_SERVER.DB.manager.exists(DbContributor, {
12
+ where: { contributorId },
13
+ });
14
+ }
15
+
16
+ export async function getContributorPageData(
17
+ contributorId: string,
18
+ ): Promise<PageContributor> {
19
+ if (!(await contributorExists(contributorId))) {
20
+ throw createError({
21
+ statusCode: 404,
22
+ message: `Contributor ${contributorId} not found!`,
23
+ });
24
+ }
25
+
26
+ const dbContributor = (await ERUDIT_SERVER.DB.manager.findOne(
27
+ DbContributor,
28
+ {
29
+ select: [
30
+ 'displayName',
31
+ 'slogan',
32
+ 'links',
33
+ 'avatar',
34
+ 'isEditor',
35
+ 'description',
36
+ ],
37
+ where: { contributorId },
38
+ },
39
+ ))!;
40
+
41
+ return {
42
+ contributorId,
43
+ displayName: dbContributor.displayName,
44
+ slogan: dbContributor.slogan,
45
+ links: dbContributor.links,
46
+ avatar: dbContributor.avatar,
47
+ isEditor: dbContributor.isEditor,
48
+ hasDescription: !!dbContributor.description,
49
+ contributions: await getContributions(contributorId),
50
+ };
51
+ }
52
+
53
+ export async function getContributions(
54
+ contributorId: string,
55
+ ): Promise<Contribution[]> {
56
+ const dbContributions = await ERUDIT_SERVER.DB.manager.find(
57
+ DbContribution,
58
+ {
59
+ select: ['contentId'],
60
+ where: { contributorId },
61
+ },
62
+ );
63
+
64
+ const contributions: Contribution[] = [];
65
+ const existingBookTitles = new Set<string>();
66
+
67
+ for (const dbContribution of dbContributions) {
68
+ const contentId = dbContribution.contentId;
69
+ const contentNavNode = getNavNode(contentId);
70
+
71
+ const bookData = await (async () => {
72
+ const bookNavNode = getNavBookOf(contentId);
73
+
74
+ if (!bookNavNode) {
75
+ return undefined;
76
+ }
77
+
78
+ const bookId = bookNavNode.fullId;
79
+
80
+ if (!bookId) {
81
+ return undefined;
82
+ }
83
+
84
+ if (bookId === contentId) {
85
+ return undefined;
86
+ }
87
+
88
+ let bookTitle = await getBookTitleFor(bookId);
89
+
90
+ let cursor = bookNavNode;
91
+ while (cursor) {
92
+ if (cursor.type === 'group') {
93
+ const groupTitle = await getContentTitle(cursor.fullId);
94
+ const newBookTitle = groupTitle + ' / ' + bookTitle;
95
+
96
+ if (bookTitle && existingBookTitles.has(bookTitle)) {
97
+ bookTitle = newBookTitle;
98
+ }
99
+ }
100
+
101
+ cursor = cursor.parent as any;
102
+ }
103
+
104
+ if (bookTitle) {
105
+ existingBookTitles.add(bookTitle);
106
+ }
107
+
108
+ return {
109
+ bookId,
110
+ bookTitle,
111
+ };
112
+ })();
113
+
114
+ const contentTitle = await getContentTitle(contentId);
115
+ const contentLink = await getContentLink(contentId);
116
+
117
+ const contribution: Contribution = {
118
+ bookId: bookData?.bookId,
119
+ bookTitle: bookData?.bookTitle,
120
+ contentType: contentNavNode.type,
121
+ contentTitle,
122
+ contentLink,
123
+ };
124
+
125
+ contributions.push(contribution);
126
+ }
127
+
128
+ return contributions;
129
+ }