@raketa-cloud/admin 1.2.0 → 1.3.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 +1 -1
- package/src/HonoAdapter.js +3 -4
- package/src/handlers/article_revisions/ArticleRevisionsHandler.js +8 -0
- package/src/handlers/articles/ArticlesHandler.js +12 -1
- package/src/handlers/event_revisions/EventRevisionsHandler.js +8 -0
- package/src/handlers/events/EventsHandler.js +13 -2
- package/src/handlers/page_revisions/PageRevisionsHandler.js +8 -0
- package/src/handlers/pages/PagesHandler.js +12 -1
- package/src/tests/handlers/article_revisions/ArticleRevisionsHandler.test/create.test.js +43 -35
- package/src/tests/handlers/articles/ArticlesHandler.test/show.test.js +12 -1
- package/src/tests/handlers/event_revisions/EventRevisionsHandler.test/create.test.js +41 -33
- package/src/tests/handlers/events/EventsHandler.test/show.test.js +12 -1
- package/src/tests/handlers/page_revisions/PageRevisionsHandler.test/create.test.js +41 -27
- package/src/tests/handlers/pages/PagesHandler.test/show.test.js +12 -1
- package/src/views/articles/_shared/_history_toolbar.ejs +2 -2
- package/src/views/articles/_shared/_toolbar.ejs +38 -26
- package/src/views/articles/_shared/history_table.ejs +6 -0
- package/src/views/events/_shared/_history_toolbar.ejs +2 -2
- package/src/views/events/_shared/_toolbar.ejs +38 -26
- package/src/views/events/_shared/history_table.ejs +6 -0
- package/src/views/pages/_shared/_history_toolbar.ejs +2 -2
- package/src/views/pages/_shared/_toolbar.ejs +38 -26
- package/src/views/pages/_shared/history_table.ejs +6 -0
package/package.json
CHANGED
package/src/HonoAdapter.js
CHANGED
|
@@ -36,10 +36,7 @@ export default class HonoAdapter extends Adapter {
|
|
|
36
36
|
this.globalConfig = config;
|
|
37
37
|
|
|
38
38
|
// TODO: Pass in a path to redirect to after login?
|
|
39
|
-
setupAuthentication(
|
|
40
|
-
router,
|
|
41
|
-
config
|
|
42
|
-
);
|
|
39
|
+
setupAuthentication(router, config);
|
|
43
40
|
|
|
44
41
|
router.get("/", (c) => {
|
|
45
42
|
return c.redirect("/en");
|
|
@@ -101,6 +98,7 @@ export default class HonoAdapter extends Adapter {
|
|
|
101
98
|
ctx.resourceConfig = resourceConfig;
|
|
102
99
|
ctx.locales = this.globalConfig.i18n.locales;
|
|
103
100
|
ctx.defaultLocale = this.globalConfig.i18n.defaultLocale;
|
|
101
|
+
ctx.maxOpenRevisions = this.globalConfig?.maxOpenRevisions || 3;
|
|
104
102
|
ctx.locale = currentLocale;
|
|
105
103
|
ctx.remoteConfig = { ...this.globalConfig.remoteConfig };
|
|
106
104
|
ctx.logger = Logger;
|
|
@@ -110,6 +108,7 @@ export default class HonoAdapter extends Adapter {
|
|
|
110
108
|
locales: ctx.locales,
|
|
111
109
|
locale: ctx.locale,
|
|
112
110
|
defaultLocale: ctx.defaultLocale,
|
|
111
|
+
maxOpenRevisions: ctx.maxOpenRevisions,
|
|
113
112
|
path: ctx.req.path,
|
|
114
113
|
query: ctx.query || {},
|
|
115
114
|
layout: {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
const create = async (ctx) => {
|
|
2
2
|
const { parent_id } = ctx.params;
|
|
3
3
|
|
|
4
|
+
const article = await ctx.client.articles.find(parent_id, ctx.locale);
|
|
5
|
+
|
|
6
|
+
if (article.revisionsCount >= ctx.maxOpenRevisions) {
|
|
7
|
+
return {
|
|
8
|
+
redirect: `/${ctx.locale}/${ctx.resourceConfig.articleTypePlural}/${parent_id}?revisions_count_error=1`,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
const { errors, articleErrors } = await ctx.client.articleRevisions.create(
|
|
5
13
|
parent_id,
|
|
6
14
|
ctx.locale
|
|
@@ -26,7 +26,18 @@ const show = async (ctx) => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const revisions = await ctx.client.articleRevisions.all(id, ctx.locale);
|
|
29
|
-
const changes = await ctx.client.articleChanges.list(
|
|
29
|
+
const changes = await ctx.client.articleChanges.list(
|
|
30
|
+
{
|
|
31
|
+
"f:change_type": [
|
|
32
|
+
"revision_publish",
|
|
33
|
+
"revision_lock",
|
|
34
|
+
"revision_unlock",
|
|
35
|
+
"update",
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
id,
|
|
39
|
+
ctx.locale
|
|
40
|
+
);
|
|
30
41
|
|
|
31
42
|
return {
|
|
32
43
|
assigns: {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
const create = async (ctx) => {
|
|
2
2
|
const { parent_id } = ctx.params;
|
|
3
3
|
|
|
4
|
+
const event = await ctx.client.events.find(parent_id, ctx.locale);
|
|
5
|
+
|
|
6
|
+
if (event.revisionsCount >= ctx.maxOpenRevisions) {
|
|
7
|
+
return {
|
|
8
|
+
redirect: `/${ctx.locale}/${ctx.resourceConfig.eventTypePlural}/${parent_id}?revisions_count_error=1`,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
const { errors, eventErrors } = await ctx.client.eventRevisions.create(
|
|
5
13
|
parent_id,
|
|
6
14
|
ctx.locale
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toDateInput, toDateTimeInput } from "../../utils";
|
|
2
2
|
|
|
3
3
|
const index = async (ctx) => {
|
|
4
4
|
const items = await ctx.client.events.list(
|
|
@@ -26,7 +26,18 @@ const show = async (ctx) => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const revisions = await ctx.client.eventRevisions.all(id, ctx.locale);
|
|
29
|
-
const changes = await ctx.client.eventChanges.list(
|
|
29
|
+
const changes = await ctx.client.eventChanges.list(
|
|
30
|
+
{
|
|
31
|
+
"f:change_type": [
|
|
32
|
+
"revision_publish",
|
|
33
|
+
"revision_lock",
|
|
34
|
+
"revision_unlock",
|
|
35
|
+
"update",
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
id,
|
|
39
|
+
ctx.locale
|
|
40
|
+
);
|
|
30
41
|
|
|
31
42
|
return {
|
|
32
43
|
assigns: {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
const create = async (ctx) => {
|
|
2
2
|
const { parent_id } = ctx.params;
|
|
3
3
|
|
|
4
|
+
const page = await ctx.client.pages.find(parent_id, ctx.locale);
|
|
5
|
+
|
|
6
|
+
if (page.revisionsCount >= ctx.maxOpenRevisions) {
|
|
7
|
+
return {
|
|
8
|
+
redirect: `/${ctx.locale}/${ctx.resourceConfig.pageTypePlural}/${parent_id}?revisions_count_error=1`,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
const { errors, pageErrors } = await ctx.client.pageRevisions.create(
|
|
5
13
|
parent_id,
|
|
6
14
|
ctx.locale
|
|
@@ -25,7 +25,18 @@ const show = async (ctx) => {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const revisions = await ctx.client.pageRevisions.all(id, ctx.locale);
|
|
28
|
-
const changes = await ctx.client.pageChanges.list(
|
|
28
|
+
const changes = await ctx.client.pageChanges.list(
|
|
29
|
+
{
|
|
30
|
+
"f:change_type": [
|
|
31
|
+
"revision_publish",
|
|
32
|
+
"revision_lock",
|
|
33
|
+
"revision_unlock",
|
|
34
|
+
"update",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
id,
|
|
38
|
+
ctx.locale
|
|
39
|
+
);
|
|
29
40
|
|
|
30
41
|
return {
|
|
31
42
|
assigns: {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import ArticleRevisionsHandler from "../../../../handlers/article_revisions/ArticleRevisionsHandler";
|
|
2
1
|
import ArticleRevisions from "../../../../cloud/ArticleRevisions";
|
|
3
|
-
|
|
2
|
+
import Articles from "../../../../cloud/Articles";
|
|
3
|
+
import ArticleRevisionsHandler from "../../../../handlers/article_revisions/ArticleRevisionsHandler";
|
|
4
4
|
import Factory from "../../../support/factory";
|
|
5
5
|
|
|
6
6
|
function buildCtx(articleID) {
|
|
7
7
|
return {
|
|
8
8
|
client: {
|
|
9
9
|
articleRevisions: new ArticleRevisions(() => ({}), "token"),
|
|
10
|
+
articles: new Articles(() => ({}), "token"),
|
|
10
11
|
},
|
|
11
12
|
params: { parent_id: articleID },
|
|
12
13
|
locale: "en",
|
|
@@ -14,66 +15,73 @@ function buildCtx(articleID) {
|
|
|
14
15
|
articleType: "article",
|
|
15
16
|
articleTypePlural: "articles",
|
|
16
17
|
},
|
|
18
|
+
maxOpenRevisions: 1,
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
describe("Article revisions", () => {
|
|
21
23
|
test("create successful", async () => {
|
|
24
|
+
const article = Factory.build("article", { id: 1, revisionsCount: 0 });
|
|
22
25
|
const revision = Factory.build("article_revision", { originalId: 1 });
|
|
23
|
-
const ctx = buildCtx(
|
|
26
|
+
const ctx = buildCtx(article.id);
|
|
27
|
+
|
|
28
|
+
jest.spyOn(ctx.client.articles, "find").mockResolvedValue(article);
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
jest
|
|
26
31
|
.spyOn(ctx.client.articleRevisions, "create")
|
|
27
|
-
.
|
|
28
|
-
revision,
|
|
29
|
-
errors: null,
|
|
30
|
-
articleErrors: null,
|
|
31
|
-
}));
|
|
32
|
+
.mockResolvedValue({ revision, errors: null, articleErrors: null });
|
|
32
33
|
|
|
33
34
|
const result = await ArticleRevisionsHandler.create(ctx);
|
|
34
35
|
|
|
35
|
-
expect(
|
|
36
|
+
expect(result).toEqual({
|
|
37
|
+
redirect: `/en/articles/${article.id}`,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("create failed when there is an opened revision for the article", async () => {
|
|
42
|
+
const article = Factory.build("article", { id: 1, revisionsCount: 2 });
|
|
43
|
+
const ctx = buildCtx(article.id);
|
|
44
|
+
|
|
45
|
+
jest.spyOn(ctx.client.articles, "find").mockResolvedValue(article);
|
|
46
|
+
|
|
47
|
+
const result = await ArticleRevisionsHandler.create(ctx);
|
|
36
48
|
|
|
37
49
|
expect(result).toEqual({
|
|
38
|
-
redirect: `/en/articles/${
|
|
50
|
+
redirect: `/en/articles/${article.id}?revisions_count_error=1`,
|
|
39
51
|
});
|
|
40
52
|
});
|
|
41
53
|
|
|
42
54
|
test("create failed with errors", async () => {
|
|
43
|
-
const
|
|
55
|
+
const article = Factory.build("article", { id: 1, revisionsCount: 0 });
|
|
44
56
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
57
|
+
const ctx = buildCtx(article.id);
|
|
45
58
|
|
|
46
|
-
|
|
47
|
-
.spyOn(ctx.client.articleRevisions, "create")
|
|
48
|
-
.mockImplementation(() => ({
|
|
49
|
-
revision: null,
|
|
50
|
-
errors,
|
|
51
|
-
articleErrors: null,
|
|
52
|
-
}));
|
|
59
|
+
jest.spyOn(ctx.client.articles, "find").mockResolvedValue(article);
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
jest
|
|
62
|
+
.spyOn(ctx.client.articleRevisions, "create")
|
|
63
|
+
.mockResolvedValue({ revision: null, errors, articleErrors: null });
|
|
57
64
|
|
|
58
|
-
expect(
|
|
65
|
+
await expect(ArticleRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
66
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
67
|
+
);
|
|
59
68
|
});
|
|
60
69
|
|
|
61
70
|
test("create failed with article errors", async () => {
|
|
62
|
-
const
|
|
71
|
+
const article = Factory.build("article", { id: 1, revisionsCount: 0 });
|
|
63
72
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
73
|
+
const ctx = buildCtx(article.id);
|
|
64
74
|
|
|
65
|
-
|
|
66
|
-
.spyOn(ctx.client.articleRevisions, "create")
|
|
67
|
-
.mockImplementation(() => ({
|
|
68
|
-
revision: null,
|
|
69
|
-
errors: null,
|
|
70
|
-
articleErrors: errors,
|
|
71
|
-
}));
|
|
75
|
+
jest.spyOn(ctx.client.articles, "find").mockResolvedValue(article);
|
|
72
76
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
jest.spyOn(ctx.client.articleRevisions, "create").mockResolvedValue({
|
|
78
|
+
revision: null,
|
|
79
|
+
errors: null,
|
|
80
|
+
articleErrors: errors,
|
|
81
|
+
});
|
|
76
82
|
|
|
77
|
-
expect(
|
|
83
|
+
await expect(ArticleRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
84
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
85
|
+
);
|
|
78
86
|
});
|
|
79
87
|
});
|
|
@@ -72,7 +72,18 @@ describe("Articles Handler", () => {
|
|
|
72
72
|
|
|
73
73
|
expect(mockedArticleFind).toHaveBeenCalledWith(article.id, "en");
|
|
74
74
|
expect(mockedArticleRevisions).toHaveBeenCalledWith(article.id, "en");
|
|
75
|
-
expect(mockedArticleChanges).toHaveBeenCalledWith(
|
|
75
|
+
expect(mockedArticleChanges).toHaveBeenCalledWith(
|
|
76
|
+
{
|
|
77
|
+
"f:change_type": [
|
|
78
|
+
"revision_publish",
|
|
79
|
+
"revision_lock",
|
|
80
|
+
"revision_unlock",
|
|
81
|
+
"update",
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
article.id,
|
|
85
|
+
"en"
|
|
86
|
+
);
|
|
76
87
|
|
|
77
88
|
expect(result).toEqual({
|
|
78
89
|
assigns: {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import Events from "../../../../cloud/Events";
|
|
1
2
|
import EventRevisionsHandler from "../../../../handlers/event_revisions/EventRevisionsHandler";
|
|
3
|
+
|
|
2
4
|
import EventRevisions from "../../../../cloud/EventRevisions";
|
|
3
5
|
|
|
4
6
|
import Factory from "../../../support/factory";
|
|
@@ -7,6 +9,7 @@ function buildCtx(eventID) {
|
|
|
7
9
|
return {
|
|
8
10
|
client: {
|
|
9
11
|
eventRevisions: new EventRevisions(() => ({}), "token"),
|
|
12
|
+
events: new Events(() => ({}), "token"),
|
|
10
13
|
},
|
|
11
14
|
params: { parent_id: eventID },
|
|
12
15
|
locale: "en",
|
|
@@ -14,66 +17,71 @@ function buildCtx(eventID) {
|
|
|
14
17
|
eventType: "event",
|
|
15
18
|
eventTypePlural: "events",
|
|
16
19
|
},
|
|
20
|
+
maxOpenRevisions: 1,
|
|
17
21
|
};
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
describe("Event revisions", () => {
|
|
21
25
|
test("create successful", async () => {
|
|
26
|
+
const event = Factory.build("event", { id: 1, revisionsCount: 0 });
|
|
22
27
|
const revision = Factory.build("event_revision", { originalId: 1 });
|
|
23
|
-
const ctx = buildCtx(
|
|
28
|
+
const ctx = buildCtx(event.id);
|
|
29
|
+
|
|
30
|
+
jest.spyOn(ctx.client.events, "find").mockResolvedValue(event);
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
jest
|
|
26
33
|
.spyOn(ctx.client.eventRevisions, "create")
|
|
27
|
-
.
|
|
28
|
-
revision,
|
|
29
|
-
errors: null,
|
|
30
|
-
eventErrors: null,
|
|
31
|
-
}));
|
|
34
|
+
.mockResolvedValue({ revision, errors: null, eventErrors: null });
|
|
32
35
|
|
|
33
36
|
const result = await EventRevisionsHandler.create(ctx);
|
|
34
37
|
|
|
35
|
-
expect(
|
|
38
|
+
expect(result).toEqual({
|
|
39
|
+
redirect: `/en/events/${event.id}`,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("create failed when there is an opened revision for the event", async () => {
|
|
44
|
+
const event = Factory.build("event", { id: 1, revisionsCount: 2 });
|
|
45
|
+
const ctx = buildCtx(event.id);
|
|
46
|
+
|
|
47
|
+
jest.spyOn(ctx.client.events, "find").mockResolvedValue(event);
|
|
48
|
+
|
|
49
|
+
const result = await EventRevisionsHandler.create(ctx);
|
|
36
50
|
|
|
37
51
|
expect(result).toEqual({
|
|
38
|
-
redirect: `/en/events/${
|
|
52
|
+
redirect: `/en/events/${event.id}?revisions_count_error=1`,
|
|
39
53
|
});
|
|
40
54
|
});
|
|
41
55
|
|
|
42
56
|
test("create failed with errors", async () => {
|
|
43
|
-
const
|
|
57
|
+
const event = Factory.build("event", { id: 1, revisionsCount: 0 });
|
|
44
58
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
59
|
+
const ctx = buildCtx(event.id);
|
|
45
60
|
|
|
46
|
-
|
|
47
|
-
.spyOn(ctx.client.eventRevisions, "create")
|
|
48
|
-
.mockImplementation(() => ({
|
|
49
|
-
revision: null,
|
|
50
|
-
errors,
|
|
51
|
-
eventErrors: null,
|
|
52
|
-
}));
|
|
61
|
+
jest.spyOn(ctx.client.events, "find").mockResolvedValue(event);
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
jest
|
|
64
|
+
.spyOn(ctx.client.eventRevisions, "create")
|
|
65
|
+
.mockResolvedValue({ revision: null, errors, eventErrors: null });
|
|
57
66
|
|
|
58
|
-
expect(
|
|
67
|
+
await expect(EventRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
68
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
69
|
+
);
|
|
59
70
|
});
|
|
60
71
|
|
|
61
72
|
test("create failed with event errors", async () => {
|
|
62
|
-
const
|
|
73
|
+
const event = Factory.build("event", { id: 1, revisionsCount: 0 });
|
|
63
74
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
75
|
+
const ctx = buildCtx(event.id);
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
.spyOn(ctx.client.eventRevisions, "create")
|
|
67
|
-
.mockImplementation(() => ({
|
|
68
|
-
revision: null,
|
|
69
|
-
errors: null,
|
|
70
|
-
eventErrors: errors,
|
|
71
|
-
}));
|
|
77
|
+
jest.spyOn(ctx.client.events, "find").mockResolvedValue(event);
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
jest
|
|
80
|
+
.spyOn(ctx.client.eventRevisions, "create")
|
|
81
|
+
.mockResolvedValue({ revision: null, errors: null, eventErrors: errors });
|
|
76
82
|
|
|
77
|
-
expect(
|
|
83
|
+
await expect(EventRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
84
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
85
|
+
);
|
|
78
86
|
});
|
|
79
87
|
});
|
|
@@ -72,7 +72,18 @@ describe("Events Handler", () => {
|
|
|
72
72
|
|
|
73
73
|
expect(mockedeventFind).toHaveBeenCalledWith(event.id, "en");
|
|
74
74
|
expect(mockedEventRevisions).toHaveBeenCalledWith(event.id, "en");
|
|
75
|
-
expect(mockedEventChanges).toHaveBeenCalledWith(
|
|
75
|
+
expect(mockedEventChanges).toHaveBeenCalledWith(
|
|
76
|
+
{
|
|
77
|
+
"f:change_type": [
|
|
78
|
+
"revision_publish",
|
|
79
|
+
"revision_lock",
|
|
80
|
+
"revision_unlock",
|
|
81
|
+
"update",
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
event.id,
|
|
85
|
+
"en"
|
|
86
|
+
);
|
|
76
87
|
|
|
77
88
|
expect(result).toEqual({
|
|
78
89
|
assigns: {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import PageRevisionsHandler from "../../../../handlers/page_revisions/PageRevisionsHandler";
|
|
2
1
|
import PageRevisions from "../../../../cloud/PageRevisions";
|
|
3
|
-
|
|
2
|
+
import Pages from "../../../../cloud/Pages";
|
|
3
|
+
import PageRevisionsHandler from "../../../../handlers/page_revisions/PageRevisionsHandler";
|
|
4
4
|
import Factory from "../../../support/factory";
|
|
5
5
|
|
|
6
6
|
function buildCtx(pageID) {
|
|
7
7
|
return {
|
|
8
8
|
client: {
|
|
9
9
|
pageRevisions: new PageRevisions(() => ({}), "token"),
|
|
10
|
+
pages: new Pages(() => ({}), "token"),
|
|
10
11
|
},
|
|
11
12
|
params: { parent_id: pageID },
|
|
12
13
|
locale: "en",
|
|
@@ -14,58 +15,71 @@ function buildCtx(pageID) {
|
|
|
14
15
|
pageType: "page",
|
|
15
16
|
pageTypePlural: "pages",
|
|
16
17
|
},
|
|
18
|
+
maxOpenRevisions: 1,
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
describe("Page revisions", () => {
|
|
21
23
|
test("create successful", async () => {
|
|
24
|
+
const page = Factory.build("page", { id: 1, revisionsCount: 0 });
|
|
22
25
|
const revision = Factory.build("page_revision", { originalId: 1 });
|
|
23
|
-
const ctx = buildCtx(
|
|
26
|
+
const ctx = buildCtx(page.id);
|
|
27
|
+
|
|
28
|
+
jest.spyOn(ctx.client.pages, "find").mockResolvedValue(page);
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
jest
|
|
26
31
|
.spyOn(ctx.client.pageRevisions, "create")
|
|
27
|
-
.
|
|
32
|
+
.mockResolvedValue({ revision, errors: null, pageErrors: null });
|
|
28
33
|
|
|
29
34
|
const result = await PageRevisionsHandler.create(ctx);
|
|
30
35
|
|
|
31
|
-
expect(
|
|
36
|
+
expect(result).toEqual({
|
|
37
|
+
redirect: `/en/pages/${page.id}`,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("create failed when there is an opened revision for the page", async () => {
|
|
42
|
+
const page = Factory.build("page", { id: 1, revisionsCount: 2 });
|
|
43
|
+
const ctx = buildCtx(page.id);
|
|
44
|
+
|
|
45
|
+
jest.spyOn(ctx.client.pages, "find").mockResolvedValue(page);
|
|
46
|
+
|
|
47
|
+
const result = await PageRevisionsHandler.create(ctx);
|
|
32
48
|
|
|
33
49
|
expect(result).toEqual({
|
|
34
|
-
redirect: `/en/pages/${
|
|
50
|
+
redirect: `/en/pages/${page.id}?revisions_count_error=1`,
|
|
35
51
|
});
|
|
36
52
|
});
|
|
37
53
|
|
|
38
54
|
test("create failed with errors", async () => {
|
|
39
|
-
const
|
|
55
|
+
const page = Factory.build("page", { id: 1, revisionsCount: 0 });
|
|
40
56
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
57
|
+
const ctx = buildCtx(page.id);
|
|
41
58
|
|
|
42
|
-
|
|
43
|
-
.spyOn(ctx.client.pageRevisions, "create")
|
|
44
|
-
.mockImplementation(() => ({ revision: null, errors, pageErrors: null }));
|
|
59
|
+
jest.spyOn(ctx.client.pages, "find").mockResolvedValue(page);
|
|
45
60
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
jest
|
|
62
|
+
.spyOn(ctx.client.pageRevisions, "create")
|
|
63
|
+
.mockResolvedValue({ revision: null, errors, pageErrors: null });
|
|
49
64
|
|
|
50
|
-
expect(
|
|
65
|
+
await expect(PageRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
66
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
67
|
+
);
|
|
51
68
|
});
|
|
52
69
|
|
|
53
70
|
test("create failed with page errors", async () => {
|
|
54
|
-
const
|
|
71
|
+
const page = Factory.build("page", { id: 1, revisionsCount: 0 });
|
|
55
72
|
const errors = [{ key: "generalError", message: "something went wrong" }];
|
|
73
|
+
const ctx = buildCtx(page.id);
|
|
56
74
|
|
|
57
|
-
|
|
58
|
-
.spyOn(ctx.client.pageRevisions, "create")
|
|
59
|
-
.mockImplementation(() => ({
|
|
60
|
-
revision: null,
|
|
61
|
-
errors: null,
|
|
62
|
-
pageErrors: errors,
|
|
63
|
-
}));
|
|
75
|
+
jest.spyOn(ctx.client.pages, "find").mockResolvedValue(page);
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
jest
|
|
78
|
+
.spyOn(ctx.client.pageRevisions, "create")
|
|
79
|
+
.mockResolvedValue({ revision: null, errors: null, pageErrors: errors });
|
|
68
80
|
|
|
69
|
-
expect(
|
|
81
|
+
await expect(PageRevisionsHandler.create(ctx)).rejects.toThrow(
|
|
82
|
+
`Failed to start revision: ${JSON.stringify(errors)}`
|
|
83
|
+
);
|
|
70
84
|
});
|
|
71
85
|
});
|
|
@@ -70,7 +70,18 @@ describe("Pages Handler", () => {
|
|
|
70
70
|
|
|
71
71
|
expect(mockedPageFind).toHaveBeenCalledWith(page.id, "en");
|
|
72
72
|
expect(mockedPageRevisions).toHaveBeenCalledWith(page.id, "en");
|
|
73
|
-
expect(mockedPageChanges).toHaveBeenCalledWith(
|
|
73
|
+
expect(mockedPageChanges).toHaveBeenCalledWith(
|
|
74
|
+
{
|
|
75
|
+
"f:change_type": [
|
|
76
|
+
"revision_publish",
|
|
77
|
+
"revision_lock",
|
|
78
|
+
"revision_unlock",
|
|
79
|
+
"update",
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
page.id,
|
|
83
|
+
"en"
|
|
84
|
+
);
|
|
74
85
|
|
|
75
86
|
expect(result).toEqual({
|
|
76
87
|
assigns: {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/${_context.resourceConfig.pageType}_changes/reverts` %>" class="inline-block">
|
|
3
3
|
<input type="hidden" name="version" value="<%= change.version %>">
|
|
4
4
|
|
|
5
|
-
<% if (history.isChangeRevertable(change, maxVersion)) { %>
|
|
5
|
+
<% if (history.isChangeRevertable(change, maxVersion) && !(resource.revisionsCount >= _context.maxOpenRevisions)) { %>
|
|
6
6
|
<button type="submit" class="btn-text">
|
|
7
7
|
Revert
|
|
8
8
|
</button>
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
|
|
22
22
|
Preview
|
|
23
23
|
</a>
|
|
24
|
-
</div>
|
|
24
|
+
</div>
|
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
<div
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<div>
|
|
2
|
+
<div class="actions-toolbar">
|
|
3
|
+
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/${_context.resourceConfig.articleType}_revisions` %>" class="inline-block">
|
|
4
|
+
<% if (resource.revisionsCount >= _context?.maxOpenRevisions) { %>
|
|
5
|
+
<button type="submit" class="btn-primary" disabled style="opacity: 50%;">
|
|
6
|
+
Start revision
|
|
7
|
+
</button>
|
|
8
|
+
<% } else {%>
|
|
9
|
+
<button type="submit" class="btn-primary">
|
|
10
|
+
Start revision
|
|
11
|
+
</button>
|
|
12
|
+
<% } %>
|
|
13
|
+
</form>
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/urls/new` %>" class="btn-text has-icon">
|
|
16
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
17
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
|
|
18
|
+
</svg>
|
|
19
|
+
|
|
20
|
+
Change URL
|
|
21
|
+
</a>
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
<form action="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/clones` %>" method="post" class="inline-block">
|
|
24
|
+
<button type="submit" class="btn-text has-icon">
|
|
25
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
26
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" />
|
|
27
|
+
</svg>
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
Clone <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.articleType)) %>
|
|
30
|
+
</button>
|
|
31
|
+
</form>
|
|
32
|
+
|
|
33
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
18
34
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
19
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="
|
|
35
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
|
20
36
|
</svg>
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
</
|
|
24
|
-
</
|
|
25
|
-
|
|
26
|
-
<a href="<%= `/${_context.locale}/${_context.resourceConfig.articleTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
27
|
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
28
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
|
29
|
-
</svg>
|
|
38
|
+
Delete <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.articleType)) %>
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
</
|
|
33
|
-
|
|
42
|
+
<% if (_context.query?.revisions_count_error) { %>
|
|
43
|
+
<div class="text-red-600">Cannot open a new revision. Current limit is <%= _context.maxOpenRevisions %>. </div>
|
|
44
|
+
<% } %>
|
|
45
|
+
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (changes && changes.list.length > 0) { %>
|
|
1
2
|
<% const maxVersion = history.getMaxVersion(changes); %>
|
|
2
3
|
|
|
3
4
|
<div class="mb-4 history-table-wrapper">
|
|
@@ -66,3 +67,8 @@
|
|
|
66
67
|
<%- pagination(changes.pagination, _context) %>
|
|
67
68
|
<% } %>
|
|
68
69
|
</div>
|
|
70
|
+
<% } else { %>
|
|
71
|
+
<div class="history-table-wrapper mb-4">
|
|
72
|
+
<p class="text-center py-4">No changes, yet.</p>
|
|
73
|
+
</div>
|
|
74
|
+
<% } %>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/${_context.resourceConfig.pageType}_changes/reverts` %>" class="inline-block">
|
|
3
3
|
<input type="hidden" name="version" value="<%= change.version %>">
|
|
4
4
|
|
|
5
|
-
<% if (history.isChangeRevertable(change, maxVersion)) { %>
|
|
5
|
+
<% if (history.isChangeRevertable(change, maxVersion) && !(resource.revisionsCount >= _context.maxOpenRevisions)) { %>
|
|
6
6
|
<button type="submit" class="btn-text">
|
|
7
7
|
Revert
|
|
8
8
|
</button>
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
|
|
22
22
|
Preview
|
|
23
23
|
</a>
|
|
24
|
-
</div>
|
|
24
|
+
</div>
|
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
<div
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<div>
|
|
2
|
+
<div class="actions-toolbar">
|
|
3
|
+
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/${_context.resourceConfig.eventType}_revisions` %>" class="inline-block">
|
|
4
|
+
<% if (resource.revisionsCount >= _context?.maxOpenRevisions) { %>
|
|
5
|
+
<button type="submit" class="btn-primary" disabled style="opacity: 50%;">
|
|
6
|
+
Start revision
|
|
7
|
+
</button>
|
|
8
|
+
<% } else {%>
|
|
9
|
+
<button type="submit" class="btn-primary">
|
|
10
|
+
Start revision
|
|
11
|
+
</button>
|
|
12
|
+
<% } %>
|
|
13
|
+
</form>
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/urls/new` %>" class="btn-text has-icon">
|
|
16
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
17
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
|
|
18
|
+
</svg>
|
|
19
|
+
|
|
20
|
+
Change URL
|
|
21
|
+
</a>
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
<form action="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/clones` %>" method="post" class="inline-block">
|
|
24
|
+
<button type="submit" class="btn-text has-icon">
|
|
25
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
26
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" />
|
|
27
|
+
</svg>
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
Clone <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.eventType)) %>
|
|
30
|
+
</button>
|
|
31
|
+
</form>
|
|
32
|
+
|
|
33
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
18
34
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
19
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="
|
|
35
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
|
20
36
|
</svg>
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
</
|
|
24
|
-
</
|
|
25
|
-
|
|
26
|
-
<a href="<%= `/${_context.locale}/${_context.resourceConfig.eventTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
27
|
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
28
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
|
29
|
-
</svg>
|
|
38
|
+
Delete <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.eventType)) %>
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
</
|
|
33
|
-
|
|
42
|
+
<% if (_context.query?.revisions_count_error) { %>
|
|
43
|
+
<div class="text-red-600">Cannot open a new revision. Current limit is <%= _context.maxOpenRevisions %>. </div>
|
|
44
|
+
<% } %>
|
|
45
|
+
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (changes && changes.list.length > 0) { %>
|
|
1
2
|
<% const maxVersion = history.getMaxVersion(changes); %>
|
|
2
3
|
|
|
3
4
|
<div class="history-table-wrapper mb-4">
|
|
@@ -66,3 +67,8 @@
|
|
|
66
67
|
<%- pagination(changes.pagination, _context) %>
|
|
67
68
|
<% } %>
|
|
68
69
|
</div>
|
|
70
|
+
<% } else { %>
|
|
71
|
+
<div class="history-table-wrapper mb-4">
|
|
72
|
+
<p class="text-center py-4">No changes, yet.</p>
|
|
73
|
+
</div>
|
|
74
|
+
<% } %>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/${_context.resourceConfig.pageType}_changes/reverts` %>" class="inline-block">
|
|
3
3
|
<input type="hidden" name="version" value="<%= change.version %>">
|
|
4
4
|
|
|
5
|
-
<% if (history.isChangeRevertable(change, maxVersion)) { %>
|
|
5
|
+
<% if (history.isChangeRevertable(change, maxVersion) && !(resource.revisionsCount >= _context.maxOpenRevisions)) { %>
|
|
6
6
|
<button type="submit" class="btn-text">
|
|
7
7
|
Revert
|
|
8
8
|
</button>
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
|
|
22
22
|
Preview
|
|
23
23
|
</a>
|
|
24
|
-
</div>
|
|
24
|
+
</div>
|
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
<div
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<div>
|
|
2
|
+
<div class="actions-toolbar">
|
|
3
|
+
<form method="POST" action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/${_context.resourceConfig.pageType}_revisions` %>" class="inline-block">
|
|
4
|
+
<% if (resource.revisionsCount >= _context?.maxOpenRevisions) { %>
|
|
5
|
+
<button type="submit" class="btn-primary" disabled style="opacity: 50%;">
|
|
6
|
+
Start revision
|
|
7
|
+
</button>
|
|
8
|
+
<% } else {%>
|
|
9
|
+
<button type="submit" class="btn-primary">
|
|
10
|
+
Start revision
|
|
11
|
+
</button>
|
|
12
|
+
<% } %>
|
|
13
|
+
</form>
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/urls/new` %>" class="btn-text has-icon">
|
|
16
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
17
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
|
|
18
|
+
</svg>
|
|
19
|
+
|
|
20
|
+
Change URL
|
|
21
|
+
</a>
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
<form action="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/clones` %>" method="post" class="inline-block">
|
|
24
|
+
<button type="submit" class="btn-text has-icon">
|
|
25
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
26
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" />
|
|
27
|
+
</svg>
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
Clone <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.pageType)) %>
|
|
30
|
+
</button>
|
|
31
|
+
</form>
|
|
32
|
+
|
|
33
|
+
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
18
34
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
19
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="
|
|
35
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
|
20
36
|
</svg>
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
</
|
|
24
|
-
</
|
|
25
|
-
|
|
26
|
-
<a href="<%= `/${_context.locale}/${_context.resourceConfig.pageTypePlural}/${resource.id}/deletes/new` %>" class="btn-danger has-icon">
|
|
27
|
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
28
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
|
29
|
-
</svg>
|
|
38
|
+
Delete <%= Inflector.humanize(Inflector.downcase(_context.resourceConfig.pageType)) %>
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
</
|
|
33
|
-
|
|
42
|
+
<% if (_context.query?.revisions_count_error) { %>
|
|
43
|
+
<div class="text-red-600">Cannot open a new revision. Current limit is <%= _context.maxOpenRevisions %>. </div>
|
|
44
|
+
<% } %>
|
|
45
|
+
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<% if (changes && changes.list.length > 0) { %>
|
|
1
2
|
<% const maxVersion = history.getMaxVersion(changes); %>
|
|
2
3
|
|
|
3
4
|
<div class="history-table-wrapper mb-4">
|
|
@@ -66,3 +67,8 @@
|
|
|
66
67
|
<%- pagination(changes.pagination, _context) %>
|
|
67
68
|
<% } %>
|
|
68
69
|
</div>
|
|
70
|
+
<% } else { %>
|
|
71
|
+
<div class="history-table-wrapper mb-4">
|
|
72
|
+
<p class="text-center py-4">No changes, yet.</p>
|
|
73
|
+
</div>
|
|
74
|
+
<% } %>
|