From 92683e73396d7a457f8f9e2789cc9612d588c4f5 Mon Sep 17 00:00:00 2001
From: Jan-Hendrik Willms <tleilax+studip@gmail.com>
Date: Fri, 24 Mar 2023 20:45:45 +0000
Subject: [PATCH] do not open iframe with invalid url and show hint in vue
 component, fixes #2169, fixes #2168

Closes #2169 and #2168

Merge request studip/studip!1640
---
 .../question_types/info/info.php              |  4 +--
 lib/models/QuestionnaireInfo.php              | 11 ++++++++
 .../questionnaires/QuestionnaireInfoEdit.vue  | 27 ++++++++++++++-----
 3 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/app/views/questionnaire/question_types/info/info.php b/app/views/questionnaire/question_types/info/info.php
index 04bae4fa8a3..2faf8503e0c 100644
--- a/app/views/questionnaire/question_types/info/info.php
+++ b/app/views/questionnaire/question_types/info/info.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * @var QuestionnaireQuestion $vote
+ * @var QuestionnaireInfo $vote
  */
 ?>
 
@@ -9,7 +9,7 @@
         <?= Icon::create('info-circle', Icon::ROLE_INFO)->asImg(20) ?>
     </div>
     <div class="description">
-        <? if (isset($vote->questiondata['url']) && trim($vote->questiondata['url'])) : ?>
+        <? if ($vote->hasValidURL()) : ?>
             <iframe <?= is_internal_url($vote->questiondata['url']) ? 'sandbox="allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-presentation allow-scripts"' : '' ?>
                     src="<?= htmlReady($vote->questiondata['url']) ?>"></iframe>
         <? endif ?>
diff --git a/lib/models/QuestionnaireInfo.php b/lib/models/QuestionnaireInfo.php
index 0b1493c5d6b..ffc09ff0d09 100644
--- a/lib/models/QuestionnaireInfo.php
+++ b/lib/models/QuestionnaireInfo.php
@@ -63,4 +63,15 @@ class QuestionnaireInfo extends QuestionnaireQuestion implements QuestionType
     {
         return [];
     }
+
+    /**
+     * Return whether a given url is valid.
+     * @return bool
+     */
+    public function hasValidURL(): bool
+    {
+        return !empty($this->questiondata['url'])
+            && trim($this->questiondata['url'])
+            && filter_var($this->questiondata['url'], FILTER_VALIDATE_URL);
+    }
 }
diff --git a/resources/vue/components/questionnaires/QuestionnaireInfoEdit.vue b/resources/vue/components/questionnaires/QuestionnaireInfoEdit.vue
index 57d6c1310fe..bc5e82970fd 100644
--- a/resources/vue/components/questionnaires/QuestionnaireInfoEdit.vue
+++ b/resources/vue/components/questionnaires/QuestionnaireInfoEdit.vue
@@ -2,7 +2,8 @@
     <div class="vote_edit">
         <label>
             {{ $gettext('Link eines Videos oder einer anderen Informationsseite (optional)') }}
-            <input type="text" v-model="val_clone.url" ref="autofocus">
+            <input type="url" v-model="val_clone.url" ref="infoUrl"
+                   @input="checkValidity()">
         </label>
 
         <div class="formpart">
@@ -24,7 +25,7 @@ export default {
         value: {
             type: Object,
             required: false,
-            default: function () {
+            default() {
                 return {
                     url: '',
                     description: ''
@@ -36,14 +37,26 @@ export default {
             required: false
         }
     },
-    data: function () {
+    data () {
         return {
-            val_clone: ''
+            val_clone: this.value,
         };
     },
-    mounted: function () {
-        this.val_clone = this.value;
-        this.$refs.autofocus.focus();
+    methods: {
+        checkValidity() {
+            this.$refs.infoUrl.setCustomValidity('');
+
+            if (!this.$refs.infoUrl.checkValidity()) {
+                this.$refs.infoUrl.setCustomValidity(
+                    this.$gettext('Der eingegebene Link ist nicht korrekt und wird nicht angezeigt werden.')
+                );
+                this.$refs.infoUrl.reportValidity();
+            }
+        }
+    },
+    mounted() {
+        this.$refs.infoUrl.focus();
+        this.checkValidity();
     },
     watch: {
         value (new_val) {
-- 
GitLab