@raketa-cloud/admin 1.6.2 → 1.7.0
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 +4 -4
- package/src/HonoAdapter.js +80 -39
- package/src/handlers/_shared/getArticlesUpdateDestination.js +7 -0
- package/src/handlers/_shared/getEventsUpdateDestination.js +7 -0
- package/src/handlers/_shared/getPagesUpdateDestination.js +8 -0
- package/src/handlers/article_revisions/CodeHandler.js +2 -1
- package/src/handlers/article_revisions/GeneralHandler.js +2 -1
- package/src/handlers/article_revisions/MetaHandler.js +2 -1
- package/src/handlers/article_revisions/TagHandler.js +2 -1
- package/src/handlers/article_revisions/VisualHandler.js +2 -1
- package/src/handlers/event_revisions/CodeHandler.js +2 -1
- package/src/handlers/event_revisions/GeneralHandler.js +2 -1
- package/src/handlers/event_revisions/MetaHandler.js +2 -1
- package/src/handlers/event_revisions/TagHandler.js +2 -1
- package/src/handlers/event_revisions/VisualHandler.js +2 -1
- package/src/handlers/page_revisions/CodeHandler.js +3 -1
- package/src/handlers/page_revisions/GeneralHandler.js +3 -1
- package/src/handlers/page_revisions/MetaHandler.js +3 -1
- package/src/handlers/page_revisions/TagHandler.js +3 -1
- package/src/handlers/page_revisions/VisualHandler.js +3 -1
- package/src/index.js +4 -0
- package/src/tests/HonoAdapter.test.js +89 -20
- package/src/tests/handlers/page_revisions/CodesHandler.test/update.test.js +50 -0
- package/src/tests/handlers/page_revisions/GeneralHandler.test/update.test.js +53 -0
- package/src/tests/handlers/page_revisions/MetaHandler.test/update.test.js +52 -0
- package/src/tests/handlers/page_revisions/TagHandler.test/update.test.js +43 -0
- package/src/tests/handlers/page_revisions/VisualHandler.test.test/update.test.js +44 -0
- package/src/views/article_revisions/codes/_form.ejs +3 -2
- package/src/views/article_revisions/general/_form.ejs +3 -2
- package/src/views/article_revisions/meta/_form.ejs +3 -2
- package/src/views/article_revisions/tags/_form.ejs +3 -2
- package/src/views/article_revisions/visuals/_form.ejs +3 -2
- package/src/views/event_revisions/codes/_form.ejs +3 -2
- package/src/views/event_revisions/general/_form.ejs +3 -2
- package/src/views/event_revisions/meta/_form.ejs +3 -2
- package/src/views/event_revisions/tags/_form.ejs +3 -2
- package/src/views/event_revisions/visuals/_form.ejs +3 -2
- package/src/views/page_revisions/codes/_form.ejs +3 -2
- package/src/views/page_revisions/general/_form.ejs +3 -2
- package/src/views/page_revisions/meta/_form.ejs +3 -2
- package/src/views/page_revisions/tags/_form.ejs +3 -2
- package/src/views/page_revisions/visuals/_form.ejs +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raketa-cloud/admin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"src/",
|
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
"prepare": "npx tailwindcss -i ./src/styles/application.css -o ./dist/application.css"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@babel/core": "7.27.
|
|
15
|
+
"@babel/core": "7.27.4",
|
|
16
16
|
"@babel/preset-env": "7.27.2",
|
|
17
17
|
"@babel/preset-typescript": "7.27.1",
|
|
18
18
|
"@repo/typescript-config": "*",
|
|
19
19
|
"@tailwindcss/forms": "0.5.7",
|
|
20
20
|
"@tailwindcss/typography": "0.5.13",
|
|
21
|
-
"@types/node": "22.15.
|
|
21
|
+
"@types/node": "22.15.30",
|
|
22
22
|
"jest": "29.7.0",
|
|
23
23
|
"tailwindcss": "3.4.4",
|
|
24
24
|
"typescript": "5.8.3"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"ejs": "3.1.10",
|
|
28
|
-
"graphql-request": "7.
|
|
28
|
+
"graphql-request": "7.2.0"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/src/HonoAdapter.js
CHANGED
|
@@ -16,14 +16,14 @@ import utils from "./helpers/utils";
|
|
|
16
16
|
|
|
17
17
|
function escapeHtml(string) {
|
|
18
18
|
const escape = {
|
|
19
|
-
'"':
|
|
20
|
-
|
|
21
|
-
"'":
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
'"': """,
|
|
20
|
+
"&": "&",
|
|
21
|
+
"'": "'",
|
|
22
|
+
"<": "<",
|
|
23
|
+
">": ">",
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
return String(string).replace(/["&'<>]/g, char => escape[char]);
|
|
26
|
+
return String(string).replace(/["&'<>]/g, (char) => escape[char]);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const renderError = (message, stack) => {
|
|
@@ -38,7 +38,8 @@ const renderError = (message, stack) => {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
// Convert to array if needed
|
|
41
|
-
const toArray =
|
|
41
|
+
const toArray = (value) =>
|
|
42
|
+
Array.isArray(value) ? value : [value].filter(Boolean);
|
|
42
43
|
|
|
43
44
|
// Handle nested form values
|
|
44
45
|
function putIn(result, key, value) {
|
|
@@ -50,9 +51,8 @@ function putIn(result, key, value) {
|
|
|
50
51
|
|
|
51
52
|
if (isLastSegment) {
|
|
52
53
|
// Set value at final segment
|
|
53
|
-
current[segment] =
|
|
54
|
-
? toArray(value)
|
|
55
|
-
: value;
|
|
54
|
+
current[segment] =
|
|
55
|
+
key.endsWith("[]") || Array.isArray(value) ? toArray(value) : value;
|
|
56
56
|
} else {
|
|
57
57
|
// Create path to next segment
|
|
58
58
|
current[segment] ??= {};
|
|
@@ -62,22 +62,26 @@ function putIn(result, key, value) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Normalize paths for consistent handling
|
|
65
|
-
const sanitizePath = filePath =>
|
|
65
|
+
const sanitizePath = (filePath) =>
|
|
66
|
+
path.normalize(path.join("/", ...filePath.split("/")));
|
|
66
67
|
|
|
67
68
|
// Find template path with fallbacks
|
|
68
69
|
function getPartialPath(partialPath, views, viewsFallback) {
|
|
69
70
|
const paths = [
|
|
70
71
|
sanitizePath(`${process.cwd()}/src/views/${views}/${partialPath}`),
|
|
71
72
|
sanitizePath(`${currentPath}/views/${views}/${partialPath}`),
|
|
72
|
-
viewsFallback &&
|
|
73
|
+
viewsFallback &&
|
|
74
|
+
sanitizePath(`${currentPath}/views/${viewsFallback}/${partialPath}`),
|
|
73
75
|
].filter(Boolean);
|
|
74
76
|
|
|
75
77
|
// Return first existing path
|
|
76
|
-
const existingPath = paths.find(path => existsSync(path));
|
|
78
|
+
const existingPath = paths.find((path) => existsSync(path));
|
|
77
79
|
|
|
78
80
|
if (existingPath) return existingPath;
|
|
79
81
|
|
|
80
|
-
throw new Error(
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Template not found: "${partialPath}"\nSearched in:\n${paths.join("\n")}`
|
|
84
|
+
);
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
// Parse request body based on content type
|
|
@@ -109,6 +113,7 @@ export default class HonoAdapter {
|
|
|
109
113
|
this.globalConfig = config;
|
|
110
114
|
this.globalBeforeFilters = [];
|
|
111
115
|
this.globalAfterFilters = [];
|
|
116
|
+
this.localRouter = [];
|
|
112
117
|
|
|
113
118
|
// TODO: Pass in a path to redirect to after login?
|
|
114
119
|
if (config) {
|
|
@@ -261,21 +266,44 @@ export default class HonoAdapter {
|
|
|
261
266
|
});
|
|
262
267
|
}
|
|
263
268
|
|
|
264
|
-
|
|
265
|
-
|
|
269
|
+
addRoute(verb, path, handler) {
|
|
270
|
+
this.localRouter = this.localRouter.filter((routeItem) => {
|
|
271
|
+
return !(routeItem.verb === verb && routeItem.path === path);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
this.localRouter.push({
|
|
275
|
+
verb,
|
|
276
|
+
path,
|
|
277
|
+
handler,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
link() {
|
|
282
|
+
this.localRouter.forEach((routeItem) => {
|
|
283
|
+
const method = this.getMethod(routeItem.verb);
|
|
284
|
+
|
|
285
|
+
method(routeItem.path, routeItem.handler);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async createAction(
|
|
290
|
+
[verb, path, resource, views, action, handler],
|
|
291
|
+
resourceConfig
|
|
292
|
+
) {
|
|
266
293
|
const { i18n } = this.globalConfig;
|
|
267
294
|
|
|
268
295
|
Object.keys(i18n.locales).forEach((currentLocale) => {
|
|
269
296
|
const completePath = `/${currentLocale}${path}`;
|
|
270
297
|
|
|
271
|
-
|
|
272
|
-
this.router.routes = this.router.routes.filter(route =>
|
|
273
|
-
route[0] !== verb || route[1] !== completePath
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
method(completePath, async (routerCtx, next) => {
|
|
298
|
+
this.addRoute(verb, completePath, async (routerCtx, next) => {
|
|
277
299
|
// Create context with request data
|
|
278
|
-
const ctx = await this.createContext(
|
|
300
|
+
const ctx = await this.createContext(
|
|
301
|
+
routerCtx,
|
|
302
|
+
next,
|
|
303
|
+
verb,
|
|
304
|
+
resource,
|
|
305
|
+
action
|
|
306
|
+
);
|
|
279
307
|
|
|
280
308
|
// Set context properties
|
|
281
309
|
Object.assign(ctx, {
|
|
@@ -285,7 +313,7 @@ export default class HonoAdapter {
|
|
|
285
313
|
maxOpenRevisions: this.globalConfig?.maxOpenRevisions || 3,
|
|
286
314
|
locale: currentLocale,
|
|
287
315
|
remoteConfig: { ...this.globalConfig.remoteConfig },
|
|
288
|
-
logger: Logger
|
|
316
|
+
logger: Logger,
|
|
289
317
|
});
|
|
290
318
|
|
|
291
319
|
// Build context object
|
|
@@ -301,15 +329,17 @@ export default class HonoAdapter {
|
|
|
301
329
|
resourceConfig,
|
|
302
330
|
adminJwt: ctx.authentication?.token,
|
|
303
331
|
remoteConfig: { ...this.globalConfig.remoteConfig },
|
|
304
|
-
globalConfig: this.globalConfig
|
|
332
|
+
globalConfig: this.globalConfig,
|
|
305
333
|
};
|
|
306
334
|
|
|
307
|
-
ctx.logger.info(
|
|
335
|
+
ctx.logger.info(
|
|
336
|
+
`${ctx.method} "${ctx.rawPath}" -> ${resource}/${action} | ${JSON.stringify(ctx.params)}`
|
|
337
|
+
);
|
|
308
338
|
|
|
309
339
|
const assigns = {
|
|
310
340
|
beforeFilterAssigns: {},
|
|
311
341
|
actionAssigns: {},
|
|
312
|
-
afterFilterAssigns: {}
|
|
342
|
+
afterFilterAssigns: {},
|
|
313
343
|
};
|
|
314
344
|
|
|
315
345
|
// Initialize global context
|
|
@@ -328,7 +358,7 @@ export default class HonoAdapter {
|
|
|
328
358
|
}
|
|
329
359
|
|
|
330
360
|
// Process action handler
|
|
331
|
-
const result = await handler(ctx, resourceConfig) || {};
|
|
361
|
+
const result = (await handler(ctx, resourceConfig)) || {};
|
|
332
362
|
assigns.actionAssigns = result.assigns || {};
|
|
333
363
|
|
|
334
364
|
if (result.headers) this.applyHeaders(result.headers, routerCtx);
|
|
@@ -340,10 +370,14 @@ export default class HonoAdapter {
|
|
|
340
370
|
if (pattern.test(ctx.path)) {
|
|
341
371
|
const afterResult = filter(ctx) || {};
|
|
342
372
|
|
|
343
|
-
if (afterResult.redirect)
|
|
373
|
+
if (afterResult.redirect)
|
|
374
|
+
return routerCtx.redirect(afterResult.redirect);
|
|
344
375
|
if (afterResult.json) return routerCtx.json(afterResult.json);
|
|
345
376
|
|
|
346
|
-
Object.assign(
|
|
377
|
+
Object.assign(
|
|
378
|
+
assigns.afterFilterAssigns,
|
|
379
|
+
afterResult.assigns || {}
|
|
380
|
+
);
|
|
347
381
|
}
|
|
348
382
|
}
|
|
349
383
|
|
|
@@ -353,16 +387,19 @@ export default class HonoAdapter {
|
|
|
353
387
|
...assigns.beforeFilterAssigns,
|
|
354
388
|
...assigns.actionAssigns,
|
|
355
389
|
...assigns.afterFilterAssigns,
|
|
356
|
-
_context
|
|
390
|
+
_context,
|
|
357
391
|
};
|
|
358
392
|
|
|
359
393
|
// Render the view
|
|
360
|
-
const rendered = this.renderAction(
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
394
|
+
const rendered = this.renderAction(
|
|
395
|
+
{
|
|
396
|
+
views,
|
|
397
|
+
viewsFallback: resourceConfig.viewsFallback,
|
|
398
|
+
action,
|
|
399
|
+
assigns: fullAssigns,
|
|
400
|
+
},
|
|
401
|
+
this.globalConfig?.helpers || {}
|
|
402
|
+
);
|
|
366
403
|
|
|
367
404
|
return routerCtx.html(rendered);
|
|
368
405
|
});
|
|
@@ -371,7 +408,9 @@ export default class HonoAdapter {
|
|
|
371
408
|
|
|
372
409
|
// Single method to handle all route types
|
|
373
410
|
routeCollection(mapping, resourceConfig) {
|
|
374
|
-
mapping.forEach(handlerMap =>
|
|
411
|
+
mapping.forEach((handlerMap) =>
|
|
412
|
+
this.createAction(handlerMap, resourceConfig)
|
|
413
|
+
);
|
|
375
414
|
}
|
|
376
415
|
|
|
377
416
|
// Use the same implementation for all route types
|
|
@@ -391,7 +430,9 @@ export default class HonoAdapter {
|
|
|
391
430
|
|
|
392
431
|
applyHeaders(headers, router) {
|
|
393
432
|
if (!headers) return;
|
|
394
|
-
Object.entries(headers).forEach(([key, value]) =>
|
|
433
|
+
Object.entries(headers).forEach(([key, value]) =>
|
|
434
|
+
router.header(key, value)
|
|
435
|
+
);
|
|
395
436
|
}
|
|
396
437
|
|
|
397
438
|
formatQuery(queries) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export default function getArticlesUpdateDestination(ctx, revision_id, section) {
|
|
2
|
+
const base = `/${ctx.locale}/${ctx.resourceConfig.articleType}_revisions/${revision_id}/`
|
|
3
|
+
const shouldPublish = ctx.body && ctx.body._next === 'publish';
|
|
4
|
+
return shouldPublish
|
|
5
|
+
? `${base}publishes/new`
|
|
6
|
+
: `${base}${section}/edit?success=1`
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export default function getEventsUpdateDestination(ctx, revision_id, section) {
|
|
2
|
+
const base = `/${ctx.locale}/${ctx.resourceConfig.eventType}_revisions/${revision_id}/`
|
|
3
|
+
const shouldPublish = ctx.body && ctx.body._next === 'publish';
|
|
4
|
+
return shouldPublish
|
|
5
|
+
? `${base}publishes/new`
|
|
6
|
+
: `${base}${section}/edit?success=1`
|
|
7
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export default function getPagesUpdateDestination(ctx, revision_id, section) {
|
|
2
|
+
const base = `/${ctx.locale}/${ctx.resourceConfig.pageType}_revisions/${revision_id}/`
|
|
3
|
+
const shouldPublish = ctx.body && ctx.body._next === 'publish';
|
|
4
|
+
return shouldPublish
|
|
5
|
+
? `${base}publishes/new`
|
|
6
|
+
: `${base}${section}/edit?success=1`
|
|
7
|
+
}
|
|
8
|
+
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateInput } from "../../utils";
|
|
2
|
+
import getArticlesUpdateDestination from "../_shared/getArticlesUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -64,7 +65,7 @@ const update = async (ctx) => {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
return {
|
|
67
|
-
redirect:
|
|
68
|
+
redirect: getArticlesUpdateDestination(ctx, revision.id, 'codes'),
|
|
68
69
|
};
|
|
69
70
|
};
|
|
70
71
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateInput } from "../../utils";
|
|
2
|
+
import getArticlesUpdateDestination from "../_shared/getArticlesUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -66,7 +67,7 @@ const update = async (ctx) => {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
return {
|
|
69
|
-
redirect:
|
|
70
|
+
redirect: getArticlesUpdateDestination(ctx, revision.id, 'general'),
|
|
70
71
|
};
|
|
71
72
|
};
|
|
72
73
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateInput } from "../../utils";
|
|
2
|
+
import getArticlesUpdateDestination from "../_shared/getArticlesUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -64,7 +65,7 @@ const update = async (ctx) => {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
return {
|
|
67
|
-
redirect:
|
|
68
|
+
redirect: getArticlesUpdateDestination(ctx, revision.id, 'meta'),
|
|
68
69
|
};
|
|
69
70
|
};
|
|
70
71
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateInput } from "../../utils";
|
|
2
|
+
import getArticlesUpdateDestination from "../_shared/getArticlesUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -72,7 +73,7 @@ const update = async (ctx) => {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
return {
|
|
75
|
-
redirect:
|
|
76
|
+
redirect: getArticlesUpdateDestination(ctx, revision.id, 'tags'),
|
|
76
77
|
};
|
|
77
78
|
};
|
|
78
79
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateInput } from "../../utils";
|
|
2
|
+
import getArticlesUpdateDestination from "../_shared/getArticlesUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -64,7 +65,7 @@ const update = async (ctx) => {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
return {
|
|
67
|
-
redirect:
|
|
68
|
+
redirect: getArticlesUpdateDestination(ctx, revision.id, 'visuals'),
|
|
68
69
|
};
|
|
69
70
|
};
|
|
70
71
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateTimeInput, toDateInput } from "../../utils";
|
|
2
|
+
import getEventsUpdateDestination from "../_shared/getEventsUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -62,7 +63,7 @@ const update = async (ctx) => {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
return {
|
|
65
|
-
redirect:
|
|
66
|
+
redirect: getEventsUpdateDestination(ctx, revision.id, 'codes'),
|
|
66
67
|
};
|
|
67
68
|
};
|
|
68
69
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateTimeInput, toDateInput } from "../../utils";
|
|
2
|
+
import getEventsUpdateDestination from "../_shared/getEventsUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -64,7 +65,7 @@ const update = async (ctx) => {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
return {
|
|
67
|
-
redirect:
|
|
68
|
+
redirect: getEventsUpdateDestination(ctx, revision.id, 'general'),
|
|
68
69
|
};
|
|
69
70
|
};
|
|
70
71
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateTimeInput, toDateInput } from "../../utils";
|
|
2
|
+
import getEventsUpdateDestination from "../_shared/getEventsUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -62,7 +63,7 @@ const update = async (ctx) => {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
return {
|
|
65
|
-
redirect:
|
|
66
|
+
redirect: getEventsUpdateDestination(ctx, revision.id, 'meta'),
|
|
66
67
|
};
|
|
67
68
|
};
|
|
68
69
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateTimeInput, toDateInput } from "../../utils";
|
|
2
|
+
import getEventsUpdateDestination from "../_shared/getEventsUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -69,7 +70,7 @@ const update = async (ctx) => {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
return {
|
|
72
|
-
redirect:
|
|
73
|
+
redirect: getEventsUpdateDestination(ctx, revision.id, 'tags'),
|
|
73
74
|
};
|
|
74
75
|
};
|
|
75
76
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toDateTimeInput, toDateInput } from "../../utils";
|
|
2
|
+
import getEventsUpdateDestination from "../_shared/getEventsUpdateDestination";
|
|
2
3
|
|
|
3
4
|
const edit_form = async (ctx) => {
|
|
4
5
|
const { parent_id } = ctx.params;
|
|
@@ -62,7 +63,7 @@ const update = async (ctx) => {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
return {
|
|
65
|
-
redirect:
|
|
66
|
+
redirect: getEventsUpdateDestination(ctx, revision.id, 'visuals'),
|
|
66
67
|
};
|
|
67
68
|
};
|
|
68
69
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import getPagesUpdateDestination from "../_shared/getPagesUpdateDestination";
|
|
2
|
+
|
|
1
3
|
const edit_form = async (ctx) => {
|
|
2
4
|
const { parent_id } = ctx.params;
|
|
3
5
|
|
|
@@ -47,7 +49,7 @@ const update = async (ctx) => {
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
return {
|
|
50
|
-
redirect:
|
|
52
|
+
redirect: getPagesUpdateDestination(ctx, revision.id, 'codes'),
|
|
51
53
|
};
|
|
52
54
|
};
|
|
53
55
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import getPagesUpdateDestination from "../_shared/getPagesUpdateDestination";
|
|
2
|
+
|
|
1
3
|
const edit_form = async (ctx) => {
|
|
2
4
|
const { parent_id } = ctx.params;
|
|
3
5
|
|
|
@@ -49,7 +51,7 @@ const update = async (ctx) => {
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
return {
|
|
52
|
-
redirect:
|
|
54
|
+
redirect: getPagesUpdateDestination(ctx, revision.id, 'general'),
|
|
53
55
|
};
|
|
54
56
|
};
|
|
55
57
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import getPagesUpdateDestination from "../_shared/getPagesUpdateDestination";
|
|
2
|
+
|
|
1
3
|
const edit_form = async (ctx) => {
|
|
2
4
|
const { parent_id } = ctx.params;
|
|
3
5
|
|
|
@@ -47,7 +49,7 @@ const update = async (ctx) => {
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
return {
|
|
50
|
-
redirect:
|
|
52
|
+
redirect: getPagesUpdateDestination(ctx, revision.id, 'meta'),
|
|
51
53
|
};
|
|
52
54
|
};
|
|
53
55
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import getPagesUpdateDestination from "../_shared/getPagesUpdateDestination";
|
|
2
|
+
|
|
1
3
|
const fetchDefaultLocaleTags = async (ctx, resource) => {
|
|
2
4
|
if (ctx.locale === ctx.defaultLocale) {
|
|
3
5
|
return { defaultLocaleName: "", defaultLocaleTags: [] };
|
|
@@ -95,7 +97,7 @@ const update = async (ctx) => {
|
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
return {
|
|
98
|
-
redirect:
|
|
100
|
+
redirect: getPagesUpdateDestination(ctx, revision.id, 'tags'),
|
|
99
101
|
};
|
|
100
102
|
};
|
|
101
103
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import getPagesUpdateDestination from "../_shared/getPagesUpdateDestination";
|
|
2
|
+
|
|
1
3
|
const edit_form = async (ctx) => {
|
|
2
4
|
const { parent_id } = ctx.params;
|
|
3
5
|
|
|
@@ -48,7 +50,7 @@ const update = async (ctx) => {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
return {
|
|
51
|
-
redirect:
|
|
53
|
+
redirect: getPagesUpdateDestination(ctx, revision.id, 'visuals'),
|
|
52
54
|
};
|
|
53
55
|
};
|
|
54
56
|
|
package/src/index.js
CHANGED
|
@@ -90,5 +90,9 @@ export { default as EventTranslationsHandler } from "./handlers/events/Translati
|
|
|
90
90
|
export { default as EventURLsHandler } from "./handlers/events/URLsHandler";
|
|
91
91
|
export { default as EventsHandler } from "./handlers/events/EventsHandler";
|
|
92
92
|
|
|
93
|
+
export { default as getPagesUpdateDestination } from "./handlers/_shared/getPagesUpdateDestination";
|
|
94
|
+
export { default as getArticlesUpdateDestination } from "./handlers/_shared/getArticlesUpdateDestination";
|
|
95
|
+
export { default as getEventsUpdateDestination } from "./handlers/_shared/getEventsUpdateDestination";
|
|
96
|
+
|
|
93
97
|
export { default as Factory } from "./tests/support/factory";
|
|
94
98
|
export { default as Logger } from "./Logger";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import HonoAdapter from "../HonoAdapter";
|
|
2
|
+
import RaketaContext from "../RaketaContext";
|
|
2
3
|
|
|
3
4
|
class HonoMock {
|
|
4
5
|
constructor() {
|
|
5
6
|
this.router = {
|
|
6
|
-
routes: []
|
|
7
|
+
routes: [],
|
|
7
8
|
};
|
|
8
9
|
this.get = jest.fn();
|
|
9
10
|
this.post = jest.fn();
|
|
@@ -17,12 +18,12 @@ class HonoMock {
|
|
|
17
18
|
describe("HonoAdapter", () => {
|
|
18
19
|
let app;
|
|
19
20
|
let adapter;
|
|
20
|
-
|
|
21
|
+
|
|
21
22
|
beforeEach(() => {
|
|
22
23
|
app = new HonoMock();
|
|
23
24
|
adapter = new HonoAdapter(app);
|
|
24
25
|
});
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
// Method tests
|
|
27
28
|
test("getMethod returns Hono's get for invalid verbs", () => {
|
|
28
29
|
expect(adapter.getMethod("not existing")).toBe(app.get);
|
|
@@ -78,58 +79,126 @@ describe("HonoAdapter", () => {
|
|
|
78
79
|
},
|
|
79
80
|
});
|
|
80
81
|
});
|
|
81
|
-
|
|
82
|
+
|
|
82
83
|
test("setGlobalContext sets the global context function", () => {
|
|
83
84
|
const newCtx = () => ({ test: true });
|
|
84
85
|
adapter.setGlobalContext(newCtx);
|
|
85
86
|
expect(adapter.globalCtx).toBe(newCtx);
|
|
86
87
|
});
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
test("applyHeaders applies headers to router context", () => {
|
|
89
90
|
const headers = {
|
|
90
91
|
"Content-Type": "application/json",
|
|
91
|
-
"X-Custom-Header": "test-value"
|
|
92
|
+
"X-Custom-Header": "test-value",
|
|
92
93
|
};
|
|
93
|
-
|
|
94
|
+
|
|
94
95
|
adapter.applyHeaders(headers, app);
|
|
95
|
-
|
|
96
|
+
|
|
96
97
|
expect(app.header).toHaveBeenCalledTimes(2);
|
|
97
98
|
expect(app.header).toHaveBeenCalledWith("Content-Type", "application/json");
|
|
98
99
|
expect(app.header).toHaveBeenCalledWith("X-Custom-Header", "test-value");
|
|
99
100
|
});
|
|
100
|
-
|
|
101
|
+
|
|
101
102
|
test("applyHeaders does nothing when headers is undefined", () => {
|
|
102
103
|
adapter.applyHeaders(undefined, app);
|
|
103
104
|
expect(app.header).not.toHaveBeenCalled();
|
|
104
105
|
});
|
|
105
|
-
|
|
106
|
+
|
|
106
107
|
test("formatQuery formats query parameters correctly", () => {
|
|
107
108
|
const queries = {
|
|
108
109
|
single: ["value"],
|
|
109
|
-
multiple: ["one", "two"]
|
|
110
|
+
multiple: ["one", "two"],
|
|
110
111
|
};
|
|
111
|
-
|
|
112
|
+
|
|
112
113
|
const result = adapter.formatQuery(queries);
|
|
113
|
-
|
|
114
|
+
|
|
114
115
|
expect(result).toEqual({
|
|
115
116
|
single: "value",
|
|
116
|
-
multiple: ["one", "two"]
|
|
117
|
+
multiple: ["one", "two"],
|
|
117
118
|
});
|
|
118
119
|
});
|
|
119
120
|
|
|
120
121
|
test("parseFormBody handles array values", () => {
|
|
121
122
|
expect(
|
|
122
123
|
adapter.parseFormBody({
|
|
123
|
-
"tags[]": ["javascript", "react", "node"]
|
|
124
|
+
"tags[]": ["javascript", "react", "node"],
|
|
124
125
|
})
|
|
125
126
|
).toEqual({
|
|
126
|
-
tags: ["javascript", "react", "node"]
|
|
127
|
+
tags: ["javascript", "react", "node"],
|
|
127
128
|
});
|
|
128
129
|
});
|
|
129
130
|
|
|
130
131
|
test("parseFormBody handles empty values", () => {
|
|
131
|
-
expect(
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
expect(adapter.parseFormBody({})).toEqual({});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("renderLayout renders an existing layout template", async () => {
|
|
136
|
+
const result = await adapter.renderLayout(
|
|
137
|
+
{
|
|
138
|
+
_context: {
|
|
139
|
+
layout: {
|
|
140
|
+
sidebar: [],
|
|
141
|
+
},
|
|
142
|
+
path: "/en/pages",
|
|
143
|
+
locale: "en",
|
|
144
|
+
locales: {
|
|
145
|
+
en: {
|
|
146
|
+
flag: "",
|
|
147
|
+
name: "English",
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
globalConfig: {
|
|
151
|
+
cloud: {
|
|
152
|
+
images_api_key: "api-key",
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
"<h1>Hi</h1>"
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
expect(typeof result).toBe("string");
|
|
161
|
+
expect(result.length).toBeGreaterThan(0);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("addRouote - adds a route to the local router", () => {
|
|
165
|
+
expect(adapter.localRouter).toEqual([]);
|
|
166
|
+
|
|
167
|
+
const handler = () => {};
|
|
168
|
+
|
|
169
|
+
adapter.addRoute("GET", "/en/pages", handler);
|
|
170
|
+
|
|
171
|
+
expect(adapter.localRouter).toEqual([
|
|
172
|
+
{ verb: "GET", path: "/en/pages", handler },
|
|
173
|
+
]);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("addRouote - overrides routes if they already exist in the local router", () => {
|
|
177
|
+
expect(adapter.localRouter).toEqual([]);
|
|
178
|
+
|
|
179
|
+
const handler = () => {};
|
|
180
|
+
const handler2 = () => {};
|
|
181
|
+
|
|
182
|
+
adapter.addRoute("GET", "/en/pages", handler);
|
|
183
|
+
|
|
184
|
+
expect(adapter.localRouter).toEqual([
|
|
185
|
+
{ verb: "GET", path: "/en/pages", handler },
|
|
186
|
+
]);
|
|
187
|
+
|
|
188
|
+
adapter.addRoute("GET", "/en/pages", handler2);
|
|
189
|
+
|
|
190
|
+
expect(adapter.localRouter).toEqual([
|
|
191
|
+
{ verb: "GET", path: "/en/pages", handler: handler2 },
|
|
192
|
+
]);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("link links local routes with the app", () => {
|
|
196
|
+
const handler = () => {};
|
|
197
|
+
adapter.addRoute("GET", "/en/pages", handler);
|
|
198
|
+
|
|
199
|
+
adapter.link();
|
|
200
|
+
|
|
201
|
+
expect(app.get).toHaveBeenCalledTimes(1);
|
|
202
|
+
expect(app.get).toHaveBeenCalledWith("/en/pages", handler);
|
|
134
203
|
});
|
|
135
|
-
});
|
|
204
|
+
});
|
|
@@ -17,6 +17,7 @@ function buildCtx(revision, updateSettings) {
|
|
|
17
17
|
pageType: "page",
|
|
18
18
|
pageTypePlural: "pages",
|
|
19
19
|
},
|
|
20
|
+
body: {},
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -70,6 +71,55 @@ describe("Page revision code settings", () => {
|
|
|
70
71
|
});
|
|
71
72
|
});
|
|
72
73
|
|
|
74
|
+
test("update and publish", async () => {
|
|
75
|
+
const revision = Factory.build("page_revision");
|
|
76
|
+
|
|
77
|
+
const updateSettings = {
|
|
78
|
+
settings: {
|
|
79
|
+
codes: {
|
|
80
|
+
scripts: JSON.stringify([{ type: "bla" }]),
|
|
81
|
+
structured_json: JSON.stringify({ type: "article" }),
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const ctx = { ...buildCtx(revision, updateSettings), body: { _next: 'publish' } };
|
|
87
|
+
|
|
88
|
+
const mockFind = jest
|
|
89
|
+
.spyOn(ctx.client.pageRevisions, "find")
|
|
90
|
+
.mockImplementation(() => revision);
|
|
91
|
+
|
|
92
|
+
const mockUpdate = jest
|
|
93
|
+
.spyOn(ctx.client.pageRevisions, "update")
|
|
94
|
+
.mockImplementation(() => ({
|
|
95
|
+
revision: {},
|
|
96
|
+
errors: null,
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
const result = await CodeHandler.update(ctx);
|
|
100
|
+
|
|
101
|
+
expect(mockFind).toHaveBeenCalledWith(revision.id, "en");
|
|
102
|
+
expect(mockUpdate).toHaveBeenCalledWith(revision.id, "en", {
|
|
103
|
+
slug: revision.slug,
|
|
104
|
+
title: revision.title,
|
|
105
|
+
visibility: revision.visibility,
|
|
106
|
+
widgets: revision.widgets,
|
|
107
|
+
tags: revision.tags.map((t) => t.id),
|
|
108
|
+
categories: revision.categories.map((c) => c.id),
|
|
109
|
+
settings: {
|
|
110
|
+
...revision.settings,
|
|
111
|
+
codes: {
|
|
112
|
+
scripts: [{ type: "bla" }],
|
|
113
|
+
structured_json: { type: "article" },
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(result).toEqual({
|
|
119
|
+
redirect: `/en/page_revisions/${revision.id}/publishes/new`,
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
73
123
|
test("update failed", async () => {
|
|
74
124
|
const revision = Factory.build("page_revision");
|
|
75
125
|
|
|
@@ -19,6 +19,7 @@ function buildCtx(revision, updateSettings) {
|
|
|
19
19
|
pageType: "page",
|
|
20
20
|
pageTypePlural: "pages",
|
|
21
21
|
},
|
|
22
|
+
body: {},
|
|
22
23
|
};
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -75,6 +76,58 @@ describe("Page revision general settings", () => {
|
|
|
75
76
|
});
|
|
76
77
|
});
|
|
77
78
|
|
|
79
|
+
test("update and publish", async () => {
|
|
80
|
+
const revision = Factory.build("page_revision");
|
|
81
|
+
|
|
82
|
+
const updateSettings = {
|
|
83
|
+
title: "Updated title",
|
|
84
|
+
slug: "/updated-url",
|
|
85
|
+
visibility: "protected",
|
|
86
|
+
settings: {
|
|
87
|
+
credentials: {
|
|
88
|
+
user: "user",
|
|
89
|
+
password: "admin123!@#",
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const ctx = { ...buildCtx(revision, updateSettings), body: { _next: 'publish' } };
|
|
95
|
+
|
|
96
|
+
const mockFind = jest
|
|
97
|
+
.spyOn(ctx.client.pageRevisions, "find")
|
|
98
|
+
.mockImplementation(() => revision);
|
|
99
|
+
|
|
100
|
+
const mockUpdate = jest
|
|
101
|
+
.spyOn(ctx.client.pageRevisions, "update")
|
|
102
|
+
.mockImplementation(() => ({
|
|
103
|
+
revision: {},
|
|
104
|
+
errors: null,
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
const result = await GeneralHandler.update(ctx);
|
|
108
|
+
|
|
109
|
+
expect(mockFind).toHaveBeenCalledWith(revision.id, "en");
|
|
110
|
+
expect(mockUpdate).toHaveBeenCalledWith(revision.id, "en", {
|
|
111
|
+
slug: updateSettings.slug,
|
|
112
|
+
title: updateSettings.title,
|
|
113
|
+
visibility: updateSettings.visibility,
|
|
114
|
+
widgets: revision.widgets,
|
|
115
|
+
tags: revision.tags.map((t) => t.id),
|
|
116
|
+
categories: revision.categories.map((c) => c.id),
|
|
117
|
+
settings: {
|
|
118
|
+
...revision.settings,
|
|
119
|
+
credentials: {
|
|
120
|
+
user: updateSettings.settings.credentials.user,
|
|
121
|
+
password: updateSettings.settings.credentials.password,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(result).toEqual({
|
|
127
|
+
redirect: `/en/page_revisions/${revision.id}/publishes/new`,
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
78
131
|
test("update failed", async () => {
|
|
79
132
|
const revision = Factory.build("page_revision");
|
|
80
133
|
|
|
@@ -17,6 +17,7 @@ function buildCtx(revision, updateSettings) {
|
|
|
17
17
|
pageType: "page",
|
|
18
18
|
pageTypePlural: "pages",
|
|
19
19
|
},
|
|
20
|
+
body: {},
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -72,6 +73,57 @@ describe("Page revision meta settings", () => {
|
|
|
72
73
|
});
|
|
73
74
|
});
|
|
74
75
|
|
|
76
|
+
test("update and publish", async () => {
|
|
77
|
+
const revision = Factory.build("page_revision");
|
|
78
|
+
|
|
79
|
+
const updateSettings = {
|
|
80
|
+
settings: {
|
|
81
|
+
meta: {
|
|
82
|
+
title: "An open graph title",
|
|
83
|
+
description: "An open graph description",
|
|
84
|
+
image: JSON.stringify({ id: 42 }),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const ctx = { ...buildCtx(revision, updateSettings), body: { _next: 'publish' } };
|
|
90
|
+
|
|
91
|
+
const mockFind = jest
|
|
92
|
+
.spyOn(ctx.client.pageRevisions, "find")
|
|
93
|
+
.mockImplementation(() => revision);
|
|
94
|
+
|
|
95
|
+
const mockUpdate = jest
|
|
96
|
+
.spyOn(ctx.client.pageRevisions, "update")
|
|
97
|
+
.mockImplementation(() => ({
|
|
98
|
+
revision: {},
|
|
99
|
+
errors: null,
|
|
100
|
+
}));
|
|
101
|
+
|
|
102
|
+
const result = await MetaHandler.update(ctx);
|
|
103
|
+
|
|
104
|
+
expect(mockFind).toHaveBeenCalledWith(revision.id, "en");
|
|
105
|
+
expect(mockUpdate).toHaveBeenCalledWith(revision.id, "en", {
|
|
106
|
+
slug: revision.slug,
|
|
107
|
+
title: revision.title,
|
|
108
|
+
visibility: revision.visibility,
|
|
109
|
+
widgets: revision.widgets,
|
|
110
|
+
tags: revision.tags.map((t) => t.id),
|
|
111
|
+
categories: revision.categories.map((c) => c.id),
|
|
112
|
+
settings: {
|
|
113
|
+
...revision.settings,
|
|
114
|
+
meta: {
|
|
115
|
+
title: updateSettings.settings.meta.title,
|
|
116
|
+
description: updateSettings.settings.meta.description,
|
|
117
|
+
image: { id: 42 },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(result).toEqual({
|
|
123
|
+
redirect: `/en/page_revisions/${revision.id}/publishes/new`,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
75
127
|
test("update failed", async () => {
|
|
76
128
|
const revision = Factory.build("page_revision");
|
|
77
129
|
|
|
@@ -28,6 +28,7 @@ function buildCtx(revision, updateSettings) {
|
|
|
28
28
|
pageType: "page",
|
|
29
29
|
pageTypePlural: "pages",
|
|
30
30
|
},
|
|
31
|
+
body: {},
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -74,6 +75,48 @@ describe("Page revision tags settings", () => {
|
|
|
74
75
|
});
|
|
75
76
|
});
|
|
76
77
|
|
|
78
|
+
test("update and publish", async () => {
|
|
79
|
+
const tags = [
|
|
80
|
+
Factory.build("tag", { id: 42 }),
|
|
81
|
+
Factory.build("tag", { id: 69 }),
|
|
82
|
+
];
|
|
83
|
+
const revision = Factory.build("page_revision", { tags: [tags[0]] });
|
|
84
|
+
|
|
85
|
+
const updateSettings = {
|
|
86
|
+
tags: [tags.map((t) => t.id)],
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const ctx = { ...buildCtx(revision, updateSettings), body: { _next: 'publish' } };
|
|
90
|
+
|
|
91
|
+
const mockFind = jest
|
|
92
|
+
.spyOn(ctx.client.pageRevisions, "find")
|
|
93
|
+
.mockImplementation(() => revision);
|
|
94
|
+
|
|
95
|
+
const mockUpdate = jest
|
|
96
|
+
.spyOn(ctx.client.pageRevisions, "update")
|
|
97
|
+
.mockImplementation(() => ({
|
|
98
|
+
revision: {},
|
|
99
|
+
errors: null,
|
|
100
|
+
}));
|
|
101
|
+
|
|
102
|
+
const result = await TagHandler.update(ctx);
|
|
103
|
+
|
|
104
|
+
expect(mockFind).toHaveBeenCalledWith(revision.id, "en");
|
|
105
|
+
expect(mockUpdate).toHaveBeenCalledWith(revision.id, "en", {
|
|
106
|
+
slug: revision.slug,
|
|
107
|
+
title: revision.title,
|
|
108
|
+
visibility: revision.visibility,
|
|
109
|
+
widgets: revision.widgets,
|
|
110
|
+
tags: updateSettings.tags,
|
|
111
|
+
categories: revision.categories.map((c) => c.id),
|
|
112
|
+
settings: revision.settings,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expect(result).toEqual({
|
|
116
|
+
redirect: `/en/page_revisions/${revision.id}/publishes/new`,
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
77
120
|
test("update failed", async () => {
|
|
78
121
|
const tags = [
|
|
79
122
|
Factory.build("tag", { id: 42 }),
|
|
@@ -17,6 +17,7 @@ function buildCtx(revision, updateSettings) {
|
|
|
17
17
|
pageType: "page",
|
|
18
18
|
pageTypePlural: "pages",
|
|
19
19
|
},
|
|
20
|
+
body: {},
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -64,6 +65,49 @@ describe("Page revision visuals settings", () => {
|
|
|
64
65
|
});
|
|
65
66
|
});
|
|
66
67
|
|
|
68
|
+
test("update and publish", async () => {
|
|
69
|
+
const revision = Factory.build("page_revision");
|
|
70
|
+
|
|
71
|
+
const updateSettings = {
|
|
72
|
+
settings: {
|
|
73
|
+
layout: "bla",
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const ctx = { ...buildCtx(revision, updateSettings), body: { _next: 'publish' } };
|
|
78
|
+
|
|
79
|
+
const mockFind = jest
|
|
80
|
+
.spyOn(ctx.client.pageRevisions, "find")
|
|
81
|
+
.mockImplementation(() => revision);
|
|
82
|
+
|
|
83
|
+
const mockUpdate = jest
|
|
84
|
+
.spyOn(ctx.client.pageRevisions, "update")
|
|
85
|
+
.mockImplementation(() => ({
|
|
86
|
+
revision: {},
|
|
87
|
+
errors: null,
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
const result = await VisualHandler.update(ctx);
|
|
91
|
+
|
|
92
|
+
expect(mockFind).toHaveBeenCalledWith(revision.id, "en");
|
|
93
|
+
expect(mockUpdate).toHaveBeenCalledWith(revision.id, "en", {
|
|
94
|
+
slug: revision.slug,
|
|
95
|
+
title: revision.title,
|
|
96
|
+
visibility: revision.visibility,
|
|
97
|
+
widgets: revision.widgets,
|
|
98
|
+
tags: revision.tags.map((t) => t.id),
|
|
99
|
+
categories: revision.categories.map((c) => c.id),
|
|
100
|
+
settings: {
|
|
101
|
+
...revision.settings,
|
|
102
|
+
layout: "bla",
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(result).toEqual({
|
|
107
|
+
redirect: `/en/page_revisions/${revision.id}/publishes/new`,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
67
111
|
test("update failed", async () => {
|
|
68
112
|
const revision = Factory.build("page_revision");
|
|
69
113
|
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
<div class="form-actions">
|
|
7
7
|
<div class="btn-toolbar">
|
|
8
|
-
<%- f.submit('Save
|
|
9
|
-
|
|
8
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
9
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
10
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
10
11
|
</div>
|
|
11
12
|
</div>
|
|
12
13
|
</form>
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
<div class="form-actions">
|
|
11
11
|
<div class="btn-toolbar">
|
|
12
|
-
<%- f.submit('Save
|
|
13
|
-
|
|
12
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
13
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
14
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
14
15
|
</div>
|
|
15
16
|
</div>
|
|
16
17
|
</form>
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
<div class="form-actions">
|
|
9
9
|
<div class="btn-toolbar">
|
|
10
|
-
<%- f.submit('Save
|
|
11
|
-
|
|
10
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
11
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
12
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
12
13
|
</div>
|
|
13
14
|
</div>
|
|
14
15
|
</form>
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
<div class="form-actions">
|
|
6
6
|
<div class="btn-toolbar">
|
|
7
|
-
<%- f.submit('Save
|
|
8
|
-
|
|
7
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
8
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
9
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
9
10
|
</div>
|
|
10
11
|
</div>
|
|
11
12
|
</form>
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
<div class="form-actions">
|
|
6
6
|
<div class="btn-toolbar">
|
|
7
|
-
<%- f.submit('Save
|
|
8
|
-
|
|
7
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
8
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
9
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
9
10
|
</div>
|
|
10
11
|
</div>
|
|
11
12
|
</form>
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
<div class="form-actions">
|
|
7
7
|
<div class="btn-toolbar">
|
|
8
|
-
<%- f.submit('Save
|
|
9
|
-
|
|
8
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
9
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
10
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
10
11
|
</div>
|
|
11
12
|
</div>
|
|
12
13
|
</form>
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
<div class="form-actions">
|
|
13
13
|
<div class="btn-toolbar">
|
|
14
|
-
<%- f.submit('Save
|
|
15
|
-
|
|
14
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
15
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
16
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
16
17
|
</div>
|
|
17
18
|
</div>
|
|
18
19
|
</form>
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
<div class="form-actions">
|
|
9
9
|
<div class="btn-toolbar">
|
|
10
|
-
<%- f.submit('Save
|
|
11
|
-
|
|
10
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
11
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
12
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
12
13
|
</div>
|
|
13
14
|
</div>
|
|
14
15
|
</form>
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
<div class="form-actions">
|
|
6
6
|
<div class="btn-toolbar">
|
|
7
|
-
<%- f.submit('Save
|
|
8
|
-
|
|
7
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
8
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
9
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
9
10
|
</div>
|
|
10
11
|
</div>
|
|
11
12
|
</form>
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
<div class="form-actions">
|
|
6
6
|
<div class="btn-toolbar">
|
|
7
|
-
<%- f.submit('Save
|
|
8
|
-
|
|
7
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
8
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
9
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
9
10
|
</div>
|
|
10
11
|
</div>
|
|
11
12
|
</form>
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
<div class="form-actions">
|
|
7
7
|
<div class="btn-toolbar">
|
|
8
|
-
<%- f.submit('Save
|
|
9
|
-
|
|
8
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
9
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
10
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
10
11
|
</div>
|
|
11
12
|
</div>
|
|
12
13
|
</form>
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
<div class="form-actions">
|
|
10
10
|
<div class="btn-toolbar">
|
|
11
|
-
<%- f.submit('Save
|
|
12
|
-
|
|
11
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
12
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
13
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
13
14
|
</div>
|
|
14
15
|
</div>
|
|
15
16
|
</form>
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
<div class="form-actions">
|
|
9
9
|
<div class="btn-toolbar">
|
|
10
|
-
<%- f.submit('Save
|
|
11
|
-
|
|
10
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
11
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
12
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
12
13
|
</div>
|
|
13
14
|
</div>
|
|
14
15
|
</form>
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
<div class="form-actions">
|
|
6
6
|
<div class="btn-toolbar">
|
|
7
|
-
<%- f.submit('Save
|
|
8
|
-
|
|
7
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
8
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
9
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
9
10
|
</div>
|
|
10
11
|
</div>
|
|
11
12
|
</form>
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
<div class="form-actions">
|
|
6
6
|
<div class="btn-toolbar">
|
|
7
|
-
<%- f.submit('Save
|
|
8
|
-
|
|
7
|
+
<%- f.submit('Save', { class: 'btn-primary' }) %>
|
|
8
|
+
<%- f.submit('Save and publish', { class: 'btn-secondary', name: '_next', value: 'publish' }) %>
|
|
9
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.originalId}` %>" class="btn-text">Back</a>
|
|
9
10
|
</div>
|
|
10
11
|
</div>
|
|
11
12
|
</form>
|