diff --git a/packages/app/client/src/plugins/iframe-block.ts b/packages/app/client/src/plugins/iframe-block.ts
new file mode 100644
index 000000000..efd6abd53
--- /dev/null
+++ b/packages/app/client/src/plugins/iframe-block.ts
@@ -0,0 +1 @@
+export { default } from '@nocobase/plugin-iframe-block/client';
\ No newline at end of file
diff --git a/packages/core/client/src/schema-component/common/iframe/Iframe.Designer.tsx b/packages/core/client/src/schema-component/common/iframe/Iframe.Designer.tsx
deleted file mode 100644
index 0b4505cea..000000000
--- a/packages/core/client/src/schema-component/common/iframe/Iframe.Designer.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import { ISchema, useField, useFieldSchema } from '@formily/react';
-import React from 'react';
-import { useTranslation } from 'react-i18next';
-import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
-import { useDesignable } from '../../hooks';
-
-export const IframeDesigner = () => {
- const field = useField();
- const fieldSchema = useFieldSchema();
- const { t } = useTranslation();
- const { dn } = useDesignable();
-
- return (
-
- {
- field.componentProps.url = url;
- field.componentProps.height = height;
- const componentProps = fieldSchema['x-component-props'] || {};
- componentProps['url'] = url;
- componentProps['height'] = height;
- fieldSchema['x-component-props'] = componentProps;
- dn.emit('patch', {
- schema: {
- 'x-uid': fieldSchema['x-uid'],
- 'x-component-props': componentProps,
- },
- });
- }}
- />
-
-
-
- );
-};
diff --git a/packages/core/client/src/schema-component/common/index.ts b/packages/core/client/src/schema-component/common/index.ts
index ba61b9f1c..3f18bf971 100644
--- a/packages/core/client/src/schema-component/common/index.ts
+++ b/packages/core/client/src/schema-component/common/index.ts
@@ -1,4 +1,2 @@
export * from './dnd-context';
-export * from './iframe';
export * from './sortable-item';
-
diff --git a/packages/core/client/src/schema-initializer/buttons/BlockInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/BlockInitializers.tsx
index d00f42fea..c7f3b0b46 100644
--- a/packages/core/client/src/schema-initializer/buttons/BlockInitializers.tsx
+++ b/packages/core/client/src/schema-initializer/buttons/BlockInitializers.tsx
@@ -164,12 +164,6 @@ export const BlockInitializers = {
title: '{{t("Markdown")}}',
component: 'MarkdownBlockInitializer',
},
- {
- key: 'iframe',
- type: 'item',
- title: '{{t("Iframe")}}',
- component: 'IframeBlockInitializer',
- },
{
key: 'auditLogs',
type: 'item',
diff --git a/packages/core/client/src/schema-initializer/items/index.tsx b/packages/core/client/src/schema-initializer/items/index.tsx
index ecef69bc1..42eb9fe50 100644
--- a/packages/core/client/src/schema-initializer/items/index.tsx
+++ b/packages/core/client/src/schema-initializer/items/index.tsx
@@ -17,7 +17,6 @@ export * from './DetailsBlockInitializer';
export * from './FilterActionInitializer';
export * from './FormBlockInitializer';
export * from './G2PlotInitializer';
-export * from './IframeBlockInitializer';
export * from './InitializerWithSwitch';
export * from './KanbanBlockInitializer';
export * from './MarkdownBlockInitializer';
@@ -38,4 +37,3 @@ export * from './TableSelectorInitializer';
export * from './UpdateActionInitializer';
export * from './UpdateSubmitActionInitializer';
export * from './ViewActionInitializer';
-
diff --git a/packages/core/client/src/schema-settings/SchemaSettings.tsx b/packages/core/client/src/schema-settings/SchemaSettings.tsx
index 5a2b2abe1..8d55f28a5 100644
--- a/packages/core/client/src/schema-settings/SchemaSettings.tsx
+++ b/packages/core/client/src/schema-settings/SchemaSettings.tsx
@@ -22,7 +22,7 @@ import {
useAPIClient,
useCollection,
useCompile,
- useDesignable,
+ useDesignable
} from '..';
import { useSchemaTemplateManager } from '../schema-templates';
import { useBlockTemplateContext } from '../schema-templates/BlockTemplate';
@@ -574,7 +574,18 @@ SchemaSettings.ActionModalItem = React.memo((props: any) => {
});
SchemaSettings.ModalItem = (props) => {
- const { hidden, title, components, scope, effects, schema, onSubmit, initialValues, ...others } = props;
+ const {
+ hidden,
+ title,
+ components,
+ scope,
+ effects,
+ schema,
+ onSubmit,
+ asyncGetInitialValues,
+ initialValues,
+ ...others
+ } = props;
const options = useContext(SchemaOptionsContext);
const cm = useContext(CollectionManagerContext);
if (hidden) {
@@ -583,7 +594,8 @@ SchemaSettings.ModalItem = (props) => {
return (
{
+ onClick={async () => {
+ const values = asyncGetInitialValues ? await asyncGetInitialValues() : initialValues;
FormDialog(schema.title || title, () => {
return (
@@ -596,7 +608,7 @@ SchemaSettings.ModalItem = (props) => {
);
})
.open({
- initialValues,
+ initialValues: values,
effects,
})
.then((values) => {
diff --git a/packages/core/server/src/helper.ts b/packages/core/server/src/helper.ts
index d866c99f4..a2534aa6e 100644
--- a/packages/core/server/src/helper.ts
+++ b/packages/core/server/src/helper.ts
@@ -56,7 +56,8 @@ export function registerMiddlewares(app: Application, options: ApplicationOption
app.use(async (ctx, next) => {
ctx.getBearerToken = () => {
- return ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
+ const token = ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
+ return token || ctx.query.token;
};
await next();
});
diff --git a/packages/plugins/iframe-block/client.d.ts b/packages/plugins/iframe-block/client.d.ts
new file mode 100644
index 000000000..765db9222
--- /dev/null
+++ b/packages/plugins/iframe-block/client.d.ts
@@ -0,0 +1,4 @@
+// @ts-nocheck
+export * from './lib/client';
+export { default } from './lib/client';
+
diff --git a/packages/plugins/iframe-block/client.js b/packages/plugins/iframe-block/client.js
new file mode 100644
index 000000000..238820257
--- /dev/null
+++ b/packages/plugins/iframe-block/client.js
@@ -0,0 +1,30 @@
+"use strict";
+
+function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
+
+function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
+
+var _index = _interopRequireWildcard(require("./lib/client"));
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+var _exportNames = {};
+Object.defineProperty(exports, "default", {
+ enumerable: true,
+ get: function get() {
+ return _index.default;
+ }
+});
+
+Object.keys(_index).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
+ if (key in exports && exports[key] === _index[key]) return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function get() {
+ return _index[key];
+ }
+ });
+});
diff --git a/packages/plugins/iframe-block/package.json b/packages/plugins/iframe-block/package.json
new file mode 100644
index 000000000..47ceed275
--- /dev/null
+++ b/packages/plugins/iframe-block/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@nocobase/plugin-iframe-block",
+ "version": "0.8.0-alpha.13",
+ "main": "lib/server/index.js",
+ "devDependencies": {
+ "@nocobase/server": "0.8.0-alpha.13",
+ "@nocobase/test": "0.8.0-alpha.13"
+ }
+}
diff --git a/packages/plugins/iframe-block/server.d.ts b/packages/plugins/iframe-block/server.d.ts
new file mode 100644
index 000000000..e70edb928
--- /dev/null
+++ b/packages/plugins/iframe-block/server.d.ts
@@ -0,0 +1,4 @@
+// @ts-nocheck
+export * from './lib/server';
+export { default } from './lib/server';
+
diff --git a/packages/plugins/iframe-block/server.js b/packages/plugins/iframe-block/server.js
new file mode 100644
index 000000000..d06a7eb92
--- /dev/null
+++ b/packages/plugins/iframe-block/server.js
@@ -0,0 +1,30 @@
+"use strict";
+
+function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
+
+function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
+
+var _index = _interopRequireWildcard(require("./lib/server"));
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+var _exportNames = {};
+Object.defineProperty(exports, "default", {
+ enumerable: true,
+ get: function get() {
+ return _index.default;
+ }
+});
+
+Object.keys(_index).forEach(function (key) {
+ if (key === "default" || key === "__esModule") return;
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
+ if (key in exports && exports[key] === _index[key]) return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function get() {
+ return _index[key];
+ }
+ });
+});
diff --git a/packages/plugins/iframe-block/src/client/Iframe.Designer.tsx b/packages/plugins/iframe-block/src/client/Iframe.Designer.tsx
new file mode 100644
index 000000000..7fc384c08
--- /dev/null
+++ b/packages/plugins/iframe-block/src/client/Iframe.Designer.tsx
@@ -0,0 +1,131 @@
+import { ISchema, useField, useFieldSchema } from '@formily/react';
+import { uid } from '@formily/shared';
+import { GeneralSchemaDesigner, SchemaSettings, useAPIClient, useDesignable } from '@nocobase/client';
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+export const IframeDesigner = () => {
+ const field = useField();
+ const fieldSchema = useFieldSchema();
+ const { t } = useTranslation();
+ const { dn } = useDesignable();
+ const api = useAPIClient();
+ const { mode, url, htmlId, height = '60vh' } = fieldSchema['x-component-props'] || {};
+
+ const saveHtml = async (html: string) => {
+ const options = {
+ values: { html },
+ };
+ if (htmlId) {
+ const { data } = await api.resource('iframeHtml').update?.({ ...options, filterByTk: htmlId });
+ return data?.data?.[0] || { id: htmlId };
+ } else {
+ const { data } = await api.resource('iframeHtml').create?.(options);
+ return data?.data;
+ }
+ };
+
+ const submitHandler = async ({ mode, url, html, height }) => {
+ const componentProps = fieldSchema['x-component-props'] || {};
+ componentProps['mode'] = mode;
+ componentProps['height'] = height;
+ componentProps['url'] = url;
+ if (mode === 'html') {
+ const data = await saveHtml(html);
+ componentProps['htmlId'] = data.id;
+ }
+ fieldSchema['x-component-props'] = componentProps;
+ field.componentProps = { ...componentProps };
+ field.data = { v: uid() };
+ dn.emit('patch', {
+ schema: {
+ 'x-uid': fieldSchema['x-uid'],
+ 'x-component-props': componentProps,
+ },
+ });
+ };
+
+ return (
+
+ {
+ const values = {
+ mode,
+ url,
+ height,
+ };
+ if (htmlId) {
+ const { data } = await api.resource('iframeHtml').get?.({ filterByTk: htmlId });
+ values['html'] = data?.data?.html || '';
+ }
+ return values;
+ }}
+ schema={
+ {
+ type: 'object',
+ title: t('Edit iframe'),
+ properties: {
+ mode: {
+ title: '{{t("Mode")}}',
+ 'x-component': 'Radio.Group',
+ 'x-decorator': 'FormItem',
+ required: true,
+ default: 'url',
+ enum: [
+ { value: 'url', label: t('URL') },
+ { value: 'html', label: t('html') },
+ ],
+ },
+ url: {
+ title: t('URL'),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Input',
+ required: true,
+ 'x-reactions': {
+ dependencies: ['mode'],
+ fulfill: {
+ state: {
+ hidden: '{{$deps[0] === "html"}}',
+ },
+ },
+ },
+ },
+ html: {
+ title: t('html'),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Input.TextArea',
+ required: true,
+ 'x-reactions': {
+ dependencies: ['mode'],
+ fulfill: {
+ state: {
+ hidden: '{{$deps[0] === "url"}}',
+ },
+ },
+ },
+ },
+ height: {
+ title: t('Height'),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Input',
+ required: true,
+ },
+ },
+ } as ISchema
+ }
+ onSubmit={submitHandler}
+ />
+
+
+
+ );
+};
diff --git a/packages/core/client/src/schema-component/common/iframe/index.tsx b/packages/plugins/iframe-block/src/client/Iframe.tsx
similarity index 57%
rename from packages/core/client/src/schema-component/common/iframe/index.tsx
rename to packages/plugins/iframe-block/src/client/Iframe.tsx
index b434d123f..f43424956 100644
--- a/packages/core/client/src/schema-component/common/iframe/index.tsx
+++ b/packages/plugins/iframe-block/src/client/Iframe.tsx
@@ -1,4 +1,5 @@
-import { observer } from '@formily/react';
+import { observer, useField } from '@formily/react';
+import { useAPIClient } from '@nocobase/client';
import { Card } from 'antd';
import React from 'react';
import { useTranslation } from 'react-i18next';
@@ -6,7 +7,7 @@ import RIframe from 'react-iframe';
import type { IIframe } from 'react-iframe/types';
import { IframeDesigner } from './Iframe.Designer';
-function isNumeric(str: string) {
+function isNumeric(str: string | undefined) {
if (typeof str !== 'string') return false; // we only process strings!
return (
!isNaN(str as any) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
@@ -14,15 +15,27 @@ function isNumeric(str: string) {
); // ...and ensure strings of whitespace fail
}
-export const Iframe: any = observer((props: IIframe) => {
- const { url, height, ...others } = props;
+export const Iframe: any = observer((props: IIframe & { html?: string; htmlId?: number; mode: string }) => {
+ const { url, htmlId, mode = 'url', height, html, ...others } = props;
+
const { t } = useTranslation();
- if (!url) {
+ const api = useAPIClient();
+ const field = useField();
+
+ const src = React.useMemo(() => {
+ if (mode === 'html') {
+ return `/api/iframeHtml:getHtml/${htmlId}?token=${api.auth.getToken()}&v=${field.data?.v || ''}`;
+ }
+ return url;
+ }, [url, mode, htmlId, field.data?.v]);
+
+ if ((mode === 'url' && !url) || (mode === 'html' && !htmlId)) {
return {t('Please fill in the iframe URL')};
}
+
return (
{
const { insert } = props;
const { t } = useTranslation();
diff --git a/packages/plugins/iframe-block/src/client/IframeBlockInitializerProvider.tsx b/packages/plugins/iframe-block/src/client/IframeBlockInitializerProvider.tsx
new file mode 100644
index 000000000..13e0fdfd4
--- /dev/null
+++ b/packages/plugins/iframe-block/src/client/IframeBlockInitializerProvider.tsx
@@ -0,0 +1,18 @@
+import { SchemaInitializerContext } from '@nocobase/client';
+import { useContext } from 'react';
+
+export const IframeBlockInitializerProvider = (props: any) => {
+ const initializes = useContext(SchemaInitializerContext);
+ const mediaItem = initializes.BlockInitializers.items.find((item) => item.key === 'media');
+ const hasIframeBlockInitializer = mediaItem.children.some(
+ (initialize) => initialize.component === 'IframeBlockInitializer',
+ );
+ !hasIframeBlockInitializer &&
+ mediaItem.children.push({
+ key: 'iframe',
+ type: 'item',
+ title: '{{t("Iframe")}}',
+ component: 'IframeBlockInitializer',
+ });
+ return props.children;
+};
diff --git a/packages/plugins/iframe-block/src/client/IframeBlockPlugin.tsx b/packages/plugins/iframe-block/src/client/IframeBlockPlugin.tsx
new file mode 100644
index 000000000..8d25bf798
--- /dev/null
+++ b/packages/plugins/iframe-block/src/client/IframeBlockPlugin.tsx
@@ -0,0 +1,13 @@
+import { SchemaComponentOptions } from '@nocobase/client';
+import React from 'react';
+import { Iframe } from './Iframe';
+import { IframeBlockInitializer } from './IframeBlockInitializer';
+import { IframeBlockInitializerProvider } from './IframeBlockInitializerProvider';
+
+export const IframeBlockPlugin = (props: any) => {
+ return (
+
+ {props.children}
+
+ );
+};
diff --git a/packages/plugins/iframe-block/src/client/index.ts b/packages/plugins/iframe-block/src/client/index.ts
new file mode 100644
index 000000000..41ae44e4d
--- /dev/null
+++ b/packages/plugins/iframe-block/src/client/index.ts
@@ -0,0 +1 @@
+export { IframeBlockPlugin as default } from './IframeBlockPlugin';
diff --git a/packages/plugins/iframe-block/src/index.ts b/packages/plugins/iframe-block/src/index.ts
new file mode 100644
index 000000000..7ddad5814
--- /dev/null
+++ b/packages/plugins/iframe-block/src/index.ts
@@ -0,0 +1 @@
+export { default } from './server';
diff --git a/packages/plugins/iframe-block/src/server/actions/index.ts b/packages/plugins/iframe-block/src/server/actions/index.ts
new file mode 100644
index 000000000..622bb9111
--- /dev/null
+++ b/packages/plugins/iframe-block/src/server/actions/index.ts
@@ -0,0 +1,17 @@
+import { Context, Next } from '@nocobase/actions';
+import { Repository } from '@nocobase/database';
+
+export async function getHtml(ctx: Context, next: Next) {
+ let { filterByTk } = ctx.action.params;
+ const { resourceName } = ctx.action;
+ const repository = ctx.db.getRepository(resourceName) as Repository;
+ const model = await repository.findById(filterByTk);
+ ctx.body = model.get('html');
+ ctx.withoutDataWrapping = true;
+
+ ctx.set({
+ 'Content-Type': 'text/html; charset=UTF-8',
+ });
+
+ await next();
+}
diff --git a/packages/plugins/iframe-block/src/server/collections/.gitkeep b/packages/plugins/iframe-block/src/server/collections/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/plugins/iframe-block/src/server/collections/iframe-html.ts b/packages/plugins/iframe-block/src/server/collections/iframe-html.ts
new file mode 100644
index 000000000..0fca8db1a
--- /dev/null
+++ b/packages/plugins/iframe-block/src/server/collections/iframe-html.ts
@@ -0,0 +1,18 @@
+import { CollectionOptions } from '@nocobase/database';
+
+export default {
+ name: 'iframeHtml',
+ createdBy: true,
+ updatedBy: true,
+ fields: [
+ {
+ type: 'uid',
+ name: 'id',
+ primaryKey: true,
+ },
+ {
+ type: 'text',
+ name: 'html',
+ },
+ ],
+} as CollectionOptions;
diff --git a/packages/plugins/iframe-block/src/server/index.ts b/packages/plugins/iframe-block/src/server/index.ts
new file mode 100644
index 000000000..b68aea57f
--- /dev/null
+++ b/packages/plugins/iframe-block/src/server/index.ts
@@ -0,0 +1 @@
+export { default } from './plugin';
diff --git a/packages/plugins/iframe-block/src/server/plugin.ts b/packages/plugins/iframe-block/src/server/plugin.ts
new file mode 100644
index 000000000..866ec25d2
--- /dev/null
+++ b/packages/plugins/iframe-block/src/server/plugin.ts
@@ -0,0 +1,30 @@
+import { InstallOptions, Plugin } from '@nocobase/server';
+import path from 'path';
+import { getHtml } from './actions';
+
+export class IframeBlockPlugin extends Plugin {
+ afterAdd() {}
+
+ beforeLoad() {}
+
+ async load() {
+ await this.db.import({
+ directory: path.resolve(__dirname, 'collections'),
+ });
+ this.app.acl.allow('iframeHtml', ['get', 'create', 'update', 'destroy'], 'allowConfigure');
+ this.app.acl.allow('iframeHtml', 'getHtml', 'loggedIn');
+ this.app.actions({
+ 'iframeHtml:getHtml': getHtml,
+ });
+ }
+
+ async install(options?: InstallOptions) {}
+
+ async afterEnable() {}
+
+ async afterDisable() {}
+
+ async remove() {}
+}
+
+export default IframeBlockPlugin;
diff --git a/packages/presets/nocobase/package.json b/packages/presets/nocobase/package.json
index a424a5588..ed88cf6be 100644
--- a/packages/presets/nocobase/package.json
+++ b/packages/presets/nocobase/package.json
@@ -31,6 +31,7 @@
"@nocobase/plugin-verification": "0.8.0-alpha.13",
"@nocobase/plugin-workflow": "0.8.0-alpha.13",
"@nocobase/plugin-map": "0.8.0-alpha.13",
+ "@nocobase/plugin-iframe-block": "0.8.0-alpha.13",
"@nocobase/server": "0.8.0-alpha.13"
},
"repository": {
diff --git a/packages/presets/nocobase/src/index.ts b/packages/presets/nocobase/src/index.ts
index d1c326d25..e70c60cc0 100644
--- a/packages/presets/nocobase/src/index.ts
+++ b/packages/presets/nocobase/src/index.ts
@@ -23,6 +23,7 @@ export class PresetNocoBase extends Plugin {
'export',
'import',
'audit-logs',
+ 'iframe-block',
].concat(plugins),
);
}