@ossy/resources 1.11.7 → 1.12.1

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 (69) hide show
  1. package/package.json +10 -4
  2. package/src/AudioResource.jsx +4 -2
  3. package/src/CreateDirectory.jsx +14 -13
  4. package/src/CreateDocument.jsx +7 -5
  5. package/src/Definition.js +2 -8
  6. package/src/DocumentEdit.jsx +9 -6
  7. package/src/DocumentView.jsx +8 -4
  8. package/src/GenericResourceCard.jsx +4 -2
  9. package/src/GenericResourceDetail.jsx +9 -4
  10. package/src/GenericResourceForm.jsx +14 -7
  11. package/src/ImageResource.jsx +4 -2
  12. package/src/PDFResource.jsx +4 -2
  13. package/src/ResourceContentPage.jsx +4 -2
  14. package/src/ResourceDescription.jsx +4 -3
  15. package/src/ResourceDetails.jsx +9 -4
  16. package/src/ResourceDialogMove.jsx +4 -3
  17. package/src/ResourceFactory.jsx +6 -3
  18. package/src/ResourceGenericView.jsx +4 -2
  19. package/src/ResourceList.jsx +4 -2
  20. package/src/ResourcePanel.jsx +9 -12
  21. package/src/ResourceTags.jsx +4 -3
  22. package/src/ResourcesPage.jsx +8 -6
  23. package/src/UploadResources.jsx +9 -11
  24. package/src/VideoResource.jsx +4 -2
  25. package/src/create-page-view.action.js +1 -38
  26. package/src/create-page-view.task.js +38 -0
  27. package/src/create.action.js +1 -98
  28. package/src/create.task.js +100 -0
  29. package/src/delete.action.js +1 -31
  30. package/src/delete.task.js +31 -0
  31. package/src/en.translations.json +48 -0
  32. package/src/get-page-view-stats.action.js +1 -65
  33. package/src/get-page-view-stats.task.js +64 -0
  34. package/src/get.action.js +1 -14
  35. package/src/get.task.js +14 -0
  36. package/src/index.js +13 -4
  37. package/src/list.action.js +1 -19
  38. package/src/list.task.js +20 -0
  39. package/src/resource-create.page.jsx +7 -5
  40. package/src/resource-detail.page.jsx +0 -2
  41. package/src/resource.helpers.js +83 -0
  42. package/src/resources.action-media.js +18 -0
  43. package/src/resources.attach-media-urls.js +1 -1
  44. package/src/resources.events.js +1 -0
  45. package/src/resources.spec.js +54 -55
  46. package/src/search.action.js +1 -19
  47. package/src/search.task.js +20 -0
  48. package/src/server.js +4 -0
  49. package/src/sv.translations.json +48 -0
  50. package/src/update-access.action.js +1 -20
  51. package/src/update-access.task.js +22 -0
  52. package/src/update-content.action.js +1 -39
  53. package/src/update-content.task.js +41 -0
  54. package/src/update-location.action.js +1 -18
  55. package/src/update-location.task.js +20 -0
  56. package/src/update-name.action.js +1 -18
  57. package/src/update-name.task.js +20 -0
  58. package/src/upload-named-version.action.js +1 -42
  59. package/src/upload-named-version.task.js +46 -0
  60. package/src/item-access.api.js +0 -34
  61. package/src/item-content.api.js +0 -35
  62. package/src/item-location.api.js +0 -34
  63. package/src/item-name.api.js +0 -34
  64. package/src/item-version.api.js +0 -48
  65. package/src/item.api.js +0 -58
  66. package/src/list.api.js +0 -43
  67. package/src/page-views-stats.api.js +0 -23
  68. package/src/page-views.api.js +0 -23
  69. package/src/search.api.js +0 -27
package/src/index.js CHANGED
@@ -1,7 +1,15 @@
1
- export * from './resource.aggregate.js'
2
- export * from './resources.events.js'
3
- export * from './resources.queries.js'
4
-
1
+ export { metadata as CreateResource } from './create.action.js'
2
+ export { metadata as GetResource } from './get.action.js'
3
+ export { metadata as ListResources } from './list.action.js'
4
+ export { metadata as SearchResources } from './search.action.js'
5
+ export { metadata as DeleteResource } from './delete.action.js'
6
+ export { metadata as UpdateResourceContent } from './update-content.action.js'
7
+ export { metadata as UpdateResourceLocation } from './update-location.action.js'
8
+ export { metadata as UpdateResourceName } from './update-name.action.js'
9
+ export { metadata as UpdateResourceAccess } from './update-access.action.js'
10
+ export { metadata as UploadNamedVersion } from './upload-named-version.action.js'
11
+ export { metadata as CreatePageView } from './create-page-view.action.js'
12
+ export { metadata as GetPageViewStats } from './get-page-view-stats.action.js'
5
13
  export * from './AudioResource.jsx'
6
14
  export * from './CreateDirectory.jsx'
7
15
  export * from './CreateDocument.jsx'
@@ -29,3 +37,4 @@ export * from './UploadResources.jsx'
29
37
  export * from './VideoResource.jsx'
30
38
  export * from './useActivePath.jsx'
31
39
  export * from './useForm.js'
40
+ export * from './resource.helpers.js'
@@ -1,19 +1 @@
1
- import { ResourcesQueries } from './resources.queries.js'
2
-
3
- export const id = 'resources/list'
4
- export const access = 'workspace'
5
-
6
- export async function run({ payload, req }) {
7
- const query = { ...(payload?.query ?? {}) }
8
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
9
- const user = payload?.user ?? req?.user
10
- let location = payload?.location ?? query.location
11
-
12
- if (location && !location.endsWith('/')) location += '/'
13
- if (location && !location.startsWith('/')) location = '/' + location
14
- if (location) query.location = location
15
-
16
- if (workspaceId) query.belongsTo = workspaceId
17
-
18
- return ResourcesQueries.GetResources(query, user)
19
- }
1
+ export const metadata = { id: 'resources/list', access: 'workspace' }
@@ -0,0 +1,20 @@
1
+ import { ResourcesQueries } from './resources.queries.js'
2
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
3
+
4
+ export const metadata = { id: 'resources/list' }
5
+
6
+ export async function run({ payload, req }) {
7
+ const query = { ...(payload?.query ?? {}) }
8
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
9
+ const user = payload?.user ?? req?.user
10
+ let location = payload?.location ?? query.location
11
+
12
+ if (location && !location.endsWith('/')) location += '/'
13
+ if (location && !location.startsWith('/')) location = '/' + location
14
+ if (location) query.location = location
15
+
16
+ if (workspaceId) query.belongsTo = workspaceId
17
+
18
+ const resources = await ResourcesQueries.GetResources(query, user)
19
+ return withResourceMedia(resources, mediaContextFromRun({ payload, req }))
20
+ }
@@ -1,6 +1,7 @@
1
1
  import React, { useState } from 'react'
2
- import { useResources } from '@ossy/sdk-react'
3
- import { View } from '@ossy/design-system'
2
+ import { createDocument } from './resource.helpers.js'
3
+ import { useSdk } from '@ossy/sdk-react'
4
+ import { View, useLocale } from '@ossy/design-system'
4
5
  import { useRouter } from '@ossy/router-react'
5
6
  import { GenericResourceForm } from './GenericResourceForm.jsx'
6
7
 
@@ -15,9 +16,10 @@ export const metadata = {
15
16
  /** Generic resource create page — slot lookup with template Fields fallback. */
16
17
  export default function GenericResourceCreatePage () {
17
18
  const router = useRouter()
19
+ const { t } = useLocale()
18
20
  const templateId = decodeURIComponent(router.params.templateId || '')
19
21
  const location = router.searchParams.location
20
- const { createDocument } = useResources()
22
+ const sdk = useSdk()
21
23
  const [error, setError] = useState()
22
24
 
23
25
  const onCancel = () => {
@@ -26,9 +28,9 @@ export default function GenericResourceCreatePage () {
26
28
  }
27
29
 
28
30
  const onSubmit = ({ name, content, type }) => {
29
- createDocument({ type, location, name, content })
31
+ createDocument(sdk, { type, location, name, content })
30
32
  .then(() => onCancel())
31
- .catch((err) => setError(err?.message || 'Create failed'))
33
+ .catch((err) => setError(err?.message || t('resources.form.createFailed')))
32
34
  }
33
35
 
34
36
  return (
@@ -5,14 +5,12 @@ import { GenericResourceDetail } from './GenericResourceDetail.jsx'
5
5
 
6
6
  export const metadata = {
7
7
  id: '@resource',
8
- title: 'Resource',
9
8
  path: {
10
9
  en: '/resources/item/:resourceId',
11
10
  sv: '/resources/objekt/:resourceId',
12
11
  },
13
12
  }
14
13
 
15
- /** Generic resource detail page — slot lookup with template fallback. */
16
14
  export default function ResourceDetailPage () {
17
15
  const router = useRouter()
18
16
  const resourceId = router.params.resourceId
@@ -0,0 +1,83 @@
1
+ import { cacheKey, normalizeLocation } from '@ossy/sdk-react'
2
+ import { metadata as CreateResource } from './create.action.js'
3
+ import { metadata as DeleteResource } from './delete.action.js'
4
+ import { metadata as ListResources } from './list.action.js'
5
+ import { metadata as UpdateResourceAccess } from './update-access.action.js'
6
+ import { metadata as UpdateResourceContent } from './update-content.action.js'
7
+ import { metadata as UpdateResourceLocation } from './update-location.action.js'
8
+ import { metadata as UpdateResourceName } from './update-name.action.js'
9
+
10
+ function invalidateLocation(sdk, location) {
11
+ sdk.invalidate(cacheKey(ListResources, { location: normalizeLocation(location) }))
12
+ }
13
+
14
+ export async function createDocument(sdk, document) {
15
+ const resource = await sdk.invoke(CreateResource, {
16
+ type: document.type,
17
+ location: normalizeLocation(document.location),
18
+ name: document.name,
19
+ content: document.content,
20
+ })
21
+ invalidateLocation(sdk, resource.location)
22
+ return resource
23
+ }
24
+
25
+ export async function createDirectory(sdk, { location, name }) {
26
+ const resource = await sdk.invoke(CreateResource, {
27
+ type: 'directory',
28
+ location: normalizeLocation(location),
29
+ name,
30
+ })
31
+ invalidateLocation(sdk, resource.location)
32
+ return resource
33
+ }
34
+
35
+ export async function uploadFile(sdk, uploadLocation, file) {
36
+ const location = normalizeLocation(uploadLocation)
37
+ const resource = await sdk.invoke(CreateResource, {
38
+ type: file.type,
39
+ location,
40
+ name: file.name,
41
+ size: file.size,
42
+ })
43
+ if (resource.content?.uploadUrl) {
44
+ await fetch(resource.content.uploadUrl, { method: 'PUT', body: file })
45
+ }
46
+ invalidateLocation(sdk, location)
47
+ return resource
48
+ }
49
+
50
+ export async function removeResource(sdk, id, location) {
51
+ await sdk.invoke(DeleteResource, { id })
52
+ if (location) invalidateLocation(sdk, location)
53
+ sdk.invalidate(cacheKey({ id: 'resources/get' }, { id }))
54
+ }
55
+
56
+ export async function updateResourceContent(sdk, id, content) {
57
+ const resource = await sdk.invoke(UpdateResourceContent, { id, content })
58
+ invalidateLocation(sdk, resource.location)
59
+ sdk.invalidate(cacheKey({ id: 'resources/get' }, { id }))
60
+ return resource
61
+ }
62
+
63
+ export async function moveResource(sdk, id, target, sourceLocation) {
64
+ const resource = await sdk.invoke(UpdateResourceLocation, { id, target })
65
+ invalidateLocation(sdk, resource.location)
66
+ if (sourceLocation) invalidateLocation(sdk, sourceLocation)
67
+ sdk.invalidate(cacheKey({ id: 'resources/get' }, { id }))
68
+ return resource
69
+ }
70
+
71
+ export async function renameResource(sdk, id, name) {
72
+ const resource = await sdk.invoke(UpdateResourceName, { id, name })
73
+ invalidateLocation(sdk, resource.location)
74
+ sdk.invalidate(cacheKey({ id: 'resources/get' }, { id }))
75
+ return resource
76
+ }
77
+
78
+ export async function updateResourceAccess(sdk, params) {
79
+ const resource = await sdk.invoke(UpdateResourceAccess, params)
80
+ invalidateLocation(sdk, resource.location)
81
+ sdk.invalidate(cacheKey({ id: 'resources/get' }, { id: params.id }))
82
+ return resource
83
+ }
@@ -0,0 +1,18 @@
1
+ import { attachResourceMediaUrls } from './resources.attach-media-urls.js'
2
+
3
+ export function mediaContextFromRun({ payload, req }) {
4
+ return {
5
+ userId: payload?.userId ?? req?.userId,
6
+ workspaceId: payload?.workspaceId ?? req?.workspaceId,
7
+ }
8
+ }
9
+
10
+ export async function withResourceMedia(result, context) {
11
+ if (Array.isArray(result)) {
12
+ return Promise.all(result.map(r => attachResourceMediaUrls(r, context)))
13
+ }
14
+ if (result && typeof result === 'object' && result.id) {
15
+ return attachResourceMediaUrls(result, context)
16
+ }
17
+ return result
18
+ }
@@ -1,6 +1,6 @@
1
1
  import { StorageClient } from '@ossy/platform'
2
2
  import { Aggregate } from '@ossy/event-store'
3
- import { Workspace } from '@ossy/workspaces'
3
+ import { Workspace } from '@ossy/workspaces/server'
4
4
 
5
5
  /**
6
6
  * Adds `content.src` (and `content.sizes[*]` URLs) for file resources, respecting
@@ -75,6 +75,7 @@ export class ResourcesEvents {
75
75
  })
76
76
  }
77
77
 
78
+ // TODO: remove named version concept
78
79
  static NamedVersionUploaded({
79
80
  createdBy,
80
81
  namedVersion,
@@ -9,72 +9,70 @@ function workspaceHeaders(userToken, workspaceId) {
9
9
  }
10
10
  }
11
11
 
12
- describe('[/resources][POST]', () => {
12
+ describe('[resources/create]', () => {
13
13
 
14
- TestUtil.AssertAuthenticationNeeded({
15
- endpoint: '/resources',
16
- method: 'POST',
17
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
14
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
15
+ actionId: 'resources/create',
16
+ payload: { type: 'directory', location: '/', name: 'test' },
17
+ headers: { workspaceId: 'ws-test' },
18
18
  })
19
19
 
20
20
  })
21
21
 
22
- describe('[/resources][GET]', () => {
23
- TestUtil.AssertAuthenticationNeeded({
24
- endpoint: '/resources',
25
- method: 'GET',
26
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
22
+ describe('[resources/list]', () => {
23
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
24
+ actionId: 'resources/list',
25
+ headers: { workspaceId: 'ws-test' },
27
26
  })
28
27
  })
29
28
 
30
- describe('[/resources/:resourceId][GET]', () => {
31
- TestUtil.AssertAuthenticationNeeded({
32
- endpoint: '/resources/r1',
33
- method: 'GET',
34
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
29
+ describe('[resources/get]', () => {
30
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
31
+ actionId: 'resources/get',
32
+ payload: { resourceId: 'r1' },
33
+ headers: { workspaceId: 'ws-test' },
35
34
  })
36
35
  })
37
36
 
38
- describe('[/resources/:resourceId/name][PUT]', () => {
39
- TestUtil.AssertAuthenticationNeeded({
40
- endpoint: '/resources/r1/name',
41
- method: 'PUT',
42
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
37
+ describe('[resources/update-name]', () => {
38
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
39
+ actionId: 'resources/update-name',
40
+ payload: { resourceId: 'r1', name: 'x' },
41
+ headers: { workspaceId: 'ws-test' },
43
42
  })
44
43
  })
45
44
 
46
- describe('[/resources/:resourceId/location][PUT]', () => {
47
- TestUtil.AssertAuthenticationNeeded({
48
- endpoint: '/resources/r1/location',
49
- method: 'PUT',
50
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
45
+ describe('[resources/update-location]', () => {
46
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
47
+ actionId: 'resources/update-location',
48
+ payload: { resourceId: 'r1', target: '/other/' },
49
+ headers: { workspaceId: 'ws-test' },
51
50
  })
52
51
  })
53
52
 
54
- describe('[/resources/:resourceId/content][PUT]', () => {
55
- TestUtil.AssertAuthenticationNeeded({
56
- endpoint: '/resources/r1/content',
57
- method: 'PUT',
58
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
53
+ describe('[resources/update-content]', () => {
54
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
55
+ actionId: 'resources/update-content',
56
+ payload: { resourceId: 'r1', content: {} },
57
+ headers: { workspaceId: 'ws-test' },
59
58
  })
60
59
  })
61
60
 
62
- describe('[/resources/:resourceId][DELETE]', () => {
63
- TestUtil.AssertAuthenticationNeeded({
64
- endpoint: '/resources/r1',
65
- method: 'DELETE',
66
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
61
+ describe('[resources/delete]', () => {
62
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
63
+ actionId: 'resources/delete',
64
+ payload: { resourceId: 'r1' },
65
+ headers: { workspaceId: 'ws-test' },
67
66
  })
68
67
  })
69
68
 
70
69
  describe('E2E: template-backed resource (workspaceId header)', () => {
71
70
  it('create → update content → delete', async () => {
72
71
  const user = await TestUtil.GetAuthenticatedTestUser()
73
- const workspace = await TestUtil.MakeRequest({
74
- endpoint: '/workspaces',
75
- method: 'POST',
72
+ const workspace = await TestUtil.InvokeAction({
73
+ actionId: 'workspaces/create',
76
74
  headers: { 'Content-Type': 'application/json', Authorization: user.token },
77
- body: JSON.stringify({ name: casual.word }),
75
+ payload: { name: casual.word },
78
76
  }).then(r => r.json())
79
77
 
80
78
  const templateType = '@ossy/web/page'
@@ -89,11 +87,10 @@ describe('E2E: template-backed resource (workspaceId header)', () => {
89
87
  },
90
88
  }
91
89
 
92
- const createRes = await TestUtil.MakeRequest({
93
- endpoint: '/resources',
94
- method: 'POST',
90
+ const createRes = await TestUtil.InvokeAction({
91
+ actionId: 'resources/create',
95
92
  headers: workspaceHeaders(user.token, workspace.id),
96
- body: JSON.stringify(createBody),
93
+ payload: createBody,
97
94
  })
98
95
 
99
96
  expect(createRes.status).toEqual(200)
@@ -105,17 +102,17 @@ describe('E2E: template-backed resource (workspaceId header)', () => {
105
102
  body: '<p>initial</p>',
106
103
  })
107
104
 
108
- const updateRes = await TestUtil.MakeRequest({
109
- endpoint: `/resources/${created.id}/content`,
110
- method: 'PUT',
105
+ const updateRes = await TestUtil.InvokeAction({
106
+ actionId: 'resources/update-content',
111
107
  headers: workspaceHeaders(user.token, workspace.id),
112
- body: JSON.stringify({
108
+ payload: {
109
+ resourceId: created.id,
113
110
  content: {
114
111
  title: 'Updated',
115
112
  description: 'D2',
116
113
  body: '<p>revised</p>',
117
114
  },
118
- }),
115
+ },
119
116
  })
120
117
 
121
118
  expect(updateRes.status).toEqual(200)
@@ -126,18 +123,20 @@ describe('E2E: template-backed resource (workspaceId header)', () => {
126
123
  body: '<p>revised</p>',
127
124
  })
128
125
 
129
- const deleteRes = await TestUtil.MakeRequest({
130
- endpoint: `/resources/${created.id}`,
131
- method: 'DELETE',
126
+ const deleteRes = await TestUtil.InvokeAction({
127
+ actionId: 'resources/delete',
132
128
  headers: workspaceHeaders(user.token, workspace.id),
129
+ payload: { resourceId: created.id },
133
130
  })
134
131
 
135
- expect(deleteRes.status).toEqual(204)
132
+ expect(deleteRes.status).toEqual(200)
133
+ const deleteBody = await deleteRes.json()
134
+ expect(deleteBody).toEqual({ ok: true })
136
135
 
137
- const listRes = await TestUtil.MakeRequest({
138
- endpoint: '/resources?location=/',
139
- method: 'GET',
136
+ const listRes = await TestUtil.InvokeAction({
137
+ actionId: 'resources/list',
140
138
  headers: workspaceHeaders(user.token, workspace.id),
139
+ payload: { query: { location: '/' } },
141
140
  })
142
141
 
143
142
  expect(listRes.status).toEqual(200)
@@ -1,19 +1 @@
1
- import { ResourcesQueries } from './resources.queries.js'
2
-
3
- export const id = 'resources/search'
4
- export const access = 'workspace'
5
-
6
- export async function run({ payload, req }) {
7
- const query = { ...(payload ?? {}) }
8
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
9
- const user = payload?.user ?? req?.user
10
- let location = query.location
11
-
12
- if (location && !location.endsWith('/')) location += '/'
13
- if (location && !location.startsWith('/')) location = '/' + location
14
- if (location) query.location = location
15
-
16
- if (workspaceId) query.belongsTo = workspaceId
17
-
18
- return ResourcesQueries.GetResources(query, user)
19
- }
1
+ export const metadata = { id: 'resources/search', access: 'workspace' }
@@ -0,0 +1,20 @@
1
+ import { ResourcesQueries } from './resources.queries.js'
2
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
3
+
4
+ export const metadata = { id: 'resources/search' }
5
+
6
+ export async function run({ payload, req }) {
7
+ const query = { ...(payload ?? {}) }
8
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
9
+ const user = payload?.user ?? req?.user
10
+ let location = query.location
11
+
12
+ if (location && !location.endsWith('/')) location += '/'
13
+ if (location && !location.startsWith('/')) location = '/' + location
14
+ if (location) query.location = location
15
+
16
+ if (workspaceId) query.belongsTo = workspaceId
17
+
18
+ const resources = await ResourcesQueries.GetResources(query, user)
19
+ return withResourceMedia(resources, mediaContextFromRun({ payload, req }))
20
+ }
package/src/server.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './resource.aggregate.js'
2
+ export * from './resources.events.js'
3
+ export * from './resources.queries.js'
4
+ export { attachResourceMediaUrls } from './resources.attach-media-urls.js'
@@ -0,0 +1,48 @@
1
+ {
2
+ "resources/home.documentTitle": "Gratis resurser",
3
+ "resources.home.title": "Gratis resurser",
4
+ "resources.home.description": "Vi tror på att leverera värde i varje steg av din resa med oss. Därför har vi samlat gratis resurser. Oavsett om du vill förbättra dina kreativa projekt eller bara söker inspiration kan du utforska galleriet nedan. Vi hoppas att du hittar något användbart — hör av dig om du har frågor!",
5
+ "resources.form.nameRequired": "Namn krävs",
6
+ "resources.form.untitled": "Namnlös",
7
+ "resources.form.selectType": "Välj en resurstyp för att fortsätta.",
8
+ "resources.form.createFailed": "Skapandet misslyckades",
9
+ "resources.form.createResource": "Skapa resurs",
10
+ "resources/create.label": "Skapa resurs",
11
+ "resources/create.description": "Skapa en ny resurs i workspace",
12
+ "resources/list.label": "Lista resurser",
13
+ "resources/list.description": "Lista resurser på en plats i workspace",
14
+ "resources/get.label": "Hämta resurs",
15
+ "resources/get.description": "Hämta en enskild resurs via id",
16
+ "resources/search.label": "Sök resurser",
17
+ "resources/search.description": "Sök resurser i en workspace",
18
+ "resources/delete.label": "Ta bort resurs",
19
+ "resources/delete.description": "Ta bort en resurs från workspace",
20
+ "resources/update-name.label": "Uppdatera resursnamn",
21
+ "resources/update-name.description": "Byt namn på en resurs i workspace",
22
+ "resources/update-content.label": "Uppdatera resursinnehåll",
23
+ "resources/update-content.description": "Uppdatera innehållet i en resurs",
24
+ "resources/update-location.label": "Uppdatera resursplats",
25
+ "resources/update-location.description": "Flytta en resurs till en ny plats",
26
+ "resources/update-access.label": "Uppdatera resursåtkomst",
27
+ "resources/update-access.description": "Ändra åtkomstinställningar för en resurs",
28
+ "resources/upload-named-version.label": "Ladda upp namngiven version",
29
+ "resources/upload-named-version.description": "Ladda upp en namngiven version av en resursfil",
30
+ "resources/create-page-view.label": "Skapa sidvisning",
31
+ "resources/create-page-view.description": "Registrera en sidvisningshändelse för analys",
32
+ "resources/get-page-view-stats.label": "Hämta sidvisningsstatistik",
33
+ "resources/get-page-view-stats.description": "Hämta aggregerad statistik för sidvisningar",
34
+ "resources/create/generic.documentTitle": "Skapa resurs",
35
+ "create-directory.documentTitle": "Skapa mapp",
36
+ "create-document.documentTitle": "Skapa dokument",
37
+ "upload-media.documentTitle": "Ladda upp",
38
+ "@resource.documentTitle": "Resurs",
39
+ "resources.upload.title": "Ladda upp",
40
+ "resources.upload.location": "Plats: {location}",
41
+ "resources.createDirectory.title": "Skapa mapp",
42
+ "resources.createDocument.title": "Skapa dokument",
43
+ "resources.createDirectory.description": "Skapa en ny mapp för att organisera dina resurser. Den nya mappen skapas på platsen: {location}",
44
+ "resources.createDirectory.namePlaceholder": "Mappnamn",
45
+ "resources.createDirectory.submit": "Skapa mapp",
46
+ "resources.createDirectory.errorEmpty": "Mappnamn får inte vara tomt",
47
+ "resources.createDirectory.errorOssyPrefix": "Mappnamn får inte börja med @ossy"
48
+ }
@@ -1,20 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Resource, ResourcesEvents } from '@ossy/resources'
3
-
4
- export const id = 'resources/update-access'
5
- export const access = 'workspace'
6
-
7
- export async function run({ payload, req }) {
8
- const createdBy = payload?.userId ?? req?.userId
9
- const resourceId = payload?.resourceId ?? req?.params?.resourceId
10
- const resourceAccess = payload?.access
11
-
12
- if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
13
- if (!['public', 'restricted'].includes(resourceAccess)) {
14
- throw Object.assign(new Error(`Invalid access value: ${resourceAccess}`), { status: 400 })
15
- }
16
-
17
- return Aggregate.Of(Resource, resourceId)
18
- .then(Aggregate.Add(ResourcesEvents.AccessUpdated({ createdBy, access: resourceAccess })))
19
- .then(Aggregate.View())
20
- }
1
+ export const metadata = { id: 'resources/update-access', access: 'workspace' }
@@ -0,0 +1,22 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Resource } from './resource.aggregate.js'
3
+ import { ResourcesEvents } from './resources.events.js'
4
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
5
+
6
+ export const metadata = { id: 'resources/update-access' }
7
+
8
+ export async function run({ payload, req }) {
9
+ const createdBy = payload?.userId ?? req?.userId
10
+ const resourceId = payload?.resourceId ?? req?.params?.resourceId
11
+ const resourceAccess = payload?.access
12
+
13
+ if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
14
+ if (!['public', 'restricted'].includes(resourceAccess)) {
15
+ throw Object.assign(new Error(`Invalid access value: ${resourceAccess}`), { status: 400 })
16
+ }
17
+
18
+ const resource = await Aggregate.Of(Resource, resourceId)
19
+ .then(Aggregate.Add(ResourcesEvents.AccessUpdated({ createdBy, access: resourceAccess })))
20
+ .then(Aggregate.View())
21
+ return withResourceMedia(resource, mediaContextFromRun({ payload, req }))
22
+ }
@@ -1,39 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Resource, ResourcesEvents } from '@ossy/resources'
3
- import { Workspace } from '@ossy/workspaces'
4
- import { getSystemResourceTemplates, normalizeAndValidateDocumentContent } from '@ossy/platform'
5
-
6
- export const id = 'resources/update-content'
7
- export const access = 'workspace'
8
-
9
- export async function run({ payload, req }) {
10
- const createdBy = payload?.userId ?? req?.userId
11
- const resourceId = payload?.resourceId ?? req?.params?.resourceId
12
- const content = payload?.content
13
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
14
-
15
- if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
16
-
17
- const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
18
-
19
- return Aggregate.Of(Resource, resourceId)
20
- .then(aggregate => {
21
- const resource = Aggregate.View()(aggregate)
22
- const systemTemplates = getSystemResourceTemplates()
23
- const allTemplates = [
24
- ...systemTemplates,
25
- ...(workspace.resourceTemplates || []).filter(t => !systemTemplates.find(s => s.id === t.id)),
26
- ]
27
- const template = allTemplates.find(t => t.id === resource.type)
28
- let finalContent = content
29
- if (template) {
30
- const normalized = normalizeAndValidateDocumentContent(content, template)
31
- if (!normalized.ok) {
32
- throw Object.assign(new Error(normalized.message), { status: 400, type: normalized.code })
33
- }
34
- finalContent = normalized.content
35
- }
36
- return Aggregate.Add(ResourcesEvents.ContentUpdated({ createdBy, content: finalContent }))(aggregate)
37
- })
38
- .then(Aggregate.View())
39
- }
1
+ export const metadata = { id: 'resources/update-content', access: 'workspace' }
@@ -0,0 +1,41 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Resource } from './resource.aggregate.js'
3
+ import { ResourcesEvents } from './resources.events.js'
4
+ import { Workspace } from '@ossy/workspaces/server'
5
+ import { getSystemResourceTemplates, normalizeAndValidateDocumentContent } from '@ossy/platform'
6
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
7
+
8
+ export const metadata = { id: 'resources/update-content' }
9
+
10
+ export async function run({ payload, req }) {
11
+ const createdBy = payload?.userId ?? req?.userId
12
+ const resourceId = payload?.resourceId ?? req?.params?.resourceId
13
+ const content = payload?.content
14
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
15
+
16
+ if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
17
+
18
+ const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
19
+
20
+ return Aggregate.Of(Resource, resourceId)
21
+ .then(aggregate => {
22
+ const resource = Aggregate.View()(aggregate)
23
+ const systemTemplates = getSystemResourceTemplates()
24
+ const allTemplates = [
25
+ ...systemTemplates,
26
+ ...(workspace.resourceTemplates || []).filter(t => !systemTemplates.find(s => s.id === t.id)),
27
+ ]
28
+ const template = allTemplates.find(t => t.id === resource.type)
29
+ let finalContent = content
30
+ if (template) {
31
+ const normalized = normalizeAndValidateDocumentContent(content, template)
32
+ if (!normalized.ok) {
33
+ throw Object.assign(new Error(normalized.message), { status: 400, type: normalized.code })
34
+ }
35
+ finalContent = normalized.content
36
+ }
37
+ return Aggregate.Add(ResourcesEvents.ContentUpdated({ createdBy, content: finalContent }))(aggregate)
38
+ })
39
+ .then(Aggregate.View())
40
+ .then(resource => withResourceMedia(resource, mediaContextFromRun({ payload, req })))
41
+ }