diff --git a/libs/plugins-runtime/src/lib/api/openUI.api.ts b/libs/plugins-runtime/src/lib/api/openUI.api.ts index a97960a..e737955 100644 --- a/libs/plugins-runtime/src/lib/api/openUI.api.ts +++ b/libs/plugins-runtime/src/lib/api/openUI.api.ts @@ -8,8 +8,9 @@ export const openUIApi = z z.string(), z.string(), z.enum(['dark', 'light']), - openUISchema.optional() + openUISchema.optional(), + z.boolean().optional() ) - .implement((title, url, theme, options) => { - return createModal(title, url, theme, options); + .implement((title, url, theme, options, allowDownloads) => { + return createModal(title, url, theme, options, allowDownloads); }); diff --git a/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts b/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts index ecc327b..ae3bf71 100644 --- a/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts +++ b/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts @@ -18,6 +18,9 @@ describe('Plugin api', () => { 'library:read', 'library:write', 'user:read', + 'comment:read', + 'comment:write', + 'allow:downloads', ], }, openModal: vi.fn(), @@ -107,7 +110,7 @@ describe('Plugin api', () => { name: 'test', id: '123', revn: 0, - } as File; + } as File; pluginManager.context.getFile.mockImplementation(() => exampleFile); diff --git a/libs/plugins-runtime/src/lib/create-modal.ts b/libs/plugins-runtime/src/lib/create-modal.ts index 1e44f25..0df0911 100644 --- a/libs/plugins-runtime/src/lib/create-modal.ts +++ b/libs/plugins-runtime/src/lib/create-modal.ts @@ -6,7 +6,8 @@ export function createModal( name: string, url: string, theme: Theme, - options?: OpenUIOptions + options?: OpenUIOptions, + allowDownloads?: boolean ) { const modal = document.createElement('plugin-modal') as PluginModalElement; @@ -44,6 +45,10 @@ export function createModal( modal.setAttribute('width', String(width)); modal.setAttribute('height', String(height)); + if (allowDownloads) { + modal.setAttribute('allow-downloads', 'true'); + } + document.body.appendChild(modal); return modal; diff --git a/libs/plugins-runtime/src/lib/create-plugin.spec.ts b/libs/plugins-runtime/src/lib/create-plugin.spec.ts index 9c413a5..b5ce200 100644 --- a/libs/plugins-runtime/src/lib/create-plugin.spec.ts +++ b/libs/plugins-runtime/src/lib/create-plugin.spec.ts @@ -32,6 +32,9 @@ describe('createPlugin', () => { 'library:read', 'library:write', 'user:read', + 'comment:read', + 'comment:write', + 'allow:downloads', ], }; diff --git a/libs/plugins-runtime/src/lib/create-sandbox.ts b/libs/plugins-runtime/src/lib/create-sandbox.ts index cf219c7..6fa1d0d 100644 --- a/libs/plugins-runtime/src/lib/create-sandbox.ts +++ b/libs/plugins-runtime/src/lib/create-sandbox.ts @@ -55,6 +55,7 @@ export function createSandbox( penpot: proxyApi, fetch: ses.harden(safeFetch), console: ses.harden(window.console), + Date: ses.harden(Date), Math: ses.harden(Math), setTimeout: ses.harden( (...[handler, timeout]: Parameters) => { diff --git a/libs/plugins-runtime/src/lib/load-plugin.spec.ts b/libs/plugins-runtime/src/lib/load-plugin.spec.ts index 647d410..8f9c083 100644 --- a/libs/plugins-runtime/src/lib/load-plugin.spec.ts +++ b/libs/plugins-runtime/src/lib/load-plugin.spec.ts @@ -43,6 +43,9 @@ describe('plugin-loader', () => { 'library:read', 'library:write', 'user:read', + 'comment:read', + 'comment:write', + 'allow:downloads', ], }; diff --git a/libs/plugins-runtime/src/lib/modal/plugin-modal.ts b/libs/plugins-runtime/src/lib/modal/plugin-modal.ts index 74db8e8..c13423f 100644 --- a/libs/plugins-runtime/src/lib/modal/plugin-modal.ts +++ b/libs/plugins-runtime/src/lib/modal/plugin-modal.ts @@ -43,6 +43,7 @@ export class PluginModalElement extends HTMLElement { const iframeSrc = this.getAttribute('iframe-src'); const width = Number(this.getAttribute('width') || '300'); const height = Number(this.getAttribute('height') || '400'); + const allowDownloads = this.getAttribute('allow-downloads') || false; if (!title || !iframeSrc) { throw new Error('title and iframe-src attributes are required'); @@ -100,6 +101,10 @@ export class PluginModalElement extends HTMLElement { 'allow-storage-access-by-user-activation' ); + if (allowDownloads) { + iframe.sandbox.add('allow-downloads'); + } + iframe.addEventListener('load', () => { this.shadowRoot?.dispatchEvent( new CustomEvent('load', { diff --git a/libs/plugins-runtime/src/lib/models/manifest.schema.ts b/libs/plugins-runtime/src/lib/models/manifest.schema.ts index 373854e..01db926 100644 --- a/libs/plugins-runtime/src/lib/models/manifest.schema.ts +++ b/libs/plugins-runtime/src/lib/models/manifest.schema.ts @@ -14,6 +14,9 @@ export const manifestSchema = z.object({ 'library:read', 'library:write', 'user:read', + 'comment:read', + 'comment:write', + 'allow:downloads', ]) ), }); diff --git a/libs/plugins-runtime/src/lib/plugin-manager.spec.ts b/libs/plugins-runtime/src/lib/plugin-manager.spec.ts index 06869f0..f9ae229 100644 --- a/libs/plugins-runtime/src/lib/plugin-manager.spec.ts +++ b/libs/plugins-runtime/src/lib/plugin-manager.spec.ts @@ -40,6 +40,9 @@ describe('createPluginManager', () => { 'library:read', 'library:write', 'user:read', + 'comment:read', + 'comment:write', + 'allow:downloads', ], }; diff --git a/libs/plugins-runtime/src/lib/plugin-manager.ts b/libs/plugins-runtime/src/lib/plugin-manager.ts index 7015b17..3e0b035 100644 --- a/libs/plugins-runtime/src/lib/plugin-manager.ts +++ b/libs/plugins-runtime/src/lib/plugin-manager.ts @@ -21,6 +21,10 @@ export async function createPluginManager( let uiMessagesCallbacks: ((message: unknown) => void)[] = []; const timeouts = new Set>(); + const allowDownloads = !!manifest.permissions.find( + (s) => s === 'allow:downloads' + ); + const themeChangeId = context.addListener('themechange', (theme: Theme) => { modal?.setTheme(theme); }); @@ -83,7 +87,7 @@ export async function createPluginManager( return; } - modal = openUIApi(name, modalUrl, theme, options); + modal = openUIApi(name, modalUrl, theme, options, allowDownloads); modal.setTheme(theme);