From deccccd177930ef5498d77a20a6f630164de691c Mon Sep 17 00:00:00 2001
From: Ron Lucke <lucke@elan-ev.de>
Date: Mon, 25 Mar 2024 09:17:55 +0000
Subject: [PATCH] fix #3823

Closes #3823

Merge request studip/studip!2702
---
 lib/models/Courseware/BlockTypes/IFrame.json  |  5 +--
 .../blocks/CoursewareIframeBlock.vue          | 31 ++++++++++++++++++-
 2 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/lib/models/Courseware/BlockTypes/IFrame.json b/lib/models/Courseware/BlockTypes/IFrame.json
index c631e248962..0737ab0536f 100644
--- a/lib/models/Courseware/BlockTypes/IFrame.json
+++ b/lib/models/Courseware/BlockTypes/IFrame.json
@@ -3,7 +3,8 @@
     "type": "object",
     "properties": {
         "url": {
-            "type": "string"
+            "type": "string",
+            "format": "uri"
         },
         "title": {
             "type": "string"
@@ -36,4 +37,4 @@
     "required": [
     ],
     "additionalProperties": true
-}
\ No newline at end of file
+}
diff --git a/resources/vue/components/courseware/blocks/CoursewareIframeBlock.vue b/resources/vue/components/courseware/blocks/CoursewareIframeBlock.vue
index d628b12e78c..be56ab413f2 100644
--- a/resources/vue/components/courseware/blocks/CoursewareIframeBlock.vue
+++ b/resources/vue/components/courseware/blocks/CoursewareIframeBlock.vue
@@ -39,7 +39,7 @@
                             </label>
                             <label>
                                 {{ $gettext('URL') }}
-                                <input type="text" v-model="currentUrl" @change="setProtocol" />
+                                <input type="text" v-model="currentUrl" @change="updateUrl" />
                             </label>
                             <label>
                                 {{ $gettext('Höhe') }}
@@ -139,6 +139,7 @@ export default {
         return {
             currentTitle: '',
             currentUrl: '',
+            currentUrlIsValid: true,
             currentHeight: '',
             currentSubmitUserId: '',
             currentSubmitParam: '',
@@ -198,6 +199,7 @@ export default {
     },
     methods: {
         ...mapActions({
+            companionError: 'companionError',
             updateBlock: 'updateBlockInContainer',
         }),
         initCurrentData() {
@@ -228,8 +230,35 @@ export default {
                 }
             }
         },
+        validateUrl() {
+            this.currentUrlIsValid = this.isValidUrl(this.currentUrl);
+        },
+        isValidUrl(urlString) {
+            const urlPattern = new RegExp(
+                '^(https?:\\/\\/)?' + // validate protocol
+                    '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name
+                    '((\\d{1,3}\\.){3}\\d{1,3}))' + // validate OR ip (v4) address
+                    '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path
+                    '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string
+                    '(\\#[-a-z\\d_]*)?$',
+                'i'
+            ); // validate fragment locator
+
+            return !!urlPattern.test(urlString);
+        },
+
+        updateUrl() {
+            this.setProtocol();
+            this.validateUrl();
+        },
 
         storeBlock() {
+            if (!this.currentUrlIsValid) {
+                this.companionError({
+                    info: this.$gettext('Bitte geben Sie eine gültige URL ein.')
+                });
+                return false;
+            }
             let attributes = {};
             attributes.payload = {};
             attributes.payload.title = this.currentTitle;
-- 
GitLab