mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
* ✨ Add clipboard:read/write permissions to plugin system (#6980) * 🔧 Fix prettier formatting in clipboard permission files --------- Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com> Co-authored-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
parent
e280168de9
commit
50bee5e176
@ -302,7 +302,20 @@
|
|||||||
[:div {:class (stl/css :permissions-list-entry)}
|
[:div {:class (stl/css :permissions-list-entry)}
|
||||||
deprecated-icon/oauth-1
|
deprecated-icon/oauth-1
|
||||||
[:p {:class (stl/css :permissions-list-text)}
|
[:p {:class (stl/css :permissions-list-text)}
|
||||||
(tr "workspace.plugins.permissions.allow-localstorage")]])])
|
(tr "workspace.plugins.permissions.allow-localstorage")]])
|
||||||
|
|
||||||
|
(cond
|
||||||
|
(contains? permissions "clipboard:write")
|
||||||
|
[:div {:class (stl/css :permissions-list-entry)}
|
||||||
|
deprecated-icon/oauth-1
|
||||||
|
[:p {:class (stl/css :permissions-list-text)}
|
||||||
|
(tr "workspace.plugins.permissions.clipboard-write")]]
|
||||||
|
|
||||||
|
(contains? permissions "clipboard:read")
|
||||||
|
[:div {:class (stl/css :permissions-list-entry)}
|
||||||
|
deprecated-icon/oauth-1
|
||||||
|
[:p {:class (stl/css :permissions-list-text)}
|
||||||
|
(tr "workspace.plugins.permissions.clipboard-read")]])])
|
||||||
|
|
||||||
(mf/defc plugins-permissions-dialog
|
(mf/defc plugins-permissions-dialog
|
||||||
{::mf/register modal/components
|
{::mf/register modal/components
|
||||||
|
|||||||
@ -54,7 +54,10 @@
|
|||||||
(conj "library:read")
|
(conj "library:read")
|
||||||
|
|
||||||
(contains? permissions "comment:write")
|
(contains? permissions "comment:write")
|
||||||
(conj "comment:read"))
|
(conj "comment:read")
|
||||||
|
|
||||||
|
(contains? permissions "clipboard:write")
|
||||||
|
(conj "clipboard:read"))
|
||||||
|
|
||||||
plugin-url
|
plugin-url
|
||||||
(u/uri plugin-url)
|
(u/uri plugin-url)
|
||||||
|
|||||||
@ -7646,6 +7646,14 @@ msgstr ""
|
|||||||
msgid "workspace.plugins.permissions.allow-download"
|
msgid "workspace.plugins.permissions.allow-download"
|
||||||
msgstr "Start file downloads."
|
msgstr "Start file downloads."
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/plugins.cljs
|
||||||
|
msgid "workspace.plugins.permissions.clipboard-read"
|
||||||
|
msgstr "Read the contents of your clipboard."
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/plugins.cljs
|
||||||
|
msgid "workspace.plugins.permissions.clipboard-write"
|
||||||
|
msgstr "Read and write to your clipboard."
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/plugins.cljs:287
|
#: src/app/main/ui/workspace/plugins.cljs:287
|
||||||
msgid "workspace.plugins.permissions.allow-localstorage"
|
msgid "workspace.plugins.permissions.allow-localstorage"
|
||||||
msgstr "Store data in the browser."
|
msgstr "Store data in the browser."
|
||||||
|
|||||||
@ -10,7 +10,27 @@ export const openUIApi = z
|
|||||||
z.enum(['dark', 'light']),
|
z.enum(['dark', 'light']),
|
||||||
openUISchema.optional(),
|
openUISchema.optional(),
|
||||||
z.boolean().optional(),
|
z.boolean().optional(),
|
||||||
|
z.boolean().optional(),
|
||||||
|
z.boolean().optional(),
|
||||||
)
|
)
|
||||||
.implement((title, url, theme, options, allowDownloads) => {
|
.implement(
|
||||||
return createModal(title, url, theme, options, allowDownloads);
|
(
|
||||||
});
|
title,
|
||||||
|
url,
|
||||||
|
theme,
|
||||||
|
options,
|
||||||
|
allowDownloads,
|
||||||
|
allowClipboardRead,
|
||||||
|
allowClipboardWrite,
|
||||||
|
) => {
|
||||||
|
return createModal(
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
theme,
|
||||||
|
options,
|
||||||
|
allowDownloads,
|
||||||
|
allowClipboardRead,
|
||||||
|
allowClipboardWrite,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -104,4 +104,73 @@ describe('createModal', () => {
|
|||||||
expect(modal.wrapper.style.width).toEqual('200px');
|
expect(modal.wrapper.style.width).toEqual('200px');
|
||||||
expect(modal.wrapper.style.height).toEqual('200px');
|
expect(modal.wrapper.style.height).toEqual('200px');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set allow-clipboard-read attribute when allowClipboardRead is true', () => {
|
||||||
|
const theme: Theme = 'light';
|
||||||
|
|
||||||
|
createModal(
|
||||||
|
'Test Modal',
|
||||||
|
'https://example.com',
|
||||||
|
theme,
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(modalMock.setAttribute).toHaveBeenCalledWith(
|
||||||
|
'allow-clipboard-read',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
|
expect(modalMock.setAttribute).not.toHaveBeenCalledWith(
|
||||||
|
'allow-clipboard-write',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set allow-clipboard-write attribute when allowClipboardWrite is true', () => {
|
||||||
|
const theme: Theme = 'light';
|
||||||
|
|
||||||
|
createModal(
|
||||||
|
'Test Modal',
|
||||||
|
'https://example.com',
|
||||||
|
theme,
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(modalMock.setAttribute).toHaveBeenCalledWith(
|
||||||
|
'allow-clipboard-write',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
|
expect(modalMock.setAttribute).not.toHaveBeenCalledWith(
|
||||||
|
'allow-clipboard-read',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set both clipboard attributes when both are true', () => {
|
||||||
|
const theme: Theme = 'light';
|
||||||
|
|
||||||
|
createModal(
|
||||||
|
'Test Modal',
|
||||||
|
'https://example.com',
|
||||||
|
theme,
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(modalMock.setAttribute).toHaveBeenCalledWith(
|
||||||
|
'allow-clipboard-read',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
|
expect(modalMock.setAttribute).toHaveBeenCalledWith(
|
||||||
|
'allow-clipboard-write',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -10,6 +10,8 @@ export function createModal(
|
|||||||
theme: Theme,
|
theme: Theme,
|
||||||
options?: OpenUIOptions,
|
options?: OpenUIOptions,
|
||||||
allowDownloads?: boolean,
|
allowDownloads?: boolean,
|
||||||
|
allowClipboardRead?: boolean,
|
||||||
|
allowClipboardWrite?: boolean,
|
||||||
) {
|
) {
|
||||||
const modal = document.createElement('plugin-modal') as PluginModalElement;
|
const modal = document.createElement('plugin-modal') as PluginModalElement;
|
||||||
|
|
||||||
@ -44,6 +46,14 @@ export function createModal(
|
|||||||
modal.setAttribute('allow-downloads', 'true');
|
modal.setAttribute('allow-downloads', 'true');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allowClipboardRead) {
|
||||||
|
modal.setAttribute('allow-clipboard-read', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowClipboardWrite) {
|
||||||
|
modal.setAttribute('allow-clipboard-write', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
document.body.appendChild(modal);
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
return modal;
|
return modal;
|
||||||
|
|||||||
@ -99,6 +99,35 @@ describe('PluginModalElement', () => {
|
|||||||
modal.remove();
|
modal.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set iframe allow attribute for clipboard permissions', () => {
|
||||||
|
const modal = document.createElement('plugin-modal');
|
||||||
|
modal.setAttribute('title', 'Test modal');
|
||||||
|
modal.setAttribute('iframe-src', 'about:blank');
|
||||||
|
modal.setAttribute('allow-clipboard-read', 'true');
|
||||||
|
modal.setAttribute('allow-clipboard-write', 'true');
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
|
const iframe = modal.shadowRoot?.querySelector('iframe');
|
||||||
|
expect(iframe).toBeTruthy();
|
||||||
|
expect(iframe?.allow).toContain('clipboard-read');
|
||||||
|
expect(iframe?.allow).toContain('clipboard-write');
|
||||||
|
|
||||||
|
modal.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not set clipboard allow attributes when permissions are absent', () => {
|
||||||
|
const modal = document.createElement('plugin-modal');
|
||||||
|
modal.setAttribute('title', 'Test modal');
|
||||||
|
modal.setAttribute('iframe-src', 'about:blank');
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
|
const iframe = modal.shadowRoot?.querySelector('iframe');
|
||||||
|
expect(iframe).toBeTruthy();
|
||||||
|
expect(iframe?.allow).toBe('');
|
||||||
|
|
||||||
|
modal.remove();
|
||||||
|
});
|
||||||
|
|
||||||
it('should dispatch close event when close button is clicked', () => {
|
it('should dispatch close event when close button is clicked', () => {
|
||||||
const modal = document.createElement('plugin-modal');
|
const modal = document.createElement('plugin-modal');
|
||||||
modal.setAttribute('title', 'Test modal');
|
modal.setAttribute('title', 'Test modal');
|
||||||
|
|||||||
@ -52,6 +52,10 @@ export class PluginModalElement extends HTMLElement {
|
|||||||
const title = this.getAttribute('title');
|
const title = this.getAttribute('title');
|
||||||
const iframeSrc = this.getAttribute('iframe-src');
|
const iframeSrc = this.getAttribute('iframe-src');
|
||||||
const allowDownloads = this.getAttribute('allow-downloads') || false;
|
const allowDownloads = this.getAttribute('allow-downloads') || false;
|
||||||
|
const allowClipboardRead =
|
||||||
|
this.getAttribute('allow-clipboard-read') || false;
|
||||||
|
const allowClipboardWrite =
|
||||||
|
this.getAttribute('allow-clipboard-write') || false;
|
||||||
|
|
||||||
if (!title || !iframeSrc) {
|
if (!title || !iframeSrc) {
|
||||||
throw new Error('title and iframe-src attributes are required');
|
throw new Error('title and iframe-src attributes are required');
|
||||||
@ -95,7 +99,12 @@ export class PluginModalElement extends HTMLElement {
|
|||||||
|
|
||||||
const iframe = document.createElement('iframe');
|
const iframe = document.createElement('iframe');
|
||||||
iframe.src = iframeSrc;
|
iframe.src = iframeSrc;
|
||||||
iframe.allow = '';
|
|
||||||
|
const allowList: string[] = [];
|
||||||
|
if (allowClipboardRead) allowList.push('clipboard-read');
|
||||||
|
if (allowClipboardWrite) allowList.push('clipboard-write');
|
||||||
|
iframe.allow = allowList.join('; ');
|
||||||
|
|
||||||
iframe.sandbox.add(
|
iframe.sandbox.add(
|
||||||
'allow-scripts',
|
'allow-scripts',
|
||||||
'allow-forms',
|
'allow-forms',
|
||||||
|
|||||||
@ -19,6 +19,8 @@ export const manifestSchema = z.object({
|
|||||||
'comment:write',
|
'comment:write',
|
||||||
'allow:downloads',
|
'allow:downloads',
|
||||||
'allow:localstorage',
|
'allow:localstorage',
|
||||||
|
'clipboard:read',
|
||||||
|
'clipboard:write',
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -123,6 +123,8 @@ describe('createPluginManager', () => {
|
|||||||
'light',
|
'light',
|
||||||
{ width: 400, height: 300 },
|
{ width: 400, height: 300 },
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
expect(mockModal.setTheme).toHaveBeenCalledWith('light');
|
expect(mockModal.setTheme).toHaveBeenCalledWith('light');
|
||||||
expect(mockModal.addEventListener).toHaveBeenCalledWith(
|
expect(mockModal.addEventListener).toHaveBeenCalledWith(
|
||||||
|
|||||||
@ -27,6 +27,14 @@ export async function createPluginManager(
|
|||||||
(s) => s === 'allow:downloads',
|
(s) => s === 'allow:downloads',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const allowClipboardRead = !!manifest.permissions.find(
|
||||||
|
(s) => s === 'clipboard:read',
|
||||||
|
);
|
||||||
|
|
||||||
|
const allowClipboardWrite = !!manifest.permissions.find(
|
||||||
|
(s) => s === 'clipboard:write',
|
||||||
|
);
|
||||||
|
|
||||||
const themeChangeId = context.addListener('themechange', (theme: Theme) => {
|
const themeChangeId = context.addListener('themechange', (theme: Theme) => {
|
||||||
modal?.setTheme(theme);
|
modal?.setTheme(theme);
|
||||||
});
|
});
|
||||||
@ -91,7 +99,15 @@ export async function createPluginManager(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
modal = openUIApi(name, modalUrl, theme, options, allowDownloads);
|
modal = openUIApi(
|
||||||
|
name,
|
||||||
|
modalUrl,
|
||||||
|
theme,
|
||||||
|
options,
|
||||||
|
allowDownloads,
|
||||||
|
allowClipboardRead,
|
||||||
|
allowClipboardWrite,
|
||||||
|
);
|
||||||
|
|
||||||
modal.setTheme(theme);
|
modal.setTheme(theme);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user