From 1533d5253c5ae416d6f1e448777faac327a1fa5c Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 20 Sep 2022 12:18:19 +0200 Subject: [PATCH] feat: Allow embedding forms within other websites * Add embedded endpoint for page controller and allow inserting submissions without CSFR as anonymous submissions for public shares. * Added submenu entry for copying the embedding code to the clipboard and added documentation on how to use the embedded view. * Switched to `vue-clipboard2` to allow copying to clipboard from submenu entry (allows setting a container for the copy action). Signed-off-by: Ferdinand Thiessen --- appinfo/routes.php | 7 + css/embedded.css | 25 ++++ docs/Embedding.md | 42 ++++++ lib/Controller/ApiController.php | 2 +- lib/Controller/PageController.php | 29 +++- src/FormsSubmit.vue | 3 +- .../SidebarTabs/SharingSidebarTab.vue | 8 ++ src/mixins/ShareLinkMixin.js | 21 +++ src/views/Submit.vue | 2 +- tests/Unit/Controller/PageControllerTest.php | 135 ++++++++++++++++++ 10 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 css/embedded.css create mode 100644 docs/Embedding.md create mode 100644 tests/Unit/Controller/PageControllerTest.php diff --git a/appinfo/routes.php b/appinfo/routes.php index 52495232..bfb2e009 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -46,6 +46,13 @@ return [ 'verb' => 'GET' ], + // Embedded View + [ + 'name' => 'page#embedded_form_view', + 'url' => '/embed/{hash}', + 'verb' => 'GET' + ], + // Internal views [ 'name' => 'page#views', diff --git a/css/embedded.css b/css/embedded.css new file mode 100644 index 00000000..6deb958c --- /dev/null +++ b/css/embedded.css @@ -0,0 +1,25 @@ +/** + * @copyright Copyright (c) 2022 Ferdinand Thiessen + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* Remove background for embedded view */ +body { + background-color: var(--color-main-background) !important; + background-image: none !important; +} diff --git a/docs/Embedding.md b/docs/Embedding.md new file mode 100644 index 00000000..914a716f --- /dev/null +++ b/docs/Embedding.md @@ -0,0 +1,42 @@ +# Embedding +Besides sharing and using the [API](./API.md) for custom forms it is possible to embed forms inside external +websites. + +## Obtaining the embedding code + +For embedding a form it is **required** to create a *public share link*.\ +The embedding code can be copied from the *sharing sidebar* or crafted manually by using the public share link: + +If the public share link looks like this:\ +`https://SERVER_DOMAIN/apps/forms/s/SHARE_HASH` + +The embeddable URL looks like this:\ +`https://SERVER_DOMAIN/apps/forms/embed/SHARE_HASH` + +Using the copy-embedding-code button on the *sharing sidebar* will automatically generate ready-to-use HTML code for embedding which looks like this: +```html + +``` +The size parameters are based on our default forms styling. + +## Custom styling +To apply custom styles on the embedded forms the [Custom CSS App](https://apps.nextcloud.com/apps/theming_customcss) can be used. + +The embedded form provides the `app-forms-embedded` class, so you can apply your styles.\ +For example if you want the form to be displayed without margins you can use this: +```css +#content-vue.app-forms-embedded { + width: 100%; + height: 100%; + border-radius: 0; + margin: 0; +} +``` + +Or if you want the form to fill the screen: +```css +#content-vue.app-forms-embedded .app-content header, +#content-vue.app-forms-embedded .app-content form { + max-width: unset; +} +``` \ No newline at end of file diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index bec161ec..1a4e699f 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -1104,7 +1104,7 @@ class ApiController extends OCSController { $submission->setFormId($formId); $submission->setTimestamp(time()); - // If not logged in or anonymous use anonID + // If not logged in, anonymous, or embedded use anonID if (!$this->currentUser || $form->getIsAnonymous()) { $anonID = "anon-user-". hash('md5', strval(time() + rand())); $submission->setUserId($anonID); diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 3026c532..b90293a7 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -36,6 +36,7 @@ use OCA\Forms\Service\FormsService; use OCP\Accounts\IAccountManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Template\PublicTemplateResponse; @@ -217,7 +218,6 @@ class PageController extends Controller { } } } - return $response; } @@ -239,4 +239,31 @@ class PageController extends Controller { ]); } } + + /** + * @NoAdminRequired + * @PublicPage + * @NoCSRFRequired + * + * @param string $hash + * @return Response + */ + public function embeddedFormView(string $hash): Response { + Util::addStyle($this->appName, 'embedded'); + $response = $this->publicLinkView($hash)->renderAs(TemplateResponse::RENDER_AS_BASE); + + $this->initialState->provideInitialState('isEmbedded', true); + + return $this->setEmbeddedCSP($response); + } + + protected function setEmbeddedCSP(TemplateResponse $response) { + $policy = new ContentSecurityPolicy(); + $policy->addAllowedFrameAncestorDomain('*'); + + $response->addHeader('X-Frame-Options', 'ALLOW'); + $response->setContentSecurityPolicy($policy); + + return $response; + } } diff --git a/src/FormsSubmit.vue b/src/FormsSubmit.vue index c8c1bec9..b13df869 100644 --- a/src/FormsSubmit.vue +++ b/src/FormsSubmit.vue @@ -21,7 +21,7 @@ -->