@micelio/strapi-plugin-core 0.1.0 → 0.2.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/README.md +5 -3
- package/dist/admin/MicelioLayout-Bmu5xCw9.js +1023 -0
- package/dist/admin/MicelioLayout-DMMsKnMo.mjs +1021 -0
- package/dist/admin/PreviewHeaderAction-BaduMvYy.mjs +61 -0
- package/dist/admin/PreviewHeaderAction-CGHycv4I.js +61 -0
- package/dist/admin/{en-D-T8z3qh.js → en-BDVuq0fN.js} +37 -1
- package/dist/admin/{en-C2O-S2ym.mjs → en-Bahc6y6N.mjs} +37 -1
- package/dist/admin/{es-RV0vkOHh.js → es-DStBGkm7.js} +37 -1
- package/dist/admin/{es-CQ2tv9N8.mjs → es-nUHBIoZG.mjs} +37 -1
- package/dist/admin/{index-D3TGbaQ7.js → index-D_Dbvo2F.js} +8 -2
- package/dist/admin/{index-CiSujTES.mjs → index-Dwr15ZsB.mjs} +8 -2
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/permissions-BAI0qDr9.js +24 -0
- package/dist/admin/permissions-CYirM30e.mjs +25 -0
- package/dist/admin/src/components/EyeIcon.d.ts +8 -0
- package/dist/admin/src/components/PreviewFrame.d.ts +47 -0
- package/dist/admin/src/components/PreviewHeaderAction.d.ts +28 -0
- package/dist/admin/src/hooks/useEntry.d.ts +31 -0
- package/dist/admin/src/hooks/usePreviewSource.d.ts +31 -0
- package/dist/admin/src/pages/MicelioLayout.d.ts +2 -1
- package/dist/admin/src/pages/PreviewPage.d.ts +2 -0
- package/dist/admin/src/permissions.d.ts +5 -0
- package/dist/admin/src/translations/en.json.d.ts +37 -1
- package/dist/admin/src/utils/fetchError.d.ts +2 -0
- package/dist/admin/src/utils/previewAction.d.ts +22 -0
- package/dist/admin/src/utils/previewPane.d.ts +59 -0
- package/dist/server/index.js +796 -105
- package/dist/server/index.mjs +796 -105
- package/dist/server/src/constants/preview.d.ts +2 -0
- package/dist/server/src/constants/taxonomy.d.ts +13 -0
- package/dist/server/src/content-types/category/schema.json.d.ts +78 -0
- package/dist/server/src/content-types/index.d.ts +125 -2
- package/dist/server/src/content-types/preview-credential/schema.json.d.ts +7 -6
- package/dist/server/src/content-types/tag/schema.json.d.ts +46 -0
- package/dist/server/src/controllers/index.d.ts +6 -0
- package/dist/server/src/editorial/transitions.d.ts +5 -2
- package/dist/server/src/index.d.ts +140 -2
- package/dist/server/src/migrations/rename-legacy-uids.d.ts +26 -0
- package/dist/server/src/migrations/site-backfill.d.ts +13 -0
- package/dist/server/src/preview/credentials.d.ts +7 -5
- package/dist/server/src/preview/draft-query.d.ts +62 -0
- package/dist/server/src/preview/revocation.d.ts +9 -1
- package/dist/server/src/preview/shortcut.d.ts +24 -0
- package/dist/server/src/routes/index.d.ts +2 -0
- package/dist/server/src/routes/taxonomy.d.ts +12 -0
- package/dist/server/src/services/index.d.ts +6 -0
- package/dist/server/src/services/preview.d.ts +25 -6
- package/dist/server/src/site-scope/documents.d.ts +13 -3
- package/dist/server/src/types/hooks.d.ts +5 -4
- package/dist/server/src/types/preview.d.ts +35 -10
- package/package.json +4 -3
- package/dist/admin/MicelioLayout-3sKGPS_0.mjs +0 -443
- package/dist/admin/MicelioLayout-DXzRS4l3.js +0 -443
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { unstable_useContentManagerContext, useAuth, useForm } from "@strapi/strapi/admin";
|
|
3
|
+
import { useNavigate } from "react-router-dom";
|
|
4
|
+
import { u as useMessages, P as PREVIEW_PERMISSION } from "./permissions-CYirM30e.mjs";
|
|
5
|
+
import { P as PLUGIN_ID } from "./index-Dwr15ZsB.mjs";
|
|
6
|
+
function hasPreview(schema) {
|
|
7
|
+
if (schema?.pluginOptions?.micelio?.siteScoped !== true) return false;
|
|
8
|
+
return Object.values(schema.attributes ?? {}).some(
|
|
9
|
+
(attribute) => attribute?.type === "dynamiczone"
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
function previewPagePath(uid, documentId, locale) {
|
|
13
|
+
const page = `/plugins/${PLUGIN_ID}/preview/${encodeURIComponent(uid)}/${encodeURIComponent(documentId)}`;
|
|
14
|
+
return locale ? `${page}?locale=${encodeURIComponent(locale)}` : page;
|
|
15
|
+
}
|
|
16
|
+
const EyeIcon = (props) => /* @__PURE__ */ jsxs(
|
|
17
|
+
"svg",
|
|
18
|
+
{
|
|
19
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
20
|
+
width: "1.6rem",
|
|
21
|
+
height: "1.6rem",
|
|
22
|
+
viewBox: "0 0 24 24",
|
|
23
|
+
fill: "none",
|
|
24
|
+
stroke: "currentColor",
|
|
25
|
+
strokeWidth: "1.75",
|
|
26
|
+
strokeLinecap: "round",
|
|
27
|
+
strokeLinejoin: "round",
|
|
28
|
+
"aria-hidden": "true",
|
|
29
|
+
focusable: "false",
|
|
30
|
+
...props,
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ jsx("path", { style: { fill: "none" }, d: "M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z" }),
|
|
33
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
function PreviewHeaderAction({
|
|
38
|
+
collectionType,
|
|
39
|
+
documentId,
|
|
40
|
+
document
|
|
41
|
+
}) {
|
|
42
|
+
const { t } = useMessages();
|
|
43
|
+
const navigate = useNavigate();
|
|
44
|
+
const { contentType } = unstable_useContentManagerContext();
|
|
45
|
+
const permissions = useAuth("PreviewHeaderAction", (state) => state.permissions);
|
|
46
|
+
const isModified = useForm("PreviewHeaderAction", (state) => state.modified);
|
|
47
|
+
const mayPreview = permissions.some(({ action }) => action === PREVIEW_PERMISSION.action);
|
|
48
|
+
if (collectionType !== "collection-types" || !documentId || !mayPreview || !hasPreview(contentType)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const uid = contentType?.uid ?? "";
|
|
52
|
+
return {
|
|
53
|
+
label: t(isModified ? "action.preview.unsaved" : "action.preview"),
|
|
54
|
+
icon: /* @__PURE__ */ jsx(EyeIcon, {}),
|
|
55
|
+
disabled: Boolean(isModified),
|
|
56
|
+
onClick: () => navigate(previewPagePath(uid, documentId, document?.locale ?? null))
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
PreviewHeaderAction
|
|
61
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const admin = require("@strapi/strapi/admin");
|
|
5
|
+
const reactRouterDom = require("react-router-dom");
|
|
6
|
+
const permissions = require("./permissions-BAI0qDr9.js");
|
|
7
|
+
const index = require("./index-D_Dbvo2F.js");
|
|
8
|
+
function hasPreview(schema) {
|
|
9
|
+
if (schema?.pluginOptions?.micelio?.siteScoped !== true) return false;
|
|
10
|
+
return Object.values(schema.attributes ?? {}).some(
|
|
11
|
+
(attribute) => attribute?.type === "dynamiczone"
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
function previewPagePath(uid, documentId, locale) {
|
|
15
|
+
const page = `/plugins/${index.PLUGIN_ID}/preview/${encodeURIComponent(uid)}/${encodeURIComponent(documentId)}`;
|
|
16
|
+
return locale ? `${page}?locale=${encodeURIComponent(locale)}` : page;
|
|
17
|
+
}
|
|
18
|
+
const EyeIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
19
|
+
"svg",
|
|
20
|
+
{
|
|
21
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
22
|
+
width: "1.6rem",
|
|
23
|
+
height: "1.6rem",
|
|
24
|
+
viewBox: "0 0 24 24",
|
|
25
|
+
fill: "none",
|
|
26
|
+
stroke: "currentColor",
|
|
27
|
+
strokeWidth: "1.75",
|
|
28
|
+
strokeLinecap: "round",
|
|
29
|
+
strokeLinejoin: "round",
|
|
30
|
+
"aria-hidden": "true",
|
|
31
|
+
focusable: "false",
|
|
32
|
+
...props,
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { style: { fill: "none" }, d: "M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z" }),
|
|
35
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "3" })
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
function PreviewHeaderAction({
|
|
40
|
+
collectionType,
|
|
41
|
+
documentId,
|
|
42
|
+
document
|
|
43
|
+
}) {
|
|
44
|
+
const { t } = permissions.useMessages();
|
|
45
|
+
const navigate = reactRouterDom.useNavigate();
|
|
46
|
+
const { contentType } = admin.unstable_useContentManagerContext();
|
|
47
|
+
const permissions$1 = admin.useAuth("PreviewHeaderAction", (state) => state.permissions);
|
|
48
|
+
const isModified = admin.useForm("PreviewHeaderAction", (state) => state.modified);
|
|
49
|
+
const mayPreview = permissions$1.some(({ action }) => action === permissions.PREVIEW_PERMISSION.action);
|
|
50
|
+
if (collectionType !== "collection-types" || !documentId || !mayPreview || !hasPreview(contentType)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const uid = contentType?.uid ?? "";
|
|
54
|
+
return {
|
|
55
|
+
label: t(isModified ? "action.preview.unsaved" : "action.preview"),
|
|
56
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(EyeIcon, {}),
|
|
57
|
+
disabled: Boolean(isModified),
|
|
58
|
+
onClick: () => navigate(previewPagePath(uid, documentId, document?.locale ?? null))
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
exports.PreviewHeaderAction = PreviewHeaderAction;
|
|
@@ -92,6 +92,42 @@ const en = {
|
|
|
92
92
|
"status.plugins.fix": "Fix",
|
|
93
93
|
"status.diagnostics.copy": "Copy diagnostics",
|
|
94
94
|
"status.diagnostics.copied": "The diagnostics were copied.",
|
|
95
|
-
"status.diagnostics.failed": "The diagnostics could not be copied."
|
|
95
|
+
"status.diagnostics.failed": "The diagnostics could not be copied.",
|
|
96
|
+
"preview.title": "Preview",
|
|
97
|
+
"preview.page": "Preview of {title}",
|
|
98
|
+
"preview.subtitle": "The saved draft, as the site renders it.",
|
|
99
|
+
"preview.back": "Back to the entry",
|
|
100
|
+
"preview.pane": "Preview",
|
|
101
|
+
"preview.entry.title": "Entry",
|
|
102
|
+
"preview.entry.readOnly": "Read-only",
|
|
103
|
+
"preview.entry.body": "The form is in the Content Manager: open it in another tab, edit and save the entry there. This page shows the saved draft and loads it again after each save.",
|
|
104
|
+
"preview.entry.bodyReadOnly": "You may read this entry but not edit it. The preview shows its saved draft.",
|
|
105
|
+
"preview.entry.locale": "Locale: {locale}",
|
|
106
|
+
"preview.entry.saved": "Draft saved {date}",
|
|
107
|
+
"preview.entry.open": "Open in the Content Manager, in a new tab",
|
|
108
|
+
"preview.entry.view": "View in the Content Manager, in a new tab",
|
|
109
|
+
"preview.device.group": "Preview width",
|
|
110
|
+
"preview.device.phone": "Phone",
|
|
111
|
+
"preview.device.tablet": "Tablet",
|
|
112
|
+
"preview.device.desktop": "Desktop",
|
|
113
|
+
"preview.static": "As static site",
|
|
114
|
+
"preview.locale": "Locale",
|
|
115
|
+
"preview.reload": "Reload",
|
|
116
|
+
"preview.updating": "Updating the preview",
|
|
117
|
+
"preview.shortcuts": "Shortcuts: Ctrl+Shift+1, 2 and 3 for phone, tablet and desktop; Ctrl+Shift+S for the static site; Ctrl+Shift+Left Arrow back to the entry. On a Mac use Command instead of Ctrl. They do not work while you type in a field.",
|
|
118
|
+
"preview.frame.title": "Preview of {title}",
|
|
119
|
+
"preview.blocked.title": "The admin does not allow this site in a frame",
|
|
120
|
+
"preview.blocked.body": "The Content Security Policy of the admin does not list {origin} in frame-src, so the browser refuses to show it. Add it to frame-src in the strapi::security config of the Strapi project and restart it. The host check on the Micelio status page reports it too.",
|
|
121
|
+
"preview.upgrade.title": "Upgrade the frontend",
|
|
122
|
+
"preview.upgrade.body": "The site's frontend speaks the preview protocol versions {versions}, and this plugin speaks none of them. The preview stays plain: it shows the draft and loads it again as a whole after each save.",
|
|
123
|
+
"preview.error.entry": "The entry could not be loaded.",
|
|
124
|
+
"preview.error.missing": "This entry does not exist.",
|
|
125
|
+
"preview.error.unavailable": "This site has no preview: set its url in the Content Manager (Site), and make sure the frontend runs there.",
|
|
126
|
+
"preview.error.forbidden": "You may not preview this entry. It needs the permission to preview drafts and the permission to read the entry.",
|
|
127
|
+
"preview.error.unsupported": "This type has no preview: its entries do not belong to a site.",
|
|
128
|
+
"preview.error.busy": "Too many preview requests. Wait a minute, then reload.",
|
|
129
|
+
"preview.error.failed": "The preview could not load. Reload to try again.",
|
|
130
|
+
"action.preview": "Preview",
|
|
131
|
+
"action.preview.unsaved": "Preview: save your changes first"
|
|
96
132
|
};
|
|
97
133
|
exports.default = en;
|
|
@@ -90,7 +90,43 @@ const en = {
|
|
|
90
90
|
"status.plugins.fix": "Fix",
|
|
91
91
|
"status.diagnostics.copy": "Copy diagnostics",
|
|
92
92
|
"status.diagnostics.copied": "The diagnostics were copied.",
|
|
93
|
-
"status.diagnostics.failed": "The diagnostics could not be copied."
|
|
93
|
+
"status.diagnostics.failed": "The diagnostics could not be copied.",
|
|
94
|
+
"preview.title": "Preview",
|
|
95
|
+
"preview.page": "Preview of {title}",
|
|
96
|
+
"preview.subtitle": "The saved draft, as the site renders it.",
|
|
97
|
+
"preview.back": "Back to the entry",
|
|
98
|
+
"preview.pane": "Preview",
|
|
99
|
+
"preview.entry.title": "Entry",
|
|
100
|
+
"preview.entry.readOnly": "Read-only",
|
|
101
|
+
"preview.entry.body": "The form is in the Content Manager: open it in another tab, edit and save the entry there. This page shows the saved draft and loads it again after each save.",
|
|
102
|
+
"preview.entry.bodyReadOnly": "You may read this entry but not edit it. The preview shows its saved draft.",
|
|
103
|
+
"preview.entry.locale": "Locale: {locale}",
|
|
104
|
+
"preview.entry.saved": "Draft saved {date}",
|
|
105
|
+
"preview.entry.open": "Open in the Content Manager, in a new tab",
|
|
106
|
+
"preview.entry.view": "View in the Content Manager, in a new tab",
|
|
107
|
+
"preview.device.group": "Preview width",
|
|
108
|
+
"preview.device.phone": "Phone",
|
|
109
|
+
"preview.device.tablet": "Tablet",
|
|
110
|
+
"preview.device.desktop": "Desktop",
|
|
111
|
+
"preview.static": "As static site",
|
|
112
|
+
"preview.locale": "Locale",
|
|
113
|
+
"preview.reload": "Reload",
|
|
114
|
+
"preview.updating": "Updating the preview",
|
|
115
|
+
"preview.shortcuts": "Shortcuts: Ctrl+Shift+1, 2 and 3 for phone, tablet and desktop; Ctrl+Shift+S for the static site; Ctrl+Shift+Left Arrow back to the entry. On a Mac use Command instead of Ctrl. They do not work while you type in a field.",
|
|
116
|
+
"preview.frame.title": "Preview of {title}",
|
|
117
|
+
"preview.blocked.title": "The admin does not allow this site in a frame",
|
|
118
|
+
"preview.blocked.body": "The Content Security Policy of the admin does not list {origin} in frame-src, so the browser refuses to show it. Add it to frame-src in the strapi::security config of the Strapi project and restart it. The host check on the Micelio status page reports it too.",
|
|
119
|
+
"preview.upgrade.title": "Upgrade the frontend",
|
|
120
|
+
"preview.upgrade.body": "The site's frontend speaks the preview protocol versions {versions}, and this plugin speaks none of them. The preview stays plain: it shows the draft and loads it again as a whole after each save.",
|
|
121
|
+
"preview.error.entry": "The entry could not be loaded.",
|
|
122
|
+
"preview.error.missing": "This entry does not exist.",
|
|
123
|
+
"preview.error.unavailable": "This site has no preview: set its url in the Content Manager (Site), and make sure the frontend runs there.",
|
|
124
|
+
"preview.error.forbidden": "You may not preview this entry. It needs the permission to preview drafts and the permission to read the entry.",
|
|
125
|
+
"preview.error.unsupported": "This type has no preview: its entries do not belong to a site.",
|
|
126
|
+
"preview.error.busy": "Too many preview requests. Wait a minute, then reload.",
|
|
127
|
+
"preview.error.failed": "The preview could not load. Reload to try again.",
|
|
128
|
+
"action.preview": "Preview",
|
|
129
|
+
"action.preview.unsaved": "Preview: save your changes first"
|
|
94
130
|
};
|
|
95
131
|
export {
|
|
96
132
|
en as default
|
|
@@ -92,6 +92,42 @@ const es = {
|
|
|
92
92
|
"status.plugins.fix": "Solución",
|
|
93
93
|
"status.diagnostics.copy": "Copiar diagnóstico",
|
|
94
94
|
"status.diagnostics.copied": "Se copió el diagnóstico.",
|
|
95
|
-
"status.diagnostics.failed": "No se pudo copiar el diagnóstico."
|
|
95
|
+
"status.diagnostics.failed": "No se pudo copiar el diagnóstico.",
|
|
96
|
+
"preview.title": "Vista previa",
|
|
97
|
+
"preview.page": "Vista previa de {title}",
|
|
98
|
+
"preview.subtitle": "El borrador guardado, tal como lo muestra el sitio.",
|
|
99
|
+
"preview.back": "Volver a la entrada",
|
|
100
|
+
"preview.pane": "Vista previa",
|
|
101
|
+
"preview.entry.title": "Entrada",
|
|
102
|
+
"preview.entry.readOnly": "Solo lectura",
|
|
103
|
+
"preview.entry.body": "El formulario está en el Gestor de contenido: ábrelo en otra pestaña, edita y guarda la entrada allí. Esta página muestra el borrador guardado y lo carga de nuevo tras cada guardado.",
|
|
104
|
+
"preview.entry.bodyReadOnly": "Puedes leer esta entrada pero no editarla. La vista previa muestra su borrador guardado.",
|
|
105
|
+
"preview.entry.locale": "Idioma: {locale}",
|
|
106
|
+
"preview.entry.saved": "Borrador guardado {date}",
|
|
107
|
+
"preview.entry.open": "Abrir en el Gestor de contenido, en una pestaña nueva",
|
|
108
|
+
"preview.entry.view": "Ver en el Gestor de contenido, en una pestaña nueva",
|
|
109
|
+
"preview.device.group": "Ancho de la vista previa",
|
|
110
|
+
"preview.device.phone": "Móvil",
|
|
111
|
+
"preview.device.tablet": "Tableta",
|
|
112
|
+
"preview.device.desktop": "Escritorio",
|
|
113
|
+
"preview.static": "Como sitio estático",
|
|
114
|
+
"preview.locale": "Idioma",
|
|
115
|
+
"preview.reload": "Recargar",
|
|
116
|
+
"preview.updating": "Actualizando la vista previa",
|
|
117
|
+
"preview.shortcuts": "Atajos: Ctrl+Mayús+1, 2 y 3 para móvil, tableta y escritorio; Ctrl+Mayús+S para el sitio estático; Ctrl+Mayús+Flecha izquierda para volver a la entrada. En Mac usa Comando en lugar de Ctrl. No funcionan mientras escribes en un campo.",
|
|
118
|
+
"preview.frame.title": "Vista previa de {title}",
|
|
119
|
+
"preview.blocked.title": "El panel no permite este sitio en un marco",
|
|
120
|
+
"preview.blocked.body": "La política de seguridad de contenido del panel no incluye {origin} en frame-src, así que el navegador se niega a mostrarlo. Añádelo a frame-src en la configuración de strapi::security del proyecto Strapi y reinícialo. La comprobación del host en la página de estado de Micelio también lo indica.",
|
|
121
|
+
"preview.upgrade.title": "Actualiza el frontend",
|
|
122
|
+
"preview.upgrade.body": "El frontend del sitio habla las versiones {versions} del protocolo de vista previa y este plugin no habla ninguna. La vista previa sigue siendo simple: muestra el borrador y lo carga de nuevo entero tras cada guardado.",
|
|
123
|
+
"preview.error.entry": "No se pudo cargar la entrada.",
|
|
124
|
+
"preview.error.missing": "Esta entrada no existe.",
|
|
125
|
+
"preview.error.unavailable": "Este sitio no tiene vista previa: define su url en el Gestor de contenido (Sitio) y comprueba que el frontend funciona allí.",
|
|
126
|
+
"preview.error.forbidden": "No puedes previsualizar esta entrada. Hace falta el permiso para previsualizar borradores y el permiso para leer la entrada.",
|
|
127
|
+
"preview.error.unsupported": "Este tipo no tiene vista previa: sus entradas no pertenecen a un sitio.",
|
|
128
|
+
"preview.error.busy": "Demasiadas solicitudes de vista previa. Espera un minuto y recarga.",
|
|
129
|
+
"preview.error.failed": "No se pudo cargar la vista previa. Recarga para intentarlo de nuevo.",
|
|
130
|
+
"action.preview": "Vista previa",
|
|
131
|
+
"action.preview.unsaved": "Vista previa: guarda primero los cambios"
|
|
96
132
|
};
|
|
97
133
|
exports.default = es;
|
|
@@ -90,7 +90,43 @@ const es = {
|
|
|
90
90
|
"status.plugins.fix": "Solución",
|
|
91
91
|
"status.diagnostics.copy": "Copiar diagnóstico",
|
|
92
92
|
"status.diagnostics.copied": "Se copió el diagnóstico.",
|
|
93
|
-
"status.diagnostics.failed": "No se pudo copiar el diagnóstico."
|
|
93
|
+
"status.diagnostics.failed": "No se pudo copiar el diagnóstico.",
|
|
94
|
+
"preview.title": "Vista previa",
|
|
95
|
+
"preview.page": "Vista previa de {title}",
|
|
96
|
+
"preview.subtitle": "El borrador guardado, tal como lo muestra el sitio.",
|
|
97
|
+
"preview.back": "Volver a la entrada",
|
|
98
|
+
"preview.pane": "Vista previa",
|
|
99
|
+
"preview.entry.title": "Entrada",
|
|
100
|
+
"preview.entry.readOnly": "Solo lectura",
|
|
101
|
+
"preview.entry.body": "El formulario está en el Gestor de contenido: ábrelo en otra pestaña, edita y guarda la entrada allí. Esta página muestra el borrador guardado y lo carga de nuevo tras cada guardado.",
|
|
102
|
+
"preview.entry.bodyReadOnly": "Puedes leer esta entrada pero no editarla. La vista previa muestra su borrador guardado.",
|
|
103
|
+
"preview.entry.locale": "Idioma: {locale}",
|
|
104
|
+
"preview.entry.saved": "Borrador guardado {date}",
|
|
105
|
+
"preview.entry.open": "Abrir en el Gestor de contenido, en una pestaña nueva",
|
|
106
|
+
"preview.entry.view": "Ver en el Gestor de contenido, en una pestaña nueva",
|
|
107
|
+
"preview.device.group": "Ancho de la vista previa",
|
|
108
|
+
"preview.device.phone": "Móvil",
|
|
109
|
+
"preview.device.tablet": "Tableta",
|
|
110
|
+
"preview.device.desktop": "Escritorio",
|
|
111
|
+
"preview.static": "Como sitio estático",
|
|
112
|
+
"preview.locale": "Idioma",
|
|
113
|
+
"preview.reload": "Recargar",
|
|
114
|
+
"preview.updating": "Actualizando la vista previa",
|
|
115
|
+
"preview.shortcuts": "Atajos: Ctrl+Mayús+1, 2 y 3 para móvil, tableta y escritorio; Ctrl+Mayús+S para el sitio estático; Ctrl+Mayús+Flecha izquierda para volver a la entrada. En Mac usa Comando en lugar de Ctrl. No funcionan mientras escribes en un campo.",
|
|
116
|
+
"preview.frame.title": "Vista previa de {title}",
|
|
117
|
+
"preview.blocked.title": "El panel no permite este sitio en un marco",
|
|
118
|
+
"preview.blocked.body": "La política de seguridad de contenido del panel no incluye {origin} en frame-src, así que el navegador se niega a mostrarlo. Añádelo a frame-src en la configuración de strapi::security del proyecto Strapi y reinícialo. La comprobación del host en la página de estado de Micelio también lo indica.",
|
|
119
|
+
"preview.upgrade.title": "Actualiza el frontend",
|
|
120
|
+
"preview.upgrade.body": "El frontend del sitio habla las versiones {versions} del protocolo de vista previa y este plugin no habla ninguna. La vista previa sigue siendo simple: muestra el borrador y lo carga de nuevo entero tras cada guardado.",
|
|
121
|
+
"preview.error.entry": "No se pudo cargar la entrada.",
|
|
122
|
+
"preview.error.missing": "Esta entrada no existe.",
|
|
123
|
+
"preview.error.unavailable": "Este sitio no tiene vista previa: define su url en el Gestor de contenido (Sitio) y comprueba que el frontend funciona allí.",
|
|
124
|
+
"preview.error.forbidden": "No puedes previsualizar esta entrada. Hace falta el permiso para previsualizar borradores y el permiso para leer la entrada.",
|
|
125
|
+
"preview.error.unsupported": "Este tipo no tiene vista previa: sus entradas no pertenecen a un sitio.",
|
|
126
|
+
"preview.error.busy": "Demasiadas solicitudes de vista previa. Espera un minuto y recarga.",
|
|
127
|
+
"preview.error.failed": "No se pudo cargar la vista previa. Recarga para intentarlo de nuevo.",
|
|
128
|
+
"action.preview": "Vista previa",
|
|
129
|
+
"action.preview.unsaved": "Vista previa: guarda primero los cambios"
|
|
94
130
|
};
|
|
95
131
|
export {
|
|
96
132
|
es as default
|
|
@@ -72,7 +72,7 @@ const plugin = {
|
|
|
72
72
|
icon: MicelioIcon,
|
|
73
73
|
intlLabel: { id: getTranslation("plugin.name"), defaultMessage: "Micelio" },
|
|
74
74
|
permissions: [],
|
|
75
|
-
Component: () => Promise.resolve().then(() => require("./MicelioLayout-
|
|
75
|
+
Component: () => Promise.resolve().then(() => require("./MicelioLayout-Bmu5xCw9.js"))
|
|
76
76
|
});
|
|
77
77
|
app.registerPlugin({
|
|
78
78
|
id: PLUGIN_ID,
|
|
@@ -83,11 +83,17 @@ const plugin = {
|
|
|
83
83
|
apis: { registerPage }
|
|
84
84
|
});
|
|
85
85
|
},
|
|
86
|
+
async bootstrap(app) {
|
|
87
|
+
const add = app.getPlugin("content-manager").apis.addDocumentHeaderAction;
|
|
88
|
+
if (typeof add !== "function") return;
|
|
89
|
+
const { PreviewHeaderAction } = await Promise.resolve().then(() => require("./PreviewHeaderAction-CGHycv4I.js"));
|
|
90
|
+
add([PreviewHeaderAction]);
|
|
91
|
+
},
|
|
86
92
|
async registerTrads({ locales }) {
|
|
87
93
|
return Promise.all(
|
|
88
94
|
locales.map(async (locale) => {
|
|
89
95
|
try {
|
|
90
|
-
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-
|
|
96
|
+
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-BDVuq0fN.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-DStBGkm7.js")) }), `./translations/${locale}.json`, 3);
|
|
91
97
|
const prefixed = {};
|
|
92
98
|
for (const [key, value] of Object.entries(data)) {
|
|
93
99
|
prefixed[getTranslation(key)] = value;
|
|
@@ -71,7 +71,7 @@ const plugin = {
|
|
|
71
71
|
icon: MicelioIcon,
|
|
72
72
|
intlLabel: { id: getTranslation("plugin.name"), defaultMessage: "Micelio" },
|
|
73
73
|
permissions: [],
|
|
74
|
-
Component: () => import("./MicelioLayout-
|
|
74
|
+
Component: () => import("./MicelioLayout-DMMsKnMo.mjs")
|
|
75
75
|
});
|
|
76
76
|
app.registerPlugin({
|
|
77
77
|
id: PLUGIN_ID,
|
|
@@ -82,11 +82,17 @@ const plugin = {
|
|
|
82
82
|
apis: { registerPage }
|
|
83
83
|
});
|
|
84
84
|
},
|
|
85
|
+
async bootstrap(app) {
|
|
86
|
+
const add = app.getPlugin("content-manager").apis.addDocumentHeaderAction;
|
|
87
|
+
if (typeof add !== "function") return;
|
|
88
|
+
const { PreviewHeaderAction } = await import("./PreviewHeaderAction-BaduMvYy.mjs");
|
|
89
|
+
add([PreviewHeaderAction]);
|
|
90
|
+
},
|
|
85
91
|
async registerTrads({ locales }) {
|
|
86
92
|
return Promise.all(
|
|
87
93
|
locales.map(async (locale) => {
|
|
88
94
|
try {
|
|
89
|
-
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-
|
|
95
|
+
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-Bahc6y6N.mjs"), "./translations/es.json": () => import("./es-nUHBIoZG.mjs") }), `./translations/${locale}.json`, 3);
|
|
90
96
|
const prefixed = {};
|
|
91
97
|
for (const [key, value] of Object.entries(data)) {
|
|
92
98
|
prefixed[getTranslation(key)] = value;
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const reactIntl = require("react-intl");
|
|
3
|
+
const en = require("./en-BDVuq0fN.js");
|
|
4
|
+
const index = require("./index-D_Dbvo2F.js");
|
|
5
|
+
function hasMessage(id) {
|
|
6
|
+
return Object.hasOwn(en.default, id);
|
|
7
|
+
}
|
|
8
|
+
function useMessages() {
|
|
9
|
+
const { formatMessage, formatDate } = reactIntl.useIntl();
|
|
10
|
+
return {
|
|
11
|
+
t: (id, values) => formatMessage(
|
|
12
|
+
{ id: index.getTranslation(id), defaultMessage: en.default[id] ?? id },
|
|
13
|
+
values
|
|
14
|
+
),
|
|
15
|
+
// Seconds included: a run again within the same minute must read as new.
|
|
16
|
+
date: (iso) => formatDate(iso, { dateStyle: "medium", timeStyle: "medium" })
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const STATUS_READ_PERMISSION = { action: "plugin::micelio.status.read", subject: null };
|
|
20
|
+
const PREVIEW_PERMISSION = { action: "plugin::micelio.preview", subject: null };
|
|
21
|
+
exports.PREVIEW_PERMISSION = PREVIEW_PERMISSION;
|
|
22
|
+
exports.STATUS_READ_PERMISSION = STATUS_READ_PERMISSION;
|
|
23
|
+
exports.hasMessage = hasMessage;
|
|
24
|
+
exports.useMessages = useMessages;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useIntl } from "react-intl";
|
|
2
|
+
import en from "./en-Bahc6y6N.mjs";
|
|
3
|
+
import { g as getTranslation } from "./index-Dwr15ZsB.mjs";
|
|
4
|
+
function hasMessage(id) {
|
|
5
|
+
return Object.hasOwn(en, id);
|
|
6
|
+
}
|
|
7
|
+
function useMessages() {
|
|
8
|
+
const { formatMessage, formatDate } = useIntl();
|
|
9
|
+
return {
|
|
10
|
+
t: (id, values) => formatMessage(
|
|
11
|
+
{ id: getTranslation(id), defaultMessage: en[id] ?? id },
|
|
12
|
+
values
|
|
13
|
+
),
|
|
14
|
+
// Seconds included: a run again within the same minute must read as new.
|
|
15
|
+
date: (iso) => formatDate(iso, { dateStyle: "medium", timeStyle: "medium" })
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const STATUS_READ_PERMISSION = { action: "plugin::micelio.status.read", subject: null };
|
|
19
|
+
const PREVIEW_PERMISSION = { action: "plugin::micelio.preview", subject: null };
|
|
20
|
+
export {
|
|
21
|
+
PREVIEW_PERMISSION as P,
|
|
22
|
+
STATUS_READ_PERMISSION as S,
|
|
23
|
+
hasMessage as h,
|
|
24
|
+
useMessages as u
|
|
25
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SVGProps } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The icon of the preview action: an eye.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Drawn here for the reason `MicelioIcon` is: `@strapi/icons` cannot be required in Node.
|
|
7
|
+
*/
|
|
8
|
+
export declare const EyeIcon: (props: SVGProps<SVGSVGElement>) => import("react").JSX.Element;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Whether the frame said it speaks a version the admin speaks. */
|
|
2
|
+
export type FrameProtocol = 'waiting' | 'ok' | 'upgrade';
|
|
3
|
+
/** What the frame reports to the page. */
|
|
4
|
+
export interface FrameState {
|
|
5
|
+
/** What the frame in view said with `ready`. */
|
|
6
|
+
protocol: FrameProtocol;
|
|
7
|
+
/** The versions the frame listed in its `ready`, once it sent one. */
|
|
8
|
+
versions: number[];
|
|
9
|
+
/** A frame is loading, or the one in view is loading the draft again after a `load-draft`. */
|
|
10
|
+
isBusy: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** What the page can ask of the frame. */
|
|
13
|
+
export interface PreviewFrameHandle {
|
|
14
|
+
/**
|
|
15
|
+
* Tells the frame in view to load the draft again, with `load-draft` to the site's origin only.
|
|
16
|
+
*
|
|
17
|
+
* @returns `false` when the frame has not said `ready` with a version the admin speaks: nothing was
|
|
18
|
+
* sent, and the page loads the preview again with a new token.
|
|
19
|
+
*/
|
|
20
|
+
loadDraft: () => boolean;
|
|
21
|
+
}
|
|
22
|
+
interface PreviewFrameProps {
|
|
23
|
+
/** The URL to load; a new value loads it in a frame of its own, and the old render stays until it is ready. */
|
|
24
|
+
src: string | undefined;
|
|
25
|
+
/** The site's origin: the only one the page posts to and reads messages from. */
|
|
26
|
+
origin: string | undefined;
|
|
27
|
+
documentId: string;
|
|
28
|
+
/** `null` for a type that is not localised. */
|
|
29
|
+
locale: string | null;
|
|
30
|
+
title: string;
|
|
31
|
+
/** The width of the page in the frame, in pixels; the frame never grows past its column. */
|
|
32
|
+
width: number;
|
|
33
|
+
onChange: (state: FrameState) => void;
|
|
34
|
+
/** The admin's Content Security Policy refused to frame `origin`. */
|
|
35
|
+
onBlocked: () => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The preview iframe of the preview page (ADR-0019, sections 4 and 5): loads the URL the token route
|
|
39
|
+
* answered, listens for `ready` from the site's origin only and posts `load-draft` to it.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* Messages from another origin or window, or that fail the schema of `contract/preview.ts`, are dropped.
|
|
43
|
+
* The iframe is sandboxed as the ADR says. A new `src` loads in a second frame stacked on the first,
|
|
44
|
+
* which the page shows once it has loaded.
|
|
45
|
+
*/
|
|
46
|
+
export declare const PreviewFrame: import('react').ForwardRefExoticComponent<PreviewFrameProps & import('react').RefAttributes<PreviewFrameHandle>>;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** What Strapi hands a header action of the edit view; the fields this action reads. */
|
|
2
|
+
interface HeaderActionProps {
|
|
3
|
+
collectionType: string;
|
|
4
|
+
/** `undefined` while the entry is being created. */
|
|
5
|
+
documentId?: string;
|
|
6
|
+
document?: {
|
|
7
|
+
locale?: string | null;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/** The description Strapi renders as a button of the edit view's header. */
|
|
11
|
+
interface HeaderActionDescription {
|
|
12
|
+
label: string;
|
|
13
|
+
icon: JSX.Element;
|
|
14
|
+
disabled: boolean;
|
|
15
|
+
onClick: () => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The **Preview** button in the header of the edit view (issue #47): it opens the preview page of the
|
|
19
|
+
* entry. Strapi runs it as a hook, once for each edit view, and shows what it returns.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* It shows only for a saved entry of a collection type that has a preview (`hasPreview`) to a role
|
|
23
|
+
* that may preview. While the form has unsaved changes it is disabled, and its label, which is also
|
|
24
|
+
* its tooltip and its accessible name, says to save first: the preview shows the saved draft.
|
|
25
|
+
* The name of the function is what Strapi keys the button by.
|
|
26
|
+
*/
|
|
27
|
+
export declare function PreviewHeaderAction({ collectionType, documentId, document, }: HeaderActionProps): HeaderActionDescription | null;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** The entry the preview shows, as the Content Manager API answers it. */
|
|
2
|
+
export interface Entry {
|
|
3
|
+
title: string;
|
|
4
|
+
/** `null` for a type that is not localised. */
|
|
5
|
+
locale: string | null;
|
|
6
|
+
/** The locales the entry has a version in. */
|
|
7
|
+
locales: string[];
|
|
8
|
+
/** When the draft was last saved, as an ISO 8601 date. */
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
}
|
|
11
|
+
/** What the page knows of the entry. */
|
|
12
|
+
export type EntryResource = {
|
|
13
|
+
kind: 'loading';
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'ready';
|
|
16
|
+
entry: Entry;
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'error';
|
|
19
|
+
status: number | undefined;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Reads the entry from the Content Manager's API and reads it again every few seconds while the
|
|
23
|
+
* page is in view, and when it comes back into view, to notice a save made in the form.
|
|
24
|
+
*
|
|
25
|
+
* @returns The entry, and `savedCount`, which goes up by one each time the draft's `updatedAt`
|
|
26
|
+
* changes after the first read. A change of locale starts counting again.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useEntry(uid: string, documentId: string, locale: string | null): {
|
|
29
|
+
resource: EntryResource;
|
|
30
|
+
savedCount: number;
|
|
31
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { PreviewMode } from '../utils/previewPane';
|
|
2
|
+
/** Why there is no URL to load. */
|
|
3
|
+
export type PreviewSourceError = 'unavailable' | 'forbidden' | 'missing' | 'unsupported' | 'busy' | 'failed';
|
|
4
|
+
/** The URL of the iframe, or why there is none. */
|
|
5
|
+
export type PreviewSource = {
|
|
6
|
+
kind: 'loading';
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'ready';
|
|
9
|
+
src: string;
|
|
10
|
+
origin: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'error';
|
|
13
|
+
reason: PreviewSourceError;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Asks `POST /micelio/preview/token` for the URL of the iframe (ADR-0019, section 3), and again when
|
|
17
|
+
* `reload()` is called, when the entry, locale or mode change, and before the grant expires.
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
* The token is single-use and 60 seconds old at most, so it is requested only when a frame is about
|
|
21
|
+
* to load. The previous source stays in `source` while the next one is requested, so the old render
|
|
22
|
+
* stays in view.
|
|
23
|
+
*/
|
|
24
|
+
export declare function usePreviewSource(entry: {
|
|
25
|
+
uid: string;
|
|
26
|
+
documentId: string;
|
|
27
|
+
locale: string | null;
|
|
28
|
+
} | undefined, mode: PreviewMode): {
|
|
29
|
+
source: PreviewSource;
|
|
30
|
+
reload: () => void;
|
|
31
|
+
};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* The core's items (`navigation.ts`) show to a role that has their permission; the pages of
|
|
6
6
|
* sibling plugins come from `GET /micelio/admin-pages`, which lists only those the admin user has
|
|
7
7
|
* the permission for. A group with nothing to show is left out. A role without the Status
|
|
8
|
-
* permission lands on About.
|
|
8
|
+
* permission lands on About. The preview page (`preview/:uid/:documentId`) has no item: the entry's edit view
|
|
9
|
+
* opens it, and its own permission protects it.
|
|
9
10
|
*/
|
|
10
11
|
export default function MicelioLayout(): import("react").JSX.Element;
|
|
@@ -3,3 +3,8 @@ export declare const STATUS_READ_PERMISSION: {
|
|
|
3
3
|
action: string;
|
|
4
4
|
subject: null;
|
|
5
5
|
};
|
|
6
|
+
/** Opens the preview page; the server's `PREVIEW_PERMISSION`, registered in its `bootstrap`. */
|
|
7
|
+
export declare const PREVIEW_PERMISSION: {
|
|
8
|
+
action: string;
|
|
9
|
+
subject: null;
|
|
10
|
+
};
|