@raketa-cloud/admin 1.7.4 → 1.7.5
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.
- package/package.json +1 -1
- package/src/HonoAdapter.js +7 -3
- package/src/cloud/Articles.js +10 -3
- package/src/cloud/Events.js +9 -2
- package/src/cloud/Pages.js +11 -1
- package/src/cloud/documents/articlesDocuments.js +4 -2
- package/src/cloud/documents/eventsDocuments.js +4 -2
- package/src/cloud/documents/pagesDocuments.js +4 -2
- package/src/handlers/articles/ArticlesHandler.js +10 -4
- package/src/handlers/articles/ExportsHandler.js +35 -0
- package/src/handlers/events/EventsHandler.js +10 -4
- package/src/handlers/events/ExportsHandler.js +35 -0
- package/src/handlers/pages/ExportsHandler.js +35 -0
- package/src/handlers/pages/PagesHandler.js +10 -4
- package/src/helpers/CSVExportHelper.js +103 -0
- package/src/routeArticlesModule.js +13 -0
- package/src/routeEventsModule.js +13 -0
- package/src/routePagesModule.js +13 -0
- package/src/tests/handlers/articles/ArticlesHandler.test/index.test.js +18 -2
- package/src/tests/handlers/articles/ExportsHandler.test/index.test.js +217 -0
- package/src/tests/handlers/events/EventsHandler.test/index.test.js +17 -1
- package/src/tests/handlers/events/ExportsHandler.test/index.test.js +212 -0
- package/src/tests/handlers/pages/ExportsHandler.test/index.test.js +212 -0
- package/src/tests/handlers/pages/PagesHandler.test/index.test.js +17 -1
- package/src/tests/helpers/CSVExportHelper.test.js +364 -0
- package/src/views/articles/_shared/_export_button.ejs +9 -0
- package/src/views/articles/index.ejs +37 -1
- package/src/views/events/_shared/_export_button.ejs +9 -0
- package/src/views/events/index.ejs +37 -1
- package/src/views/layouts/application.ejs +2 -0
- package/src/views/pages/_shared/_export_button.ejs +9 -0
- package/src/views/pages/index.ejs +37 -1
package/package.json
CHANGED
package/src/HonoAdapter.js
CHANGED
|
@@ -364,6 +364,7 @@ export default class HonoAdapter {
|
|
|
364
364
|
if (result.headers) this.applyHeaders(result.headers, routerCtx);
|
|
365
365
|
if (result.redirect) return routerCtx.redirect(result.redirect);
|
|
366
366
|
if (result.json) return routerCtx.json(result.json);
|
|
367
|
+
if (result.text) return routerCtx.text(result.text);
|
|
367
368
|
|
|
368
369
|
// Process global after filters
|
|
369
370
|
for (const { pattern, filter } of this.globalAfterFilters) {
|
|
@@ -373,6 +374,7 @@ export default class HonoAdapter {
|
|
|
373
374
|
if (afterResult.redirect)
|
|
374
375
|
return routerCtx.redirect(afterResult.redirect);
|
|
375
376
|
if (afterResult.json) return routerCtx.json(afterResult.json);
|
|
377
|
+
if (afterResult.text) return routerCtx.text(afterResult.text);
|
|
376
378
|
|
|
377
379
|
Object.assign(
|
|
378
380
|
assigns.afterFilterAssigns,
|
|
@@ -437,9 +439,11 @@ export default class HonoAdapter {
|
|
|
437
439
|
|
|
438
440
|
formatQuery(queries) {
|
|
439
441
|
const result = {};
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
442
|
+
if (queries && typeof queries === "object") {
|
|
443
|
+
Object.entries(queries).forEach(([key, value]) => {
|
|
444
|
+
result[key] = value.length === 1 ? value[0] : value;
|
|
445
|
+
});
|
|
446
|
+
}
|
|
443
447
|
return result;
|
|
444
448
|
}
|
|
445
449
|
}
|
package/src/cloud/Articles.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dateToUTCISO8601String, toDateInput } from "../utils";
|
|
1
|
+
import { dateToUTCISO8601String, toUTCISO8601String, toDateInput } from "../utils";
|
|
2
2
|
import RaketaClient from "./RaketaClient";
|
|
3
3
|
import {
|
|
4
4
|
ArticleByIdDocument,
|
|
@@ -71,6 +71,13 @@ export default class Articles extends RaketaClient {
|
|
|
71
71
|
async list(query, locale, articleTypeSlug) {
|
|
72
72
|
const vars = this.buildListInput(query, locale);
|
|
73
73
|
|
|
74
|
+
if (vars.input.filters.last_updated_from) {
|
|
75
|
+
vars.input.filters.last_updated_from = toUTCISO8601String(vars.input.filters.last_updated_from);
|
|
76
|
+
}
|
|
77
|
+
if (vars.input.filters.last_updated_to) {
|
|
78
|
+
vars.input.filters.last_updated_to = toUTCISO8601String(vars.input.filters.last_updated_to);
|
|
79
|
+
}
|
|
80
|
+
|
|
74
81
|
const { articles: items } = await this.query(ArticlesDocument, {
|
|
75
82
|
input: {
|
|
76
83
|
...vars.input,
|
|
@@ -150,7 +157,7 @@ export default class Articles extends RaketaClient {
|
|
|
150
157
|
paramsCopy.settings = JSON.stringify(paramsCopy.settings);
|
|
151
158
|
|
|
152
159
|
const BatchTranslateDocument = buildBatchTranslateArticleDocument(
|
|
153
|
-
locales.length
|
|
160
|
+
locales.length,
|
|
154
161
|
);
|
|
155
162
|
|
|
156
163
|
const variables = {};
|
|
@@ -204,7 +211,7 @@ export default class Articles extends RaketaClient {
|
|
|
204
211
|
ArticleChangePreviewDocument,
|
|
205
212
|
{
|
|
206
213
|
input,
|
|
207
|
-
}
|
|
214
|
+
},
|
|
208
215
|
);
|
|
209
216
|
|
|
210
217
|
return articleChangePreview;
|
package/src/cloud/Events.js
CHANGED
|
@@ -86,6 +86,13 @@ export default class Events extends RaketaClient {
|
|
|
86
86
|
async list(query, locale, eventTypeSlug) {
|
|
87
87
|
const vars = this.buildListInput(query, locale);
|
|
88
88
|
|
|
89
|
+
if (vars.input.filters.last_updated_from) {
|
|
90
|
+
vars.input.filters.last_updated_from = toUTCISO8601String(vars.input.filters.last_updated_from);
|
|
91
|
+
}
|
|
92
|
+
if (vars.input.filters.last_updated_to) {
|
|
93
|
+
vars.input.filters.last_updated_to = toUTCISO8601String(vars.input.filters.last_updated_to);
|
|
94
|
+
}
|
|
95
|
+
|
|
89
96
|
const { events: items } = await this.query(EventsDocument, {
|
|
90
97
|
input: {
|
|
91
98
|
...vars.input,
|
|
@@ -165,7 +172,7 @@ export default class Events extends RaketaClient {
|
|
|
165
172
|
paramsCopy.settings = JSON.stringify(paramsCopy.settings);
|
|
166
173
|
|
|
167
174
|
const BatchTranslateDocument = buildBatchTranslateEventDocument(
|
|
168
|
-
locales.length
|
|
175
|
+
locales.length,
|
|
169
176
|
);
|
|
170
177
|
|
|
171
178
|
const variables = {};
|
|
@@ -219,7 +226,7 @@ export default class Events extends RaketaClient {
|
|
|
219
226
|
EventChangePreviewDocument,
|
|
220
227
|
{
|
|
221
228
|
input,
|
|
222
|
-
}
|
|
229
|
+
},
|
|
223
230
|
);
|
|
224
231
|
|
|
225
232
|
return eventChangePreview;
|
package/src/cloud/Pages.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toUTCISO8601String } from "../utils";
|
|
1
2
|
import RaketaClient from "./RaketaClient";
|
|
2
3
|
import {
|
|
3
4
|
ChangePageSlugDocument,
|
|
@@ -61,6 +62,15 @@ export default class Pages extends RaketaClient {
|
|
|
61
62
|
async list(query, locale, pageTypeSlug) {
|
|
62
63
|
const vars = this.buildListInput(query, locale);
|
|
63
64
|
|
|
65
|
+
if (vars.input.filters.last_updated_from) {
|
|
66
|
+
vars.input.filters.last_updated_from = toUTCISO8601String(vars.input.filters.last_updated_from);
|
|
67
|
+
}
|
|
68
|
+
if (vars.input.filters.last_updated_to) {
|
|
69
|
+
vars.input.filters.last_updated_to = toUTCISO8601String(vars.input.filters.last_updated_to);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(vars.input.filters)
|
|
73
|
+
|
|
64
74
|
const { pages: items } = await this.query(PagesDocument, {
|
|
65
75
|
input: {
|
|
66
76
|
...vars.input,
|
|
@@ -141,7 +151,7 @@ export default class Pages extends RaketaClient {
|
|
|
141
151
|
copyParams.settings = JSON.stringify(copyParams.settings);
|
|
142
152
|
|
|
143
153
|
const BatchTranslateDocument = buildBatchTranslatePageDocument(
|
|
144
|
-
locales.length
|
|
154
|
+
locales.length,
|
|
145
155
|
);
|
|
146
156
|
|
|
147
157
|
const variables = {};
|
|
@@ -18,6 +18,8 @@ function minimalArticleFragment() {
|
|
|
18
18
|
title
|
|
19
19
|
visibility
|
|
20
20
|
publishAt
|
|
21
|
+
insertedAt
|
|
22
|
+
updatedAt
|
|
21
23
|
}
|
|
22
24
|
`;
|
|
23
25
|
}
|
|
@@ -196,7 +198,7 @@ const buildBatchTranslateArticleDocument = (count) => gql`
|
|
|
196
198
|
article ${articleFragment()}
|
|
197
199
|
errors ${errorsFragment()}
|
|
198
200
|
}
|
|
199
|
-
|
|
201
|
+
`,
|
|
200
202
|
)
|
|
201
203
|
.join("\n")}
|
|
202
204
|
}
|
|
@@ -240,7 +242,7 @@ const buildBatchStartArticleRevisionDocument = (count) => gql`
|
|
|
240
242
|
errors ${errorsFragment()}
|
|
241
243
|
articleErrors ${errorsFragment()}
|
|
242
244
|
}
|
|
243
|
-
|
|
245
|
+
`,
|
|
244
246
|
)
|
|
245
247
|
.join("\n")}
|
|
246
248
|
}
|
|
@@ -20,6 +20,8 @@ function minimalEventFragment() {
|
|
|
20
20
|
publishAt
|
|
21
21
|
startsAt
|
|
22
22
|
endsAt
|
|
23
|
+
insertedAt
|
|
24
|
+
updatedAt
|
|
23
25
|
}
|
|
24
26
|
`;
|
|
25
27
|
}
|
|
@@ -204,7 +206,7 @@ const buildBatchTranslateEventDocument = (count) => gql`
|
|
|
204
206
|
event ${eventFragment()}
|
|
205
207
|
errors ${errorsFragment()}
|
|
206
208
|
}
|
|
207
|
-
|
|
209
|
+
`,
|
|
208
210
|
)
|
|
209
211
|
.join("\n")}
|
|
210
212
|
}
|
|
@@ -232,7 +234,7 @@ const buildBatchStartEventRevisionDocument = (count) => gql`
|
|
|
232
234
|
errors ${errorsFragment()}
|
|
233
235
|
eventErrors ${errorsFragment()}
|
|
234
236
|
}
|
|
235
|
-
|
|
237
|
+
`,
|
|
236
238
|
)
|
|
237
239
|
.join("\n")}
|
|
238
240
|
}
|
|
@@ -17,6 +17,8 @@ function minimalPageFragment() {
|
|
|
17
17
|
slug
|
|
18
18
|
title
|
|
19
19
|
visibility
|
|
20
|
+
insertedAt
|
|
21
|
+
updatedAt
|
|
20
22
|
}
|
|
21
23
|
`;
|
|
22
24
|
}
|
|
@@ -192,7 +194,7 @@ const buildBatchTranslatePageDocument = (count) => gql`
|
|
|
192
194
|
page ${pageFragment()}
|
|
193
195
|
errors ${errorsFragment()}
|
|
194
196
|
}
|
|
195
|
-
|
|
197
|
+
`,
|
|
196
198
|
)
|
|
197
199
|
.join("\n")}
|
|
198
200
|
}
|
|
@@ -236,7 +238,7 @@ const buildBatchStartPageRevisionDocument = (count) => gql`
|
|
|
236
238
|
errors ${errorsFragment()}
|
|
237
239
|
pageErrors ${errorsFragment()}
|
|
238
240
|
}
|
|
239
|
-
|
|
241
|
+
`,
|
|
240
242
|
)
|
|
241
243
|
.join("\n")}
|
|
242
244
|
}
|
|
@@ -4,13 +4,19 @@ const index = async (ctx) => {
|
|
|
4
4
|
const items = await ctx.client.articles.list(
|
|
5
5
|
ctx.query,
|
|
6
6
|
ctx.locale,
|
|
7
|
-
ctx.resourceConfig.articleType
|
|
7
|
+
ctx.resourceConfig.articleType,
|
|
8
8
|
);
|
|
9
9
|
const tags = await ctx.client.tags.all(ctx.locale);
|
|
10
|
+
const languages = ctx.client.articles.getConfig().i18n.locales;
|
|
11
|
+
const translations = Object.keys(languages).map((key) => ({
|
|
12
|
+
name: languages[key].name,
|
|
13
|
+
code: key,
|
|
14
|
+
}));
|
|
10
15
|
|
|
11
16
|
return {
|
|
12
17
|
assigns: {
|
|
13
18
|
tags: tags.list,
|
|
19
|
+
translations,
|
|
14
20
|
items,
|
|
15
21
|
visibilityOptions: ctx.client.articles.visibilityOptions(),
|
|
16
22
|
},
|
|
@@ -36,7 +42,7 @@ const show = async (ctx) => {
|
|
|
36
42
|
],
|
|
37
43
|
},
|
|
38
44
|
id,
|
|
39
|
-
ctx.locale
|
|
45
|
+
ctx.locale,
|
|
40
46
|
);
|
|
41
47
|
|
|
42
48
|
return {
|
|
@@ -81,13 +87,13 @@ const create = async (ctx) => {
|
|
|
81
87
|
|
|
82
88
|
const resource = ctx.client.articles.deepMerge(
|
|
83
89
|
ctx.client.articles.defaults(),
|
|
84
|
-
ctx.client.articles.formatDates(params)
|
|
90
|
+
ctx.client.articles.formatDates(params),
|
|
85
91
|
);
|
|
86
92
|
|
|
87
93
|
const { errors } = await ctx.client.articles.create(
|
|
88
94
|
resource,
|
|
89
95
|
ctx.locale,
|
|
90
|
-
ctx.resourceConfig.articleType
|
|
96
|
+
ctx.resourceConfig.articleType,
|
|
91
97
|
);
|
|
92
98
|
|
|
93
99
|
if (errors) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import CSVExportHelper from "../../helpers/CSVExportHelper";
|
|
2
|
+
|
|
3
|
+
const exportCSV = async (ctx) => {
|
|
4
|
+
const { query, client, resourceConfig, remoteConfig } = ctx;
|
|
5
|
+
const articleType = resourceConfig.articleType;
|
|
6
|
+
const locale = query.locale || "en";
|
|
7
|
+
|
|
8
|
+
const articles = await client.articles.listAll(query, locale, articleType);
|
|
9
|
+
|
|
10
|
+
const fieldMapping = CSVExportHelper.standardFieldMapping(
|
|
11
|
+
remoteConfig,
|
|
12
|
+
locale,
|
|
13
|
+
articleType,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const headers = [...CSVExportHelper.standardHeaders];
|
|
17
|
+
|
|
18
|
+
const csv = CSVExportHelper.formatDataAsCSV(
|
|
19
|
+
articles.list,
|
|
20
|
+
headers,
|
|
21
|
+
fieldMapping,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
text: csv,
|
|
26
|
+
headers: {
|
|
27
|
+
"Content-Type": "text/csv",
|
|
28
|
+
"Content-Disposition": `attachment; filename="${CSVExportHelper.generateExportFilename(articleType)}"`,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default {
|
|
34
|
+
index: exportCSV,
|
|
35
|
+
};
|
|
@@ -4,13 +4,19 @@ const index = async (ctx) => {
|
|
|
4
4
|
const items = await ctx.client.events.list(
|
|
5
5
|
ctx.query,
|
|
6
6
|
ctx.locale,
|
|
7
|
-
ctx.resourceConfig.eventType
|
|
7
|
+
ctx.resourceConfig.eventType,
|
|
8
8
|
);
|
|
9
9
|
const tags = await ctx.client.tags.all(ctx.locale);
|
|
10
|
+
const languages = ctx.client.events.getConfig().i18n.locales;
|
|
11
|
+
const translations = Object.keys(languages).map((key) => ({
|
|
12
|
+
name: languages[key].name,
|
|
13
|
+
code: key,
|
|
14
|
+
}));
|
|
10
15
|
|
|
11
16
|
return {
|
|
12
17
|
assigns: {
|
|
13
18
|
tags: tags.list,
|
|
19
|
+
translations,
|
|
14
20
|
items,
|
|
15
21
|
visibilityOptions: ctx.client.events.visibilityOptions(),
|
|
16
22
|
},
|
|
@@ -36,7 +42,7 @@ const show = async (ctx) => {
|
|
|
36
42
|
],
|
|
37
43
|
},
|
|
38
44
|
id,
|
|
39
|
-
ctx.locale
|
|
45
|
+
ctx.locale,
|
|
40
46
|
);
|
|
41
47
|
|
|
42
48
|
return {
|
|
@@ -81,13 +87,13 @@ const create = async (ctx) => {
|
|
|
81
87
|
|
|
82
88
|
const resource = ctx.client.events.deepMerge(
|
|
83
89
|
ctx.client.events.defaults(),
|
|
84
|
-
ctx.client.events.formatDates(params)
|
|
90
|
+
ctx.client.events.formatDates(params),
|
|
85
91
|
);
|
|
86
92
|
|
|
87
93
|
const { errors } = await ctx.client.events.create(
|
|
88
94
|
resource,
|
|
89
95
|
ctx.locale,
|
|
90
|
-
ctx.resourceConfig.eventType
|
|
96
|
+
ctx.resourceConfig.eventType,
|
|
91
97
|
);
|
|
92
98
|
|
|
93
99
|
if (errors) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import CSVExportHelper from "../../helpers/CSVExportHelper";
|
|
2
|
+
|
|
3
|
+
const exportCSV = async (ctx) => {
|
|
4
|
+
const { query, client, resourceConfig, remoteConfig } = ctx;
|
|
5
|
+
const eventType = resourceConfig.eventType;
|
|
6
|
+
const locale = query.locale || "en";
|
|
7
|
+
|
|
8
|
+
const events = await client.events.listAll(query, locale, eventType);
|
|
9
|
+
|
|
10
|
+
const fieldMapping = CSVExportHelper.standardFieldMapping(
|
|
11
|
+
remoteConfig,
|
|
12
|
+
locale,
|
|
13
|
+
eventType,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const headers = [...CSVExportHelper.standardHeaders];
|
|
17
|
+
|
|
18
|
+
const csv = CSVExportHelper.formatDataAsCSV(
|
|
19
|
+
events.list,
|
|
20
|
+
headers,
|
|
21
|
+
fieldMapping,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
text: csv,
|
|
26
|
+
headers: {
|
|
27
|
+
"Content-Type": "text/csv",
|
|
28
|
+
"Content-Disposition": `attachment; filename="${CSVExportHelper.generateExportFilename(eventType)}"`,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default {
|
|
34
|
+
index: exportCSV,
|
|
35
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import CSVExportHelper from "../../helpers/CSVExportHelper";
|
|
2
|
+
|
|
3
|
+
const exportCSV = async (ctx) => {
|
|
4
|
+
const { query, client, resourceConfig, remoteConfig } = ctx;
|
|
5
|
+
const pageType = resourceConfig.pageType;
|
|
6
|
+
const locale = query.locale || "en";
|
|
7
|
+
|
|
8
|
+
const pages = await client.pages.listAll(query, locale, pageType);
|
|
9
|
+
|
|
10
|
+
const fieldMapping = CSVExportHelper.standardFieldMapping(
|
|
11
|
+
remoteConfig,
|
|
12
|
+
locale,
|
|
13
|
+
pageType,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const headers = [...CSVExportHelper.standardHeaders];
|
|
17
|
+
|
|
18
|
+
const csv = CSVExportHelper.formatDataAsCSV(
|
|
19
|
+
pages.list,
|
|
20
|
+
headers,
|
|
21
|
+
fieldMapping,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
text: csv,
|
|
26
|
+
headers: {
|
|
27
|
+
"Content-Type": "text/csv",
|
|
28
|
+
"Content-Disposition": `attachment; filename="${CSVExportHelper.generateExportFilename(pageType)}"`,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default {
|
|
34
|
+
index: exportCSV,
|
|
35
|
+
};
|
|
@@ -2,14 +2,20 @@ const index = async (ctx) => {
|
|
|
2
2
|
const items = await ctx.client.pages.list(
|
|
3
3
|
ctx.query,
|
|
4
4
|
ctx.locale,
|
|
5
|
-
ctx.resourceConfig.pageType
|
|
5
|
+
ctx.resourceConfig.pageType,
|
|
6
6
|
);
|
|
7
7
|
|
|
8
8
|
const tags = await ctx.client.tags.all(ctx.locale);
|
|
9
|
+
const languages = ctx.client.pages.getConfig().i18n.locales;
|
|
10
|
+
const translations = Object.keys(languages).map((key) => ({
|
|
11
|
+
name: languages[key].name,
|
|
12
|
+
code: key,
|
|
13
|
+
}));
|
|
9
14
|
|
|
10
15
|
return {
|
|
11
16
|
assigns: {
|
|
12
17
|
tags: tags.list,
|
|
18
|
+
translations,
|
|
13
19
|
items,
|
|
14
20
|
visibilityOptions: ctx.client.pages.visibilityOptions(),
|
|
15
21
|
},
|
|
@@ -35,7 +41,7 @@ const show = async (ctx) => {
|
|
|
35
41
|
],
|
|
36
42
|
},
|
|
37
43
|
id,
|
|
38
|
-
ctx.locale
|
|
44
|
+
ctx.locale,
|
|
39
45
|
);
|
|
40
46
|
|
|
41
47
|
return {
|
|
@@ -79,13 +85,13 @@ const create = async (ctx) => {
|
|
|
79
85
|
const params = { ...ctx.resourceParams.resource };
|
|
80
86
|
const resource = ctx.client.pages.deepMerge(
|
|
81
87
|
ctx.client.pages.defaults(),
|
|
82
|
-
params
|
|
88
|
+
params,
|
|
83
89
|
);
|
|
84
90
|
|
|
85
91
|
const { errors } = await ctx.client.pages.create(
|
|
86
92
|
resource,
|
|
87
93
|
ctx.locale,
|
|
88
|
-
ctx.resourceConfig.pageType
|
|
94
|
+
ctx.resourceConfig.pageType,
|
|
89
95
|
);
|
|
90
96
|
|
|
91
97
|
if (errors) {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
function escapeCSVValue(value) {
|
|
2
|
+
if (value === null || value === undefined) {
|
|
3
|
+
return "";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const stringValue = String(value);
|
|
7
|
+
|
|
8
|
+
if (
|
|
9
|
+
stringValue.includes(",") ||
|
|
10
|
+
stringValue.includes('"') ||
|
|
11
|
+
stringValue.includes("\n") ||
|
|
12
|
+
stringValue.includes("\r")
|
|
13
|
+
) {
|
|
14
|
+
return `"${stringValue.replace(/"/g, '""')}"`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return stringValue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function formatDataAsCSV(data, headers, fieldMapping) {
|
|
21
|
+
const headerRow = headers.join(",");
|
|
22
|
+
|
|
23
|
+
const rows = data.map((item) => {
|
|
24
|
+
return headers
|
|
25
|
+
.map((header) => {
|
|
26
|
+
const field = fieldMapping[header];
|
|
27
|
+
let value = "";
|
|
28
|
+
|
|
29
|
+
if (typeof field === "function") {
|
|
30
|
+
value = field(item);
|
|
31
|
+
} else if (field.includes(".")) {
|
|
32
|
+
const props = field.split(".");
|
|
33
|
+
let nestedValue = item;
|
|
34
|
+
|
|
35
|
+
for (const prop of props) {
|
|
36
|
+
if (nestedValue && nestedValue[prop] !== undefined) {
|
|
37
|
+
nestedValue = nestedValue[prop];
|
|
38
|
+
} else {
|
|
39
|
+
nestedValue = "";
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
value = nestedValue;
|
|
45
|
+
} else {
|
|
46
|
+
value = item[field] !== undefined ? item[field] : "";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return escapeCSVValue(value);
|
|
50
|
+
})
|
|
51
|
+
.join(",");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return [headerRow, ...rows].join("\r\n");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function generateExportFilename(contentType) {
|
|
58
|
+
const date = new Date();
|
|
59
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
60
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
61
|
+
const year = String(date.getFullYear()).slice(2);
|
|
62
|
+
|
|
63
|
+
return `${day}${month}${year}_${contentType}_export.csv`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const extractResourceType = (resource, type) => {
|
|
67
|
+
if (resource.pageType) return resource.pageType;
|
|
68
|
+
if (resource.articleType) return resource.articleType;
|
|
69
|
+
if (resource.eventType) return resource.eventType;
|
|
70
|
+
if (resource.resourceType) return resource.resourceType;
|
|
71
|
+
if (resource.type) return resource.type;
|
|
72
|
+
return type;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const standardFieldMapping = (remoteConfig, locale, type) => ({
|
|
76
|
+
id: "id",
|
|
77
|
+
type: (item) => extractResourceType(item, type),
|
|
78
|
+
locale: (item) => (item.locale ? item.locale : locale),
|
|
79
|
+
title: "title",
|
|
80
|
+
URL: (item) => remoteConfig.resourceUrl(item, type, locale),
|
|
81
|
+
visibility: "visibility",
|
|
82
|
+
created_at: "insertedAt",
|
|
83
|
+
updated_at: "updatedAt",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const standardHeaders = [
|
|
87
|
+
"id",
|
|
88
|
+
"type",
|
|
89
|
+
"locale",
|
|
90
|
+
"title",
|
|
91
|
+
"URL",
|
|
92
|
+
"visibility",
|
|
93
|
+
"created_at",
|
|
94
|
+
"updated_at",
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
export default {
|
|
98
|
+
escapeCSVValue,
|
|
99
|
+
formatDataAsCSV,
|
|
100
|
+
generateExportFilename,
|
|
101
|
+
standardFieldMapping,
|
|
102
|
+
standardHeaders,
|
|
103
|
+
};
|
|
@@ -15,6 +15,7 @@ import ArticlesHandler from "./handlers/articles/ArticlesHandler";
|
|
|
15
15
|
import ChangeHandler from "./handlers/articles/ChangeHandler";
|
|
16
16
|
import ClonesHandler from "./handlers/articles/ClonesHandler";
|
|
17
17
|
import DeletesHandler from "./handlers/articles/DeletesHandler";
|
|
18
|
+
import ExportsHandler from "./handlers/articles/ExportsHandler";
|
|
18
19
|
import HistoryHandler from "./handlers/articles/HistoryHandler";
|
|
19
20
|
import TranslationsHandler from "./handlers/articles/TranslationsHandler";
|
|
20
21
|
import URLsHandler from "./handlers/articles/URLsHandler";
|
|
@@ -35,6 +36,18 @@ function routeArticlesModule(
|
|
|
35
36
|
articleTypePlural,
|
|
36
37
|
};
|
|
37
38
|
|
|
39
|
+
const exportsMapping = [
|
|
40
|
+
[
|
|
41
|
+
"GET",
|
|
42
|
+
`/${baseResource}/exports`,
|
|
43
|
+
"exports",
|
|
44
|
+
"articles/exports",
|
|
45
|
+
"index",
|
|
46
|
+
ExportsHandler.index,
|
|
47
|
+
],
|
|
48
|
+
];
|
|
49
|
+
adapter.routeCollection(exportsMapping, resourceConfig);
|
|
50
|
+
|
|
38
51
|
routeCollection(adapter, baseResource, ArticlesHandler, {
|
|
39
52
|
...resourceConfig,
|
|
40
53
|
viewsFallback: "articles",
|
package/src/routeEventsModule.js
CHANGED
|
@@ -15,6 +15,7 @@ import ClonesHandler from "./handlers/events/ClonesHandler";
|
|
|
15
15
|
import DeletesHandler from "./handlers/events/DeletesHandler";
|
|
16
16
|
import EventsAPIHandler from "./handlers/events/EventsAPIHandler";
|
|
17
17
|
import EventsHandler from "./handlers/events/EventsHandler";
|
|
18
|
+
import ExportsHandler from "./handlers/events/ExportsHandler";
|
|
18
19
|
import HistoryHandler from "./handlers/events/HistoryHandler";
|
|
19
20
|
import TranslationsHandler from "./handlers/events/TranslationsHandler";
|
|
20
21
|
import URLsHandler from "./handlers/events/URLsHandler";
|
|
@@ -35,6 +36,18 @@ function routeEventsModule(
|
|
|
35
36
|
eventTypePlural,
|
|
36
37
|
};
|
|
37
38
|
|
|
39
|
+
const exportsMapping = [
|
|
40
|
+
[
|
|
41
|
+
"GET",
|
|
42
|
+
`/${baseResource}/exports`,
|
|
43
|
+
"exports",
|
|
44
|
+
"events/exports",
|
|
45
|
+
"index",
|
|
46
|
+
ExportsHandler.index,
|
|
47
|
+
],
|
|
48
|
+
];
|
|
49
|
+
adapter.routeCollection(exportsMapping, resourceConfig);
|
|
50
|
+
|
|
38
51
|
routeCollection(adapter, baseResource, EventsHandler, {
|
|
39
52
|
...resourceConfig,
|
|
40
53
|
viewsFallback: "events",
|
package/src/routePagesModule.js
CHANGED
|
@@ -13,6 +13,7 @@ import WidgetHandler from "./handlers/page_revisions/WidgetHandler";
|
|
|
13
13
|
import ChangeHandler from "./handlers/pages/ChangeHandler";
|
|
14
14
|
import ClonesHandler from "./handlers/pages/ClonesHandler";
|
|
15
15
|
import DeletesHandler from "./handlers/pages/DeletesHandler";
|
|
16
|
+
import ExportsHandler from "./handlers/pages/ExportsHandler";
|
|
16
17
|
import HistoryHandler from "./handlers/pages/HistoryHandler";
|
|
17
18
|
import PagesAPIHandler from "./handlers/pages/PagesAPIHandler";
|
|
18
19
|
import PagesHandler from "./handlers/pages/PagesHandler";
|
|
@@ -35,6 +36,18 @@ function routePagesModule(
|
|
|
35
36
|
pageTypePlural,
|
|
36
37
|
};
|
|
37
38
|
|
|
39
|
+
const exportsMapping = [
|
|
40
|
+
[
|
|
41
|
+
"GET",
|
|
42
|
+
`/${baseResource}/exports`,
|
|
43
|
+
"exports",
|
|
44
|
+
"pages/exports",
|
|
45
|
+
"index",
|
|
46
|
+
ExportsHandler.index,
|
|
47
|
+
],
|
|
48
|
+
];
|
|
49
|
+
adapter.routeCollection(exportsMapping, resourceConfig);
|
|
50
|
+
|
|
38
51
|
routeCollection(adapter, pageTypePlural, PagesHandler, {
|
|
39
52
|
...resourceConfig,
|
|
40
53
|
viewsFallback: "pages",
|