From 1c55c6f41a32a1aa830b29798d9afd1fb9e3d8dc Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Willms <tleilax+studip@gmail.com> Date: Wed, 19 Oct 2022 11:32:37 +0000 Subject: [PATCH] lint .vue files as well, fixes #1696 Closes #1696 Merge request studip/studip!772 --- .eslintrc.json | 10 +- package.json | 2 +- resources/assets/javascripts/bootstrap/oer.js | 18 ++-- resources/assets/javascripts/lib/blubber.js | 38 ++++--- resources/assets/javascripts/lib/files.js | 24 +++-- resources/assets/javascripts/lib/oer.js | 22 ++-- .../vue/components/BlubberGlobalstream.vue | 19 ++-- resources/vue/components/BlubberThread.vue | 101 +++++++++--------- .../vue/components/BlubberThreadWidget.vue | 9 +- .../vue/components/CacheAdministration.vue | 8 +- resources/vue/components/Datetimepicker.vue | 2 +- resources/vue/components/EditableList.vue | 38 +++---- resources/vue/components/FilesTable.vue | 49 +++++---- resources/vue/components/I18nTextarea.vue | 37 ++++--- .../vue/components/MyCoursesColorPicker.vue | 2 +- .../vue/components/MyCoursesNavigation.vue | 2 +- resources/vue/components/MyCoursesTables.vue | 2 +- resources/vue/components/MyCoursesTiles.vue | 8 +- resources/vue/components/StudipDialog.vue | 3 +- resources/vue/components/StudipFileSize.vue | 2 + resources/vue/components/StudipMessageBox.vue | 6 +- .../CoursewareAccordionContainer.vue | 4 +- .../courseware/CoursewareActionWidget.vue | 2 - .../CoursewareBiographyAchievementsBlock.vue | 2 - .../CoursewareBiographyGoalsBlock.vue | 2 + ...ewareBiographyPersonalInformationBlock.vue | 2 - .../courseware/CoursewareChartBlock.vue | 2 +- .../courseware/CoursewareContentBookmarks.vue | 1 + .../CoursewareContentOverviewElements.vue | 4 +- .../CoursewareContentPermissions.vue | 1 + .../courseware/CoursewareDefaultBlock.vue | 8 -- .../courseware/CoursewareDefaultContainer.vue | 1 - .../courseware/CoursewareDialogCardsBlock.vue | 6 +- .../courseware/CoursewareHeadlineBlock.vue | 8 +- .../courseware/CoursewareImageMapBlock.vue | 14 +-- .../courseware/CoursewareKeyPointBlock.vue | 5 +- .../CoursewareManagerCopySelector.vue | 6 +- .../courseware/CoursewareManagerElement.vue | 5 +- .../CoursewareManagerLinkSelector.vue | 8 +- .../CoursewareStructuralElement.vue | 16 +-- .../components/courseware/CoursewareTabs.vue | 8 +- .../courseware/CoursewareTabsContainer.vue | 4 +- .../courseware/CoursewareTimelineBlock.vue | 6 +- .../courseware/CoursewareToolsAdmin.vue | 1 - 44 files changed, 269 insertions(+), 249 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index d9a963e6315..55c17a0dfa1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,13 +1,16 @@ { - "parser": "@babel/eslint-parser", "parserOptions": { + "parser": "@babel/eslint-parser", "sourceType": "module" }, "env": { "browser": true, "es6": true }, - "extends": "eslint:recommended", + "extends": [ + "eslint:recommended", + "plugin:vue/essential" + ], "globals": { "STUDIP": "writable", "CKEDITOR": "writable", @@ -15,6 +18,9 @@ "_": "writable", "jQuery": "writable" }, + "plugins": [ + "vue" + ], "rules": { "no-unused-vars": "off", diff --git a/package.json b/package.json index 18f37b309e3..7d8898bebd5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Stud.IP", "private": true, "scripts": { - "lint": "eslint resources/assets/javascripts resources/vue", + "lint": "eslint --ext .js,.vue resources/assets/javascripts resources/vue", "css-lint": "stylelint resources/assets/stylesheets", "webpack-dev": "webpack --config webpack.dev.js --mode development", "webpack-prod": "webpack --config webpack.prod.js --mode production", diff --git a/resources/assets/javascripts/bootstrap/oer.js b/resources/assets/javascripts/bootstrap/oer.js index eff927f4797..2b471496a64 100644 --- a/resources/assets/javascripts/bootstrap/oer.js +++ b/resources/assets/javascripts/bootstrap/oer.js @@ -57,14 +57,16 @@ STUDIP.ready(() => { STUDIP.Vue.load().then(({createApp}) => { STUDIP.OER.EditApp = createApp({ el: '.oercampus_editmaterial', - data: { - name: $('.oercampus_editmaterial input.oername').val(), - logo_url: $('.oercampus_editmaterial .logo_file').data("oldurl"), - customlogo: $('.oercampus_editmaterial .logo_file').data("customlogo"), - filename: $('.oercampus_editmaterial .file.drag-and-drop').data("filename"), - filesize: $('.oercampus_editmaterial .file.drag-and-drop').data("filesize"), - tags: $('.oercampus_editmaterial .oer_tags').data("defaulttags"), - minimumTags: 5 + data() { + return { + name: $('.oercampus_editmaterial input.oername').val(), + logo_url: $('.oercampus_editmaterial .logo_file').data("oldurl"), + customlogo: $('.oercampus_editmaterial .logo_file').data("customlogo"), + filename: $('.oercampus_editmaterial .file.drag-and-drop').data("filename"), + filesize: $('.oercampus_editmaterial .file.drag-and-drop').data("filesize"), + tags: $('.oercampus_editmaterial .oer_tags').data("defaulttags"), + minimumTags: 5 + }; }, mounted: function () { jQuery("#difficulty_slider_edit").slider({ diff --git a/resources/assets/javascripts/lib/blubber.js b/resources/assets/javascripts/lib/blubber.js index 6a72382e7b6..777f221b52b 100644 --- a/resources/assets/javascripts/lib/blubber.js +++ b/resources/assets/javascripts/lib/blubber.js @@ -19,13 +19,15 @@ const Blubber = { STUDIP.Vue.load().then(({createApp}) => { STUDIP.Blubber.App = createApp({ el: '#layout_container', - data: { - threads: $('.blubber_threads_widget').data('threads_data'), - thread_data: panel_data.thread_data, - active_thread: panel_data.active_thread, - threads_more_down: panel_data.threads_more_down, - waiting: false, - display_context_posting: 0 + data() { + return { + threads: $('.blubber_threads_widget').data('threads_data'), + thread_data: panel_data.thread_data, + active_thread: panel_data.active_thread, + threads_more_down: panel_data.threads_more_down, + waiting: false, + display_context_posting: 0 + }; }, methods: { changeActiveThread: function (thread_id) { @@ -96,13 +98,15 @@ const Blubber = { STUDIP.Vue.load().then(({createApp}) => { createApp({ el: this, - data: { - threads: panel_data.threads_data, - thread_data: panel_data.thread_data, - active_thread: panel_data.active_thread, - threads_more_down: panel_data.threads_more_down, - waiting: false, - display_context_posting: 0 + data () { + return { + threads: panel_data.threads_data, + thread_data: panel_data.thread_data, + active_thread: panel_data.active_thread, + threads_more_down: panel_data.threads_more_down, + waiting: false, + display_context_posting: 0 + }; }, components, }); @@ -186,8 +190,10 @@ const Blubber = { let components = STUDIP.Blubber.components; return createApp({ el: '#blubber_contact_ids', - data: { - users: [] + data () { + return { + users: [] + }; }, methods: { addUser: function (user_id, name) { diff --git a/resources/assets/javascripts/lib/files.js b/resources/assets/javascripts/lib/files.js index 9d06a18ea15..c013107ec23 100644 --- a/resources/assets/javascripts/lib/files.js +++ b/resources/assets/javascripts/lib/files.js @@ -10,11 +10,13 @@ const Files = { STUDIP.Vue.load().then(({createApp}) => { this.filesapp = createApp({ el: "#layout_content", - data: { - "files": jQuery("#files_table_form").data("files") || [], - "folders": jQuery("#files_table_form").data("folders") || [], - "topfolder": jQuery("#files_table_form").data("topfolder"), - "breadcrumbs": jQuery("#files_table_form").data("breadcrumbs") || [] + data() { + return { + files: jQuery("#files_table_form").data("files") || [], + folders: jQuery("#files_table_form").data("folders") || [], + topfolder: jQuery("#files_table_form").data("topfolder"), + breadcrumbs: jQuery("#files_table_form").data("breadcrumbs") || [] + }; }, methods: { hasFilesOfType (type) { @@ -45,11 +47,13 @@ const Files = { STUDIP.Vue.load().then(({createApp}) => { createApp({ el: table, - data: { - "files": jQuery(table).data("files") || [], - "folders": jQuery(table).data("folders") || [], - "topfolder": jQuery(table).data("topfolder"), - "breadcrumbs": jQuery(table).data("breadcrumbs") || [] + data() { + return { + files: jQuery(table).data("files") || [], + folders: jQuery(table).data("folders") || [], + topfolder: jQuery(table).data("topfolder"), + breadcrumbs: jQuery(table).data("breadcrumbs") || [] + }; }, components: { FilesTable, }, }); diff --git a/resources/assets/javascripts/lib/oer.js b/resources/assets/javascripts/lib/oer.js index dac19ec56ad..0542a1e5f36 100644 --- a/resources/assets/javascripts/lib/oer.js +++ b/resources/assets/javascripts/lib/oer.js @@ -37,16 +37,18 @@ const OER = { STUDIP.Vue.load().then(({createApp}) => { STUDIP.OER.Search = createApp({ el: ".oer_search", - data: { - browseMode: false, - tags: $(".oer_search").data("tags"), - tagHistory: [], - searchtext: "", - activeFilterPanel: false, - difficulty: [1, 12], - category: null, - results: false, - material_select_url_template: $(".oer_search").data("material_select_url_template") + data() { + return { + browseMode: false, + tags: $(".oer_search").data("tags"), + tagHistory: [], + searchtext: "", + activeFilterPanel: false, + difficulty: [1, 12], + category: null, + results: false, + material_select_url_template: $(".oer_search").data("material_select_url_template") + }; }, methods: { sync_search_text: function () { diff --git a/resources/vue/components/BlubberGlobalstream.vue b/resources/vue/components/BlubberGlobalstream.vue index 0c4976c887f..548e1fd57ba 100644 --- a/resources/vue/components/BlubberGlobalstream.vue +++ b/resources/vue/components/BlubberGlobalstream.vue @@ -3,7 +3,7 @@ <div class="scrollable_area" v-scroll> <blubber-public-composer></blubber-public-composer> <ol class="postings" aria-live="polite"> - <li class="more" v-if="stream_data.more_up"> + <li class="more" v-if="streamData.more_up"> <studip-asset-img file="ajax-indicator-black.svg" width="20"></studip-asset-img> </li> @@ -37,7 +37,8 @@ name: 'blubber-globalstream', data: function () { return { - already_loading_down: 0 + already_loading_down: 0, + streamData: this.stream_data, }; }, props: ['stream_data', 'more_down'], @@ -52,14 +53,14 @@ addPosting: function (posting) { let exists = false; for (let i in this.stream_data) { - if (this.stream_data[i].thread_id === posting.thread_id) { + if (this.streamData[i].thread_id === posting.thread_id) { exists = true; return; } } if (!exists) { posting.class = posting.class + " new"; - this.stream_data.push(posting); + this.streamData.push(posting); this.$nextTick(() => { STUDIP.Markup.element($(this.$el).find(`.postings > li[data-thread_id="${posting.thread_id}"]`)); }); @@ -74,8 +75,8 @@ }); }, computed: { - sortedPostings: function () { - return this.stream_data.sort((a, b) => b.mkdate - a.mkdate); + sortedPostings() { + return [...this.streamData].sort((a, b) => b.mkdate - a.mkdate); } }, directives: { @@ -94,9 +95,9 @@ stream.already_loading_down = 1; let earliest_mkdate = null; - for (let i in stream.stream_data) { - if ((earliest_mkdate === null) || stream.stream_data[i].mkdate < earliest_mkdate) { - earliest_mkdate = stream.stream_data[i].mkdate; + for (let i in stream.streamData) { + if ((earliest_mkdate === null) || stream.streamData[i].mkdate < earliest_mkdate) { + earliest_mkdate = stream.streamData[i].mkdate; } } //load older comments diff --git a/resources/vue/components/BlubberThread.vue b/resources/vue/components/BlubberThread.vue index bb742feaa7d..a3a5d7027a4 100644 --- a/resources/vue/components/BlubberThread.vue +++ b/resources/vue/components/BlubberThread.vue @@ -1,13 +1,13 @@ <template> <div class="blubber_thread" :class="{dragover: dragging}" - :id="'blubberthread_' + thread_data.thread_posting.thread_id" + :id="'blubberthread_' + threadData.thread_posting.thread_id" @dragover.prevent="dragover" @dragleave.prevent="dragleave" @drop.prevent="upload"> - <div class="responsive-visible context_info" v-if="thread_data.notifications"> + <div class="responsive-visible context_info" v-if="threadData.notifications"> <a href="#" @click.prevent="toggleFollow()" class="followunfollow" - :class="{unfollowed: !thread_data.followed}" + :class="{unfollowed: !threadData.followed}" :title="$gettext('Benachrichtigungen für diese Konversation abstellen.')" :data-thread_id="thread_data.thread_posting.thread_id"> <StudipIcon shape="decline" :size="20" class="follow text-bottom"></StudipIcon> @@ -17,23 +17,23 @@ </div> <div class="scrollable_area" v-scroll> <div class="all_content"> - <div class="thread_posting" v-if="hasContent(thread_data.thread_posting.content)"> + <div class="thread_posting" v-if="hasContent(threadData.thread_posting.content)"> <div class="contextinfo"> - <studip-date-time :timestamp="thread_data.thread_posting.mkdate" :relative="true"></studip-date-time> - <a :href="getUserProfileURL(thread_data.thread_posting.user_id, thread_data.thread_posting.user_username)">{{ thread_data.thread_posting.user_name }}</a> - <a :href="getUserProfileURL(thread_data.thread_posting.user_id, thread_data.thread_posting.user_username)" class="avatar" :style="{ backgroundImage: 'url(' + thread_data.thread_posting.avatar + ')' }"></a> + <studip-date-time :timestamp="threadData.thread_posting.mkdate" :relative="true"></studip-date-time> + <a :href="getUserProfileURL(threadData.thread_posting.user_id, threadData.thread_posting.user_username)">{{ threadData.thread_posting.user_name }}</a> + <a :href="getUserProfileURL(threadData.thread_posting.user_id, threadData.thread_posting.user_username)" class="avatar" :style="{ backgroundImage: 'url(' + threadData.thread_posting.avatar + ')' }"></a> </div> - <div class="content" v-html="thread_data.thread_posting.html"></div> + <div class="content" v-html="threadData.thread_posting.html"></div> <div class="link_to_comments"></div> </div> - <div v-if="!hasContent(thread_data.thread_posting.content) && !thread_data.comments.length" class="empty_blubber_background"> + <div v-if="!hasContent(threadData.thread_posting.content) && !threadData.comments.length" class="empty_blubber_background"> <div v-translate>Starte die Konversation jetzt!</div> </div> <ol class="comments" aria-live="polite"> - <li class="more" v-if="thread_data.more_up"> + <li class="more" v-if="threadData.more_up"> <studip-asset-img file="ajax-indicator-black.svg" width="20"></studip-asset-img> </li> @@ -61,14 +61,14 @@ </div> </li> - <li class="more" v-if="thread_data.more_down"> + <li class="more" v-if="threadData.more_down"> <studip-asset-img file="ajax-indicator-black.svg" width="20"></studip-asset-img> </li> </ol> </div> </div> - <div class="writer" v-if="thread_data.thread_posting.commentable"> + <div class="writer" v-if="threadData.thread_posting.commentable"> <studip-icon shape="blubber" size="30" role="info"></studip-icon> <textarea :placeholder="writerTextareaPlaceholder" @keyup.enter.exact="submit" @@ -93,7 +93,8 @@ return { already_loading_up: 0, already_loading_down: 0, - dragging: false + dragging: false, + threadData: this.thread_data }; }, props: ['thread_data'], @@ -102,9 +103,9 @@ if (!text || typeof text !== "string") { text = $(this.$el).find(".writer textarea").val(); $(this.$el).find(".writer textarea").val(""); - if (this.thread_data.thread_posting.thread_id) { + if (this.threadData.thread_posting.thread_id) { sessionStorage.removeItem( - 'BlubberMemory-Writer-' + this.thread_data.thread_posting.thread_id + 'BlubberMemory-Writer-' + this.threadData.thread_posting.thread_id ); } } @@ -126,14 +127,14 @@ let thread = this; //AJAX-Request ... - STUDIP.api.POST(`blubber/threads/${this.thread_data.thread_posting.thread_id}/comments`, { + STUDIP.api.POST(`blubber/threads/${this.threadData.thread_posting.thread_id}/comments`, { data: { content: text } }).then(data => { // Check following state - if (this.thread_data.notifications) { - STUDIP.api.GET(`blubber/threads/${this.thread_data.thread_posting.thread_id}/follow`).then(followed => { + if (this.threadData.notifications) { + STUDIP.api.GET(`blubber/threads/${this.threadData.thread_posting.thread_id}/follow`).then(followed => { jQuery('.followunfollow').toggleClass('unfollowed', !followed); }); } @@ -158,9 +159,9 @@ }, saveCommentToSession (event) { let value = event.target.value; - if (this.thread_data.thread_posting.thread_id) { + if (this.threadData.thread_posting.thread_id) { sessionStorage.setItem( - `BlubberMemory-Writer-${this.thread_data.thread_posting.thread_id}`, + `BlubberMemory-Writer-${this.threadData.thread_posting.thread_id}`, value ); } @@ -195,19 +196,19 @@ this.$nextTick(() => { STUDIP.Markup.element($(this.$el).find(`.comments > li[data-comment_id="${comment.comment_id}"]`)); }); - for (let i in this.thread_data.comments) { - if (this.thread_data.comments[i].comment_id === comment.comment_id) { - this.thread_data.comments[i].content = comment.content; - this.thread_data.comments[i].html = comment.html; + for (let i in this.threadData.comments) { + if (this.threadData.comments[i].comment_id === comment.comment_id) { + this.threadData.comments[i].content = comment.content; + this.threadData.comments[i].html = comment.html; return; } } - this.thread_data.comments.push(comment); + this.threadData.comments.push(comment); }, removeComment (comment_id) { - this.thread_data.comments.forEach((comment, i) => { + this.threadData.comments.forEach((comment, i) => { if (comment.comment_id === comment_id) { - this.$delete(this.thread_data.comments, i); + this.$delete(this.threadData.comments, i); } }); }, @@ -295,7 +296,7 @@ } let comment_id = $(li).data('comment_id'); let comment_data = null; - this.thread_data.comments.forEach((comment, i) => { + this.threadData.comments.forEach((comment, i) => { if (comment.comment_id === comment_id) { comment_data = comment; } @@ -314,7 +315,7 @@ let comment_id = li.data('comment_id'); let content = li.find('textarea').val(); - thread.thread_data.comments.forEach((comment) => { + thread.threadData.comments.forEach((comment) => { if (comment.comment_id === comment_id) { comment.html = content; } @@ -322,13 +323,13 @@ li.find('.content').removeClass('editing'); - STUDIP.api.PUT(`blubber/threads/${this.thread_data.thread_posting.thread_id}/comments/${comment_id}`, { + STUDIP.api.PUT(`blubber/threads/${this.threadData.thread_posting.thread_id}/comments/${comment_id}`, { data: { content: content }, }).done((output) => { if (this.hasContent(output.content)) { - thread.thread_data.comments.forEach((comment) => { + thread.threadData.comments.forEach((comment) => { if (comment.comment_id === comment_id) { comment.html = output.html; comment.content = output.content; @@ -359,10 +360,10 @@ }, toggleFollow () { STUDIP.Blubber.followunfollow( - this.thread_data.thread_posting.thread_id, - !this.thread_data.followed + this.threadData.thread_posting.thread_id, + !this.threadData.followed ).done(state => { - this.thread_data.followed = state; + this.threadData.followed = state; }); }, hasContent (input) { @@ -390,15 +391,15 @@ thread.$root.display_context_posting = top >= $(el).find('.all_content .thread_posting').height() ? 1 : 0; - if (thread.thread_data.more_up && top < 1000 && !thread.already_loading_up) { + if (thread.threadData.more_up && top < 1000 && !thread.already_loading_up) { thread.already_loading_up = 1; - let earliest_mkdate = thread.thread_data.comments.reduce((min, comment) => { + let earliest_mkdate = thread.threadData.comments.reduce((min, comment) => { return min === null ? comment.mkdate : Math.min(min, comment.mkdate); }, null); //load older comments - STUDIP.api.GET(`blubber/threads/${thread.thread_data.thread_posting.thread_id}/comments`, { + STUDIP.api.GET(`blubber/threads/${thread.threadData.thread_posting.thread_id}/comments`, { data: { modifier: 'olderthan', timestamp: earliest_mkdate, @@ -407,7 +408,7 @@ }).done((data) => { top = $(el).scrollTop(); thread.addComments(data.comments, false); - thread.thread_data.more_up = data.more_up; + thread.threadData.more_up = data.more_up; thread.$nextTick(function () { //scroll to the position where we were: let new_height = $(el).find(".all_content").height(); @@ -421,15 +422,15 @@ }); } - if (thread.thread_data.more_down && (top > $(thread).find(".scrollable_area .all_content").height() - 1000) && !thread.already_loading_down) { + if (thread.threadData.more_down && (top > $(thread).find(".scrollable_area .all_content").height() - 1000) && !thread.already_loading_down) { thread.already_loading_down = 1; - let latest_mkdate = thread.thread_data.comments.reduce((max, comment) => { + let latest_mkdate = thread.threadData.comments.reduce((max, comment) => { return Math.max(max, comment.mkdate); }, null); //load newer comments - STUDIP.api.GET(`blubber/threads/${thread.thread_data.thread_posting.thread_id}/comments`, { + STUDIP.api.GET(`blubber/threads/${thread.threadData.thread_posting.thread_id}/comments`, { data: { modifier: 'newerthan', timestamp: latest_mkdate, @@ -437,7 +438,7 @@ } }).done((data) => { thread.addComments(data.comments, false); - thread.thread_data.more_down = data.more_down; + thread.threadData.more_down = data.more_down; }).always(() => { thread.already_loading_down = 0; }); @@ -448,7 +449,7 @@ }, mounted () { //when everything is initialized this.$nextTick(function () { - if (this.thread_data.comments.length > 0) { + if (this.threadData.comments.length > 0) { this.scrollDown(); } @@ -462,8 +463,8 @@ STUDIP.Markup.element(this); }); - if (this.thread_data.thread_posting.thread_id) { - let memory = sessionStorage.getItem(`BlubberMemory-Writer-${this.thread_data.thread_posting.thread_id}`); + if (this.threadData.thread_posting.thread_id) { + let memory = sessionStorage.getItem(`BlubberMemory-Writer-${this.threadData.thread_posting.thread_id}`); if (memory) { $(this.$el) .find('.writer').addClass('filled') @@ -474,24 +475,24 @@ }, computed: { sortedComments () { - return this.thread_data.comments.sort((a, b) => a.mkdate - b.mkdate); + return [...this.threadData.comments].sort((a, b) => a.mkdate - b.mkdate); }, writerTextareaPlaceholder() { - return this.hasContent(this.thread_data.thread_posting.content) + return this.hasContent(this.threadData.thread_posting.content) ? this.$gettext('Kommentar schreiben. Enter zum Abschicken.') : this.$gettext('Nachricht schreiben. Enter zum Abschicken.'); } }, updated () { this.$nextTick(function () { - if (this.thread_data.thread_posting.thread_id) { - let memory = sessionStorage.getItem('BlubberMemory-Writer-' + this.thread_data.thread_posting.thread_id); + if (this.threadData.thread_posting.thread_id) { + let memory = sessionStorage.getItem('BlubberMemory-Writer-' + this.threadData.thread_posting.thread_id); $(this.$el).find('.writer textarea').val(memory); } }); }, watch: { - thread_data (new_data, old_data) { + threadData (new_data, old_data) { if (new_data.thread_posting.thread_id !== old_data.thread_posting.thread_id) { //if the thread got reloaded by a new thread //markup contents diff --git a/resources/vue/components/BlubberThreadWidget.vue b/resources/vue/components/BlubberThreadWidget.vue index 2a2de1ca5f7..0606eb88972 100644 --- a/resources/vue/components/BlubberThreadWidget.vue +++ b/resources/vue/components/BlubberThreadWidget.vue @@ -34,7 +34,8 @@ data () { return { display_more_down: this.more_down, - already_loading_down: 0 + already_loading_down: 0, + allThreads: this.threads }; }, methods: { @@ -50,11 +51,11 @@ return STUDIP.URLHelper.getURL(`dispatch.php/blubber/index/${thread_id}`); }, addThread (thread) { - let thread_ids = this.threads.map((t) => t.thread_id); + let thread_ids = this.allThreads.map((t) => t.thread_id); if (thread_ids.indexOf(thread.thread_id) !== -1) { return; } - this.threads.push(thread); + this.allThreads.push(thread); } }, directives: { @@ -104,7 +105,7 @@ }, computed: { sortedThreads () { - return this.threads.sort((a, b) => { + return [...this.allThreads].sort((a, b) => { return b.timestamp - a.timestamp || b.mkdate - a.mkdate || b.name.localeCompare(a.name); diff --git a/resources/vue/components/CacheAdministration.vue b/resources/vue/components/CacheAdministration.vue index 14e1d53f583..af3d461f695 100644 --- a/resources/vue/components/CacheAdministration.vue +++ b/resources/vue/components/CacheAdministration.vue @@ -55,9 +55,11 @@ export default { }, currentConfig: { type: Object, - default: { - component: null, - props: [] + default() { + return { + component: null, + props: [] + }; } } }, diff --git a/resources/vue/components/Datetimepicker.vue b/resources/vue/components/Datetimepicker.vue index 3ec930c12e0..87f6dfd2940 100644 --- a/resources/vue/components/Datetimepicker.vue +++ b/resources/vue/components/Datetimepicker.vue @@ -38,7 +38,7 @@ export default { } }, mounted () { - let value = parseInt(this.value, 10) !== NaN ? parseInt(this.value, 10) : this.value; + let value = !isNaN(parseInt(this.value, 10)) ? parseInt(this.value, 10) : this.value; if (Number.isInteger(value)) { let date = new Date(value * 1000); let formatted_date = diff --git a/resources/vue/components/EditableList.vue b/resources/vue/components/EditableList.vue index 8b224227213..e35d1d6c155 100644 --- a/resources/vue/components/EditableList.vue +++ b/resources/vue/components/EditableList.vue @@ -18,11 +18,15 @@ <translate>Oder aus Liste auswählen:</translate> <select @change="quickselect" @keydown="navigate_or_select"> <option value=""><translate>Direkt auswählen ...</translate></option> - <template v-for="opt in selectable"> - <optgroup v-if="opt.label && opt.options" :label="opt.label"> - <option v-for="option in opt.options" :disabled="isSelected(option.value)" :value="JSON.stringify({value: option.value, name: option.name})">{{ option.name + (isSelected(option.value) ? ' ✓' : '') }}</option> + <template v-for="(opt, idx) in selectable"> + <optgroup v-if="opt.label && opt.options" :label="opt.label" :key="idx"> + <option v-for="(option, index) in opt.options" :disabled="isSelected(option.value)" :value="JSON.stringify({value: option.value, name: option.name})" :key="index"> + {{ option.name + (isSelected(option.value) ? ' ✓' : '') }} + </option> </optgroup> - <option v-else :disabled="isSelected(opt.value)" @click="quicksearch" :value="JSON.stringify({value: opt.value, name: opt.name})">{{ opt.name + (isSelected(option.value) ? ' ✓' : '') }}</option> + <option v-else :disabled="isSelected(opt.value)" @click="quicksearch" :value="JSON.stringify({value: opt.value, name: opt.name})" :key="idx"> + {{ opt.name + (isSelected(option.value) ? ' ✓' : '') }} + </option> </template> </select> @@ -52,13 +56,14 @@ export default { category_order: { type: Array, required: false, - default: [] + default: () => [], } }, data () { return { resort: false, //this is just for triggering the computed property sortedItems to be sorted again - preventChangeOfQuickselect: false + preventChangeOfQuickselect: false, + allItems: this.items }; }, methods: { @@ -68,16 +73,8 @@ export default { icon = id.split('__')[1]; id = id.split('__')[0]; } - let insert = true; - for (let i in this.items) { - if (this.items[i].value === id) { - insert = false; - break; - } - } - - if (insert) { - this.items.push({ + if (!this.allItems.find(item => item.value === id)) { + this.allItems.push({ value: id, name: name, icon: icon, @@ -143,22 +140,19 @@ export default { }, computed: { sortedItems () { - let v = this; - let i = this.resort; - let items = this.items.sort(function (a, b) { + return [...this.items].sort((a, b) => { if (a.icon === b.icon) { return a.name.localeCompare(b.name); } else { let a_icon = a.icon || ''; let b_icon = b.icon || ''; - if (v.category_order.indexOf(a_icon) > -1 && v.category_order.indexOf(b_icon) > -1) { - return v.category_order.indexOf(a_icon) < v.category_order.indexOf(b_icon) ? -1 : 1; + if (this.category_order.indexOf(a_icon) > -1 && this.category_order.indexOf(b_icon) > -1) { + return this.category_order.indexOf(a_icon) < this.category_order.indexOf(b_icon) ? -1 : 1; } else { return a_icon.localeCompare(b_icon); } } }); - return items; } }, mounted () { diff --git a/resources/vue/components/FilesTable.vue b/resources/vue/components/FilesTable.vue index 15f2d49802f..e58d2140b02 100644 --- a/resources/vue/components/FilesTable.vue +++ b/resources/vue/components/FilesTable.vue @@ -15,9 +15,7 @@ {{ breadcrumbs[0].name }} </span> </a> - <span v-for="(breadcrumb, index) in breadcrumbs" - :key="breadcrumb.folder_id" - v-if="index > 0"> + <span v-for="breadcrumb in breadcrumbs.slice(1)" :key="breadcrumb.folder_id"> /<a :href="breadcrumb.url"> {{ breadcrumb.name }} </a> @@ -36,8 +34,7 @@ <col v-if="showdownloads" style="width: 100px" class="responsive-hidden"> <col style="width: 150px" class="responsive-hidden"> <col style="width: 120px" class="responsive-hidden"> - <col v-if="topfolder.additionalColumns" - v-for="(name, index) in topfolder.additionalColumns" + <col v-for="(name, index) in additionalColumns" :key="index" data-filter-ignore class="responsive-hidden"> @@ -81,8 +78,7 @@ {{ $gettext('Datum') }} </a> </th> - <th v-if="topfolder.additionalColumns" - v-for="(name, index) in topfolder.additionalColumns" + <th v-for="(name, index) in additionalColumns" :key="index" @click="sort(index)" class="responsive-hidden" @@ -112,7 +108,8 @@ <tbody class="subfolders" v-if="displayedFolders.length > 0"> <tr v-for="folder in displayedFolders" :id="'row_folder_' + folder.id " - :data-permissions="folder.permissions"> + :data-permissions="folder.permissions" + :key="folder.id"> <td v-if="show_bulk_actions"> <studip-proxied-checkbox name="ids[]" @@ -146,12 +143,12 @@ <td class="responsive-hidden" style="white-space: nowrap;"> <studip-date-time :timestamp="folder.chdate" :relative="true"></studip-date-time> </td> - <template v-if="topfolder.additionalColumns" - v-for="(name, index) in topfolder.additionalColumns"> + <template v-for="(name, index) in additionalColumns"> <td v-if="folder.additionalColumns && folder.additionalColumns[index] && folder.additionalColumns[index].html" class="responsive-hidden" - v-html="folder.additionalColumns[index].html"></td> - <td v-else class="responsive-hidden"></td> + v-html="folder.additionalColumns[index].html" + :key="index"></td> + <td v-else class="responsive-hidden" :key="index"></td> </template> <td class="actions" v-html="folder.actions"> </td> @@ -162,7 +159,8 @@ :class="file.new ? 'new' : ''" :id="'fileref_' + file.id" role="row" - :data-permissions="getPermissions(file)"> + :data-permissions="getPermissions(file)" + :key="file.id"> <td v-if="show_bulk_actions"> <studip-proxied-checkbox name="ids[]" @@ -209,12 +207,12 @@ <td data-sort-value="file.chdate" class="responsive-hidden" style="white-space: nowrap;"> <studip-date-time :timestamp="file.chdate" :relative="true"></studip-date-time> </td> - <template v-if="topfolder.additionalColumns" - v-for="(name, index) in topfolder.additionalColumns"> + <template v-for="(name, index) in additionalColumns"> <td v-if="file.additionalColumns && file.additionalColumns[index] && file.additionalColumns[index].html" class="responsive-hidden" - v-html="file.additionalColumns[index].html"></td> - <td v-else class="responsive-hidden"></td> + v-html="file.additionalColumns[index].html" + :key="index"></td> + <td v-else class="responsive-hidden" :key="index"></td> </template> <td class="actions" v-html="file.actions"> </td> @@ -299,6 +297,8 @@ export default { selectedIds: [undefined], // Includes invalid value to trigger watch on mounted sortedBy: this.initial_sort.sortedBy, sortDirection: this.initial_sort.sortDirection, + allFiles: this.files, + allFolders: this.folders, filter: '' }; }, @@ -318,10 +318,10 @@ export default { return classes; }, removeFile (id) { - this.files = this.files.filter(file => file.id != id) + this.allFiles = this.allFiles.filter(file => file.id != id) }, removeFolder (id) { - this.folders = this.folders.filter(folder => folder.id != id) + this.allFolders = this.allFolders.filter(folder => folder.id != id) }, sortArray (array) { if (!array.length) { @@ -344,7 +344,7 @@ export default { } // Additional sorting - if (this.topfolder.additionalColumns.hasOwnProperty(this.sortedBy) && arrayHasKey) { + if (this.topfolder.additionalColumns[this.sortedBy] !== undefined && arrayHasKey) { const is_string = array.some(item => { return typeof item.additionalColumns[this.sortedBy].order === "string" && !isNaN(parseFloat(item.additionalColumns[this.sortedBy].order)); @@ -379,7 +379,7 @@ export default { let highlighted = sanitizeHTML(string); if (this.needleForFilter.length > 0) { // Escape needle for regexp, see https://stackoverflow.com/a/3561711 - const pattern = this.needleForFilter.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + const pattern = this.needleForFilter.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') const regExp = new RegExp(pattern, 'gi'); highlighted = highlighted.replace(regExp, '<span class="filter-match">$&</span>'); } @@ -392,6 +392,9 @@ export default { + (this.showdownloads ? 1 : 0) + Object.keys(this.topfolder.additionalColumns).length; }, + additionalColumns () { + return this.topfolder.additionalColumns || []; + }, sortedFiles () { return this.sortArray(this.files); }, @@ -402,7 +405,7 @@ export default { return [].concat(this.files.map(file => file.id)).concat(this.folders.map(folder => folder.id)); }, displayedFiles () { - let files = [].concat(this.files); + let files = [...this.allFiles]; if (this.needleForFilter.length > 0) { files = files.filter(file => { return this.valueMatchesFilter(file.name) @@ -412,7 +415,7 @@ export default { return this.sortArray(files); }, displayedFolders () { - let folders = [].concat(this.folders); + let folders = [...this.allFolders]; if (this.needleForFilter.length > 0) { folders = folders.filter(folder => { return this.valueMatchesFilter(folder.name) diff --git a/resources/vue/components/I18nTextarea.vue b/resources/vue/components/I18nTextarea.vue index 084cdfb5c71..76a025c9aee 100644 --- a/resources/vue/components/I18nTextarea.vue +++ b/resources/vue/components/I18nTextarea.vue @@ -1,15 +1,13 @@ <template> <div class="i18n_group" v-if="languages.length > 1"> <div class="i18n" - v-for="language in languages" - v-if="(selectedLanguage !== null) && (language.id === selectedLanguage.id)" - :data-lang="language.name" - :data-icon="'url(' + assetsURL + 'images/languages/' + language.picture + ')'"> + :data-lang="primaryLanguage.name" + :data-icon="'url(' + assetsURL + 'images/languages/' + primaryLanguage.picture + ')'"> <input type=text ref="inputfield" :name="nameOfInput(language.id)" - v-model="values[selectedLanguage.id]" - :required="required && defaultLanguage === language.id" + v-model="values[primaryLanguage.id]" + :required="required && defaultLanguage === primaryLanguage.id" v-bind="$attrs" v-on="$listeners" v-if="type === 'text'"> @@ -19,34 +17,36 @@ v-on="$listeners" v-model="values[language.id]" :required="required && defaultLanguage === language.id" - v-else-if="type === 'textarea'">{{ values[language.id] }}</textarea> + v-else-if="type === 'textarea'"></textarea> <studip-wysiwyg :name="nameOfInput(language.id)" ref="inputfield" v-model="values[selectedLanguage.id]" v-bind="$attrs" v-on="$listeners" - :required="required && defaultLanguage === language.id" + :required="required && defaultLanguage === primaryLanguage.id" v-else-if="type === 'wysiwyg' && !wysiwyg_disabled"></studip-wysiwyg> <textarea-with-toolbar :name="nameOfInput(language.id)" ref="inputfield" v-else - v-model="values[selectedLanguage.id]" + v-model="values[primaryLanguage.id]" v-bind="$attrs" - :required="required && defaultLanguage === language.id" + :required="required && defaultLanguage === primaryLanguage.id" v-on="$listeners"></textarea-with-toolbar> </div> <input type="hidden" - v-for="language in languages" - v-if="(selectedLanguage !== null) && (language.id !== selectedLanguage.id)" + v-for="language in secondaryLanguages" v-model="values[language.id]" :required="required && defaultLanguage === language.id" - :name="nameOfInput(language.id)"> + :name="nameOfInput(language.id)" + :key="language.id"> <select class="i18n" tabindex="0" @change="selectLanguage" :aria-label="$gettext('Sprache des Textfeldes auswählen.')" :style="'background-image: url(' + assetsURL + 'images/languages/' + selectedLanguage.picture + ')'"> - <option v-for="language in languages" :value="language.id">{{language.name}}</option> + <option v-for="language in languages" :value="language.id" :key="language.id"> + {{language.name}} + </option> </select> </div> <div v-else> @@ -130,10 +130,11 @@ export default { this.selectedLanguage = this.languages[i]; break; } - let jsonvalue = false; + let jsonvalue; try { jsonvalue = JSON.parse(this.value); } catch (except) { + jsonvalue = false; } if (jsonvalue !== false) { this.values = jsonvalue; @@ -179,6 +180,12 @@ export default { } } return languages; + }, + primaryLanguage () { + return this.languages.find(language => language.id === this.selectedLanguage.id); + }, + secondaryLanguages () { + return this.languages.filter(language => language.id !== this.selectedLanguage.id); } }, inheritAttrs: false, diff --git a/resources/vue/components/MyCoursesColorPicker.vue b/resources/vue/components/MyCoursesColorPicker.vue index ecf24a7ff93..368ae5fd5dc 100644 --- a/resources/vue/components/MyCoursesColorPicker.vue +++ b/resources/vue/components/MyCoursesColorPicker.vue @@ -1,6 +1,6 @@ <template> <ul class="my-courses-color-picker"> - <li v-for="(i, index) in color_count" :id="i" :class="getCSSClasses(index)"> + <li v-for="(i, index) in color_count" :id="i" :class="getCSSClasses(index)" :key="index"> <a @click="selectColor(index)" :title="getTitle(i, index)"> {{ getTitle(i) }} </a> diff --git a/resources/vue/components/MyCoursesNavigation.vue b/resources/vue/components/MyCoursesNavigation.vue index f3dbad2816a..739668c6f2c 100644 --- a/resources/vue/components/MyCoursesNavigation.vue +++ b/resources/vue/components/MyCoursesNavigation.vue @@ -1,6 +1,6 @@ <template> <ul class="my-courses-navigation" v-if="navigationLength > 0"> - <li v-for="nav in navigation" class="my-courses-navigation-item" :class="nav.class"> + <li v-for="(nav, index) in navigation" class="my-courses-navigation-item" :class="nav.class" :key="index"> <a v-if="nav" :href="nav.url" v-bind="nav.attr"> <studip-icon :shape="nav.icon.shape" :role="nav.icon.role" :size="iconSize"></studip-icon> </a> diff --git a/resources/vue/components/MyCoursesTables.vue b/resources/vue/components/MyCoursesTables.vue index c542e3d3899..0a5abad846c 100644 --- a/resources/vue/components/MyCoursesTables.vue +++ b/resources/vue/components/MyCoursesTables.vue @@ -36,7 +36,7 @@ </th> <th v-if="!responsiveDisplay" class="dont-hide" colspan="2"></th> </tr> - <tr v-for="course in getOrderedCourses(subgroup.ids)" :data-course-id="course.id" :class="getCourseClasses(course)"> + <tr v-for="course in getOrderedCourses(subgroup.ids)" :data-course-id="course.id" :class="getCourseClasses(course)" :key="course.id"> <td :class="`gruppe${course.group}`"></td> <td :class="{'subcourse-indented': isChild(course)}"> <span :style="{backgroundImage: `url(${course.avatar}`}" class="my-courses-avatar course-avatar-small" :title="course.name" alt=""></span> diff --git a/resources/vue/components/MyCoursesTiles.vue b/resources/vue/components/MyCoursesTiles.vue index 28ff6c6a15d..71c5b645384 100644 --- a/resources/vue/components/MyCoursesTiles.vue +++ b/resources/vue/components/MyCoursesTiles.vue @@ -1,7 +1,7 @@ <template> <div class="my-courses my-courses--tiles"> - <template v-for="group in groups"> - <div class="group-label">{{ group.name }}</div> + <template v-for="(group, index) in groups"> + <div class="group-label" :key="index">{{ group.name }}</div> <article class="studip" v-for="subgroup in group.data" :key="subgroup.id" :class="getGroupCssClasses(subgroup)"> <header v-if="subgroup.label"> <h1> @@ -10,11 +10,11 @@ </header> <section class="studip-grid"> <template v-for="course in getOrderedCourses(subgroup.ids)"> - <div class="course-group-label" v-if="isParent(course)"> + <div class="course-group-label" v-if="isParent(course)" :key="course.id"> {{ getCourseName(course, getConfig('sem_number')) }} </div> - <article class="studip-grid-element" :data-course-id="course.id" :class="getCourseCssClasses(course)"> + <article class="studip-grid-element" :data-course-id="course.id" :class="getCourseCssClasses(course)" :key="course.id"> <header class="tiles-grid-element-header"> <span class="tiles-grid-element-options"> <studip-action-menu :items="getActionMenuForCourse(course, true)" diff --git a/resources/vue/components/StudipDialog.vue b/resources/vue/components/StudipDialog.vue index 242bc230ac0..603b8b04942 100644 --- a/resources/vue/components/StudipDialog.vue +++ b/resources/vue/components/StudipDialog.vue @@ -190,6 +190,7 @@ export default { if (this.message) { return this.$gettext('Information'); } + return ''; }, dialogWidth() { return this.currentWidth ? (this.currentWidth - dialogPadding * 4) + 'px' : 'unset'; @@ -217,7 +218,7 @@ export default { this.left = 5; this.currentWidth = window.innerWidth - 16; } - + this.top = (window.innerHeight - this.currentHeight) / 2; this.footerHeight = this.$refs.footer.offsetHeight; }, diff --git a/resources/vue/components/StudipFileSize.vue b/resources/vue/components/StudipFileSize.vue index 46a0699f3d6..4b26c15e33c 100644 --- a/resources/vue/components/StudipFileSize.vue +++ b/resources/vue/components/StudipFileSize.vue @@ -31,6 +31,8 @@ let rel = (this.size / 1024 / 1024 / 1024 / 1024).toFixed(1); return rel + " TB"; } + + return this.size; } } } diff --git a/resources/vue/components/StudipMessageBox.vue b/resources/vue/components/StudipMessageBox.vue index 471225ee38f..0e2fcb74599 100644 --- a/resources/vue/components/StudipMessageBox.vue +++ b/resources/vue/components/StudipMessageBox.vue @@ -5,14 +5,14 @@ <span>{{ $gettext('Detailanzeige umschalten') }}</span> </a> <a v-if="!hideClose" class="close" href="" :title="$gettext('Nachrichtenbox schließen')" @click.prevent="closed = true"> - <span>{{ $gettext('Nachrichtenbox schließen') }}</span> + <span>{{ $gettext('Nachrichtenbox schließen') }}</span> </a> </div> <slot></slot> <div v-if="showDetails" class="messagebox_details"> <slot name="details"> <ul> - <li v-for="detail in details" v-html="detail"></li> + <li v-for="(detail, index) in details" v-html="detail" :key="index"></li> </ul> </slot> </div> @@ -32,7 +32,7 @@ export default { }, details: { type: Array, - default: [], + default: () => [], }, hideDetails: { type: Boolean, diff --git a/resources/vue/components/courseware/CoursewareAccordionContainer.vue b/resources/vue/components/courseware/CoursewareAccordionContainer.vue index 7d9f7626232..8e4d0984d1a 100644 --- a/resources/vue/components/courseware/CoursewareAccordionContainer.vue +++ b/resources/vue/components/courseware/CoursewareAccordionContainer.vue @@ -67,7 +67,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="option"> @@ -241,7 +241,7 @@ export default { return null; }, updateContent(blockAdder) { - if(blockAdder.hasOwnProperty('container') && blockAdder.container.id === this.container.id) { + if (blockAdder.container !== undefined && blockAdder.container.id === this.container.id) { this.initCurrentData(); } } diff --git a/resources/vue/components/courseware/CoursewareActionWidget.vue b/resources/vue/components/courseware/CoursewareActionWidget.vue index 2db5c2f964d..fa79d124892 100644 --- a/resources/vue/components/courseware/CoursewareActionWidget.vue +++ b/resources/vue/components/courseware/CoursewareActionWidget.vue @@ -63,7 +63,6 @@ </template> <script> -import StudipIcon from './../StudipIcon.vue'; import SidebarWidget from '../SidebarWidget.vue'; import CoursewareExport from '@/vue/mixins/courseware/export.js'; import { mapActions, mapGetters } from 'vuex'; @@ -72,7 +71,6 @@ export default { name: 'courseware-action-widget', props: ['structuralElement', 'canVisit'], components: { - StudipIcon, SidebarWidget, }, mixins: [CoursewareExport], diff --git a/resources/vue/components/courseware/CoursewareBiographyAchievementsBlock.vue b/resources/vue/components/courseware/CoursewareBiographyAchievementsBlock.vue index 7a3ce6dc82e..532352f5b88 100755 --- a/resources/vue/components/courseware/CoursewareBiographyAchievementsBlock.vue +++ b/resources/vue/components/courseware/CoursewareBiographyAchievementsBlock.vue @@ -81,7 +81,6 @@ import CoursewareDefaultBlock from './CoursewareDefaultBlock.vue'; import { mapActions } from 'vuex'; import { blockMixin } from './block-mixin.js'; -import StudipIcon from '../StudipIcon.vue'; import StudipWysiwyg from '../StudipWysiwyg.vue'; export default { @@ -89,7 +88,6 @@ export default { mixins: [blockMixin], components: { CoursewareDefaultBlock, - StudipIcon, StudipWysiwyg, }, props: { diff --git a/resources/vue/components/courseware/CoursewareBiographyGoalsBlock.vue b/resources/vue/components/courseware/CoursewareBiographyGoalsBlock.vue index 4ff944ba4a2..5938a2853ad 100755 --- a/resources/vue/components/courseware/CoursewareBiographyGoalsBlock.vue +++ b/resources/vue/components/courseware/CoursewareBiographyGoalsBlock.vue @@ -85,6 +85,8 @@ export default { case 'professional': return this.$gettext('Berufliches Ziel'); } + + throw new Error('Undefined data type ' + this.currentData.type); }, }, mounted() { diff --git a/resources/vue/components/courseware/CoursewareBiographyPersonalInformationBlock.vue b/resources/vue/components/courseware/CoursewareBiographyPersonalInformationBlock.vue index e8ee5b40ea5..14ba1be9acc 100755 --- a/resources/vue/components/courseware/CoursewareBiographyPersonalInformationBlock.vue +++ b/resources/vue/components/courseware/CoursewareBiographyPersonalInformationBlock.vue @@ -75,14 +75,12 @@ import CoursewareDefaultBlock from './CoursewareDefaultBlock.vue'; import { mapActions } from 'vuex'; import { blockMixin } from './block-mixin.js'; -import StudipIcon from '../StudipIcon.vue'; export default { name: 'courseware-biography-personal-information-block', mixins: [blockMixin], components: { CoursewareDefaultBlock, - StudipIcon, }, props: { block: Object, diff --git a/resources/vue/components/courseware/CoursewareChartBlock.vue b/resources/vue/components/courseware/CoursewareChartBlock.vue index 4329659fe90..96726bf9e58 100644 --- a/resources/vue/components/courseware/CoursewareChartBlock.vue +++ b/resources/vue/components/courseware/CoursewareChartBlock.vue @@ -61,7 +61,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="{name, rgb}"> diff --git a/resources/vue/components/courseware/CoursewareContentBookmarks.vue b/resources/vue/components/courseware/CoursewareContentBookmarks.vue index 329289c22df..85345428b85 100644 --- a/resources/vue/components/courseware/CoursewareContentBookmarks.vue +++ b/resources/vue/components/courseware/CoursewareContentBookmarks.vue @@ -62,6 +62,7 @@ export default { return bookmark.relationships.course.data.id === this.bookmarkFilter; }); } + return []; } }, methods: { diff --git a/resources/vue/components/courseware/CoursewareContentOverviewElements.vue b/resources/vue/components/courseware/CoursewareContentOverviewElements.vue index c23e6d621b1..0e6a87fde0c 100644 --- a/resources/vue/components/courseware/CoursewareContentOverviewElements.vue +++ b/resources/vue/components/courseware/CoursewareContentOverviewElements.vue @@ -193,7 +193,7 @@ ><studip-icon shape="arr_1down" size="10" /></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="{ name, hex }"> @@ -540,7 +540,7 @@ export default { const ownerId = element.relationships.owner.data.id; const owner = this.userById({ id: ownerId }); - return owner.attributes['formatted-name']; + return owner.attributes['formatted-name']; }, addElement() { this.setShowOverviewElementAddDialog(true); diff --git a/resources/vue/components/courseware/CoursewareContentPermissions.vue b/resources/vue/components/courseware/CoursewareContentPermissions.vue index 22640721aa2..a2de339d4f2 100755 --- a/resources/vue/components/courseware/CoursewareContentPermissions.vue +++ b/resources/vue/components/courseware/CoursewareContentPermissions.vue @@ -233,6 +233,7 @@ export default { this.userPermsWriteUsers = this.element.attributes['write-approval'].users; } + /* eslint-disable no-await-in-loop */ for (const user_perm_obj of this.userPermsReadUsers) { let userObj = await this.getUser(user_perm_obj.id); let writePerm = this.userPermsWriteUsers.some(user_write_perm => user_write_perm.id === user_perm_obj.id) ? true : false; diff --git a/resources/vue/components/courseware/CoursewareDefaultBlock.vue b/resources/vue/components/courseware/CoursewareDefaultBlock.vue index 5df64c99a5a..b3023f5b409 100644 --- a/resources/vue/components/courseware/CoursewareDefaultBlock.vue +++ b/resources/vue/components/courseware/CoursewareDefaultBlock.vue @@ -79,14 +79,10 @@ </template> <script> -import CoursewareBlockComments from './CoursewareBlockComments.vue'; import CoursewareBlockEdit from './CoursewareBlockEdit.vue'; import CoursewareBlockExportOptions from './CoursewareBlockExportOptions.vue'; -import CoursewareBlockFeedback from './CoursewareBlockFeedback.vue'; import CoursewareBlockInfo from './CoursewareBlockInfo.vue'; import CoursewareBlockActions from './CoursewareBlockActions.vue'; -import CoursewareTabs from './CoursewareTabs.vue'; -import CoursewareTab from './CoursewareTab.vue'; import StudipDialog from '../StudipDialog.vue'; import StudipIcon from '../StudipIcon.vue'; import { blockMixin } from './block-mixin.js'; @@ -97,14 +93,10 @@ export default { name: 'courseware-default-block', mixins: [blockMixin], components: { - CoursewareBlockComments, CoursewareBlockEdit, CoursewareBlockExportOptions, - CoursewareBlockFeedback, CoursewareBlockActions, CoursewareBlockInfo, - CoursewareTabs, - CoursewareTab, StudipDialog, StudipIcon, CoursewareBlockDiscussion, diff --git a/resources/vue/components/courseware/CoursewareDefaultContainer.vue b/resources/vue/components/courseware/CoursewareDefaultContainer.vue index 962febba6f3..5822072f482 100644 --- a/resources/vue/components/courseware/CoursewareDefaultContainer.vue +++ b/resources/vue/components/courseware/CoursewareDefaultContainer.vue @@ -137,7 +137,6 @@ export default { deleteContainer: 'deleteContainer', lockObject: 'lockObject', unlockObject: 'unlockObject', - companionInfo: 'companionInfo', }), async displayEditDialog() { await this.loadContainer({ id: this.container.id, options: { include: 'edit-blocker' } }); diff --git a/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue b/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue index 022590bb005..b051aecb862 100644 --- a/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue +++ b/resources/vue/components/courseware/CoursewareDialogCardsBlock.vue @@ -11,7 +11,7 @@ > <template #content> <div class="cw-block-dialog-cards-content"> - <button + <button class="cw-dialogcards-prev cw-dialogcards-navbutton" :class="{'cw-dialogcards-prev-disabled': hasNoPerv}" @click="prevCard" @@ -69,7 +69,7 @@ :name="$gettext('Karte') + ' ' + (index + 1).toString()" :selected="index === 0" canBeEmpty - > + > <form class="default" @submit.prevent=""> <label> <translate>Bild Vorderseite</translate>: @@ -118,7 +118,6 @@ import CoursewareTabs from './CoursewareTabs.vue'; import CoursewareTab from './CoursewareTab.vue'; import { blockMixin } from './block-mixin.js'; import { mapActions } from 'vuex'; -import StudipIcon from '../StudipIcon.vue'; export default { name: 'courseware-dialog-cards-block', @@ -128,7 +127,6 @@ export default { CoursewareFileChooser, CoursewareTabs, CoursewareTab, - StudipIcon, }, props: { block: Object, diff --git a/resources/vue/components/courseware/CoursewareHeadlineBlock.vue b/resources/vue/components/courseware/CoursewareHeadlineBlock.vue index 8a5bc6ce5d8..292ad60b119 100644 --- a/resources/vue/components/courseware/CoursewareHeadlineBlock.vue +++ b/resources/vue/components/courseware/CoursewareHeadlineBlock.vue @@ -68,7 +68,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="{name, hex}"> @@ -85,7 +85,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="option"> @@ -108,7 +108,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="{name, hex}"> @@ -138,7 +138,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="{name, hex}"> diff --git a/resources/vue/components/courseware/CoursewareImageMapBlock.vue b/resources/vue/components/courseware/CoursewareImageMapBlock.vue index f097e782bc6..597213b52b1 100644 --- a/resources/vue/components/courseware/CoursewareImageMapBlock.vue +++ b/resources/vue/components/courseware/CoursewareImageMapBlock.vue @@ -72,7 +72,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="{name, rgba}"> @@ -417,6 +417,9 @@ export default { this.currentShapes.forEach((value, key) => { let shape = value; let area = {}; + let coords = ''; + let x = 0; + let y = 0; area.id = 'shape-' + key; switch (shape.type) { @@ -425,9 +428,6 @@ export default { area.coords = shape.data.centerX + ', ' + shape.data.centerY + ', ' + shape.data.radius; break; case 'ellipse': - let coords = ''; - let x = 0; - let y = 0; for (let theta = 0; theta < 2 * Math.PI; theta += (2 * Math.PI) / 20) { x = parseInt(shape.data.X) + Math.round(parseInt(shape.data.radiusX) * Math.cos(theta)); y = parseInt(shape.data.Y) + Math.round(parseInt(shape.data.radiusY) * Math.sin(theta)); @@ -438,10 +438,10 @@ export default { break; case 'rect': case 'text': - let x2 = parseInt(shape.data.X) + parseInt(shape.data.width); - let y2 = parseInt(shape.data.Y) + parseInt(shape.data.height); + x = parseInt(shape.data.X) + parseInt(shape.data.width); + y = parseInt(shape.data.Y) + parseInt(shape.data.height); area.shape = 'rect'; - area.coords = shape.data.X + ', ' + shape.data.Y + ', ' + x2 + ', ' + y2; + area.coords = shape.data.X + ', ' + shape.data.Y + ', ' + x + ', ' + y; break; } area.title = shape.title; diff --git a/resources/vue/components/courseware/CoursewareKeyPointBlock.vue b/resources/vue/components/courseware/CoursewareKeyPointBlock.vue index 05d5d2fd701..2fc29b45935 100644 --- a/resources/vue/components/courseware/CoursewareKeyPointBlock.vue +++ b/resources/vue/components/courseware/CoursewareKeyPointBlock.vue @@ -40,7 +40,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="{name, hex}"> @@ -58,7 +58,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="option"> @@ -171,6 +171,7 @@ export default { return 'status-yellow'; case 'blue': + default: return 'clickable'; } } diff --git a/resources/vue/components/courseware/CoursewareManagerCopySelector.vue b/resources/vue/components/courseware/CoursewareManagerCopySelector.vue index 3c22dcd9a14..084e9a2f39c 100644 --- a/resources/vue/components/courseware/CoursewareManagerCopySelector.vue +++ b/resources/vue/components/courseware/CoursewareManagerCopySelector.vue @@ -28,7 +28,7 @@ </ul> </li> </ul> - <courseware-companion-box + <courseware-companion-box v-if="!hasRemoteCid && semesterMap.length === 0" :msgCompanion="$gettext('Es wurden keine Veranstaltung mit Courseware-Inhalten gefunden.')" mood="sad" @@ -113,7 +113,7 @@ export default { return this.remoteCid !== ''; }, loadedCourses() { - return this.courses.sort((a, b) => a.attributes.title > b.attributes.title); + return [...this.courses].sort((a, b) => a.attributes.title > b.attributes.title); } }, methods: { @@ -218,7 +218,7 @@ export default { elementId: elementId, migrate: true }); - } catch(error) { + } catch(error) { console.debug(error); this.copyAllInProgressText = this.$gettext('Beim Kopiervorgang sind Fehler aufgetreten.'); } diff --git a/resources/vue/components/courseware/CoursewareManagerElement.vue b/resources/vue/components/courseware/CoursewareManagerElement.vue index 2bd892c2ed5..bae3725a7cd 100644 --- a/resources/vue/components/courseware/CoursewareManagerElement.vue +++ b/resources/vue/components/courseware/CoursewareManagerElement.vue @@ -471,6 +471,9 @@ export default { if(!this.insertingInProgress) { this.insertingInProgress = true; if (source === 'self') { + block.relationships.container.data.id = this.filingData.parentItem.id; + block.attributes.position = this.filingData.parentItem.relationships.blocks.data.length + 1; + let sourceContainer = await this.containerById({id: block.relationships.container.data.id}); sourceContainer.attributes.payload.sections.forEach(section => { let index = section.blocks.indexOf(block.id); @@ -494,8 +497,6 @@ export default { }); await this.unlockObject({id: destinationContainer.id, type: 'courseware-containers'}); - block.relationships.container.data.id = this.filingData.parentItem.id; - block.attributes.position = this.filingData.parentItem.relationships.blocks.data.length + 1; await this.lockObject({id: block.id, type: 'courseware-blocks'}); await this.updateBlock({ block: block, diff --git a/resources/vue/components/courseware/CoursewareManagerLinkSelector.vue b/resources/vue/components/courseware/CoursewareManagerLinkSelector.vue index 1f4774ff05a..a735024a2ad 100644 --- a/resources/vue/components/courseware/CoursewareManagerLinkSelector.vue +++ b/resources/vue/components/courseware/CoursewareManagerLinkSelector.vue @@ -13,15 +13,11 @@ <script> import CoursewareManagerElement from './CoursewareManagerElement.vue'; -import CoursewareCompanionBox from './CoursewareCompanionBox.vue'; import { mapActions, mapGetters } from 'vuex'; export default { name: 'courseware-manager-link-selector', - components: { - CoursewareManagerElement, - CoursewareCompanionBox, - }, + components: { CoursewareManagerElement }, data() { return { @@ -75,4 +71,4 @@ export default { await this.loadOwnCourseware(); }, } -</script> \ No newline at end of file +</script> diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index 25fff6f6c12..e8de3b1518b 100644 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -229,7 +229,7 @@ ><studip-icon shape="arr_1down" size="10" /></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung</translate>. </template> <template #selected-option="{ name, hex }"> @@ -1278,13 +1278,15 @@ export default { return containers; }, + owner() { - const user = this.$store.getters['users/related']({ - parent: { type: this.structuralElement.type, id: this.structuralElement.id }, - relationship: 'owner' + const owner = this.relatedUsers({ + parent: this.structuralElement, + relationship: 'owner', }); - return user ?? null; + return owner ?? null; }, + ownerName() { return this.owner?.attributes['formatted-name'] ?? '?'; }, @@ -1574,7 +1576,9 @@ export default { async createElement() { let title = this.newChapterName; // this is the title of the new element let parent_id = this.currentId; // new page is descandant as default - if (this.errorEmptyChapterName = title.trim() === '') { + + this.errorEmptyChapterName = title.trim(); + if (this.errorEmptyChapterName === '') { return; } if (this.newChapterParent === 'sibling') { diff --git a/resources/vue/components/courseware/CoursewareTabs.vue b/resources/vue/components/courseware/CoursewareTabs.vue index 13a6b79d34d..b5cc5a60f76 100644 --- a/resources/vue/components/courseware/CoursewareTabs.vue +++ b/resources/vue/components/courseware/CoursewareTabs.vue @@ -39,8 +39,8 @@ export default { }, computed: { sortedTabs() { - return this.tabs.sort((a, b) => { - return a.index > b.index ? 1 : a.index < b.index ? -1 : 0; + return [...this.tabs].sort((a, b) => { + return a.index - b.index; }); } }, @@ -106,7 +106,7 @@ export default { }, getTabButtonByName(name) { let selectorId = null; - this.tabs.forEach((tab) => { + this.tabs.forEach((tab) => { if (tab.name === name) { selectorId = tab.selectorId; } @@ -118,7 +118,7 @@ export default { }, getTabButtonByAlias(alias) { let selectorId = null; - this.tabs.forEach((tab) => { + this.tabs.forEach((tab) => { if (tab.alias === alias) { selectorId = tab.selectorId; } diff --git a/resources/vue/components/courseware/CoursewareTabsContainer.vue b/resources/vue/components/courseware/CoursewareTabsContainer.vue index 15b3c87b574..b07d2a6b35b 100644 --- a/resources/vue/components/courseware/CoursewareTabsContainer.vue +++ b/resources/vue/components/courseware/CoursewareTabsContainer.vue @@ -78,7 +78,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="option"> @@ -259,7 +259,7 @@ export default { return null; }, updateContent(blockAdder) { - if(blockAdder.hasOwnProperty('container') && blockAdder.container.id === this.container.id) { + if(blockAdder.container !== undefined && blockAdder.container.id === this.container.id) { this.initCurrentData(); } } diff --git a/resources/vue/components/courseware/CoursewareTimelineBlock.vue b/resources/vue/components/courseware/CoursewareTimelineBlock.vue index 451478ecad9..d8db9176710 100755 --- a/resources/vue/components/courseware/CoursewareTimelineBlock.vue +++ b/resources/vue/components/courseware/CoursewareTimelineBlock.vue @@ -11,7 +11,7 @@ > <template #content> <ol class="cw-timeline"> - <li + <li v-for="(item, index) in sortedItems" :key="index" class="cw-timeline-item" @@ -90,7 +90,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="{name, hex}"> @@ -107,7 +107,7 @@ <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" size="10"/></span> </template> - <template #no-options="{ search, searching, loading }"> + <template #no-options> <translate>Es steht keine Auswahl zur Verfügung.</translate> </template> <template #selected-option="option"> diff --git a/resources/vue/components/courseware/CoursewareToolsAdmin.vue b/resources/vue/components/courseware/CoursewareToolsAdmin.vue index 36dfee21f21..337a39e47fe 100644 --- a/resources/vue/components/courseware/CoursewareToolsAdmin.vue +++ b/resources/vue/components/courseware/CoursewareToolsAdmin.vue @@ -57,7 +57,6 @@ export default { permission: this.currentPermissionLevel, progression: this.currentProgression, }); -; }, }, mounted() { -- GitLab