From 94cbbf169a730e17391d4a1eb9aa0414330c779d Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Tue, 1 Jul 2025 17:27:43 +0530 Subject: [PATCH] feat: prevent skipping videos --- frontend/src/components/Settings/Settings.vue | 31 ++++++++++++------- frontend/src/components/VideoBlock.vue | 3 ++ frontend/src/pages/Lesson.vue | 1 + frontend/src/stores/settings.js | 14 +++++++-- frontend/src/utils/index.js | 25 +++++++++------ lms/lms/api.py | 9 ++---- .../doctype/lms_settings/lms_settings.json | 15 +++++++-- 7 files changed, 65 insertions(+), 33 deletions(-) diff --git a/frontend/src/components/Settings/Settings.vue b/frontend/src/components/Settings/Settings.vue index 26fc3e8f..a4e219a3 100644 --- a/frontend/src/components/Settings/Settings.vue +++ b/frontend/src/components/Settings/Settings.vue @@ -130,6 +130,13 @@ const tabsStructure = computed(() => { label: 'General', icon: 'Wrench', fields: [ + { + label: 'Allow Guest Access', + name: 'allow_guest_access', + description: + 'If enabled, users can access the course and batch lists without logging in.', + type: 'checkbox', + }, { label: 'Enable Learning Paths', name: 'enable_learning_paths', @@ -138,11 +145,11 @@ const tabsStructure = computed(() => { type: 'checkbox', }, { - label: 'Allow Guest Access', - name: 'allow_guest_access', - description: - 'If enabled, users can access the course and batch lists without logging in.', + label: 'Prevent Skipping Videos', + name: 'prevent_skipping_videos', type: 'checkbox', + description: + 'If enabled, students cannot skip videos in a lesson.', }, { label: 'Send calendar invite for evaluations', @@ -154,6 +161,14 @@ const tabsStructure = computed(() => { { type: 'Column Break', }, + { + label: 'Livecode URL', + name: 'livecode_url', + doctype: 'Livecode URL', + type: 'text', + description: + 'https://docs.frappe.io/learning/falcon-self-hosting-guide', + }, { label: 'Batch Confirmation Email Template', name: 'batch_confirmation_template', @@ -166,14 +181,6 @@ const tabsStructure = computed(() => { doctype: 'Email Template', type: 'Link', }, - { - label: 'Livecode URL', - name: 'livecode_url', - doctype: 'Livecode URL', - type: 'text', - description: - 'https://docs.frappe.io/learning/falcon-self-hosting-guide', - }, { label: 'Unsplash Access Key', name: 'unsplash_access_key', diff --git a/frontend/src/components/VideoBlock.vue b/frontend/src/components/VideoBlock.vue index 42a22cd1..86ddfa87 100644 --- a/frontend/src/components/VideoBlock.vue +++ b/frontend/src/components/VideoBlock.vue @@ -74,6 +74,7 @@ v-model="currentTime" @input="changeCurrentTime" class="duration-slider h-1" + :disabled="preventSkippingVideos.data" />
@@ -155,6 +156,7 @@ import { ref, onMounted, computed, watch } from 'vue' import { Pause, Maximize, Volume2, VolumeX } from 'lucide-vue-next' import { Button, Dialog } from 'frappe-ui' import { formatSeconds, formatTimestamp } from '@/utils' +import { useSettings } from '@/stores/settings' import Play from '@/components/Icons/Play.vue' import QuizInVideo from '@/components/Modals/QuizInVideo.vue' @@ -170,6 +172,7 @@ const showQuizLoader = ref(false) const quizLoadTimer = ref(0) const currentQuiz = ref(null) const nextQuiz = ref({}) +const { preventSkippingVideos } = useSettings() const props = defineProps({ file: { diff --git a/frontend/src/pages/Lesson.vue b/frontend/src/pages/Lesson.vue index 830cc712..afb36b4e 100644 --- a/frontend/src/pages/Lesson.vue +++ b/frontend/src/pages/Lesson.vue @@ -551,6 +551,7 @@ const getPlyrSource = async () => { await nextTick() if (plyrSources.value.length == 0) { plyrSources.value = await enablePlyr() + console.log(plyrSources.value) } updateVideoWatchDuration() } diff --git a/frontend/src/stores/settings.js b/frontend/src/stores/settings.js index 85ad5156..205354ba 100644 --- a/frontend/src/stores/settings.js +++ b/frontend/src/stores/settings.js @@ -9,21 +9,31 @@ export const useSettings = defineStore('settings', () => { const activeTab = ref(null) const learningPaths = createResource({ - url: 'lms.lms.api.is_learning_path_enabled', + url: 'lms.lms.api.get_lms_setting', + params: { field: 'enable_learning_paths' }, auto: true, cache: ['learningPath'], }) const allowGuestAccess = createResource({ - url: 'lms.lms.api.is_guest_allowed', + url: 'lms.lms.api.get_lms_setting', + params: { field: 'allow_guest_access' }, auto: true, cache: ['allowGuestAccess'], }) + const preventSkippingVideos = createResource({ + url: 'lms.lms.api.get_lms_setting', + params: { field: 'prevent_skipping_videos' }, + auto: true, + cache: ['preventSkippingVideos'], + }) + return { isSettingsOpen, activeTab, learningPaths, allowGuestAccess, + preventSkippingVideos, } }) diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js index a9ce4fe2..79ec05ee 100644 --- a/frontend/src/utils/index.js +++ b/frontend/src/utils/index.js @@ -1,4 +1,3 @@ -import { watch } from 'vue' import { call, toast } from 'frappe-ui' import { useTimeAgo } from '@vueuse/core' import { Quiz } from '@/utils/quiz' @@ -557,17 +556,23 @@ const setupPlyrForVideo = (video, players) => { video.setAttribute('data-plyr-embed-id', videoID) } + let controls = [ + 'play-large', + 'play', + 'progress', + 'current-time', + 'mute', + 'volume', + 'fullscreen', + ] + + if (useSettings().preventSkippingVideos.data) { + controls.splice(controls.indexOf('progress'), 1) + } + const player = new Plyr(video, { youtube: { noCookie: true }, - controls: [ - 'play-large', - 'play', - 'progress', - 'current-time', - 'mute', - 'volume', - 'fullscreen', - ], + controls: controls, }) players.push(player) diff --git a/lms/lms/api.py b/lms/lms/api.py index cbd9585d..d9d9de51 100644 --- a/lms/lms/api.py +++ b/lms/lms/api.py @@ -1304,13 +1304,8 @@ def get_notifications(filters): @frappe.whitelist(allow_guest=True) -def is_guest_allowed(): - return frappe.get_cached_value("LMS Settings", None, "allow_guest_access") - - -@frappe.whitelist(allow_guest=True) -def is_learning_path_enabled(): - return frappe.get_cached_value("LMS Settings", None, "enable_learning_paths") +def get_lms_setting(field): + return frappe.get_cached_value("LMS Settings", None, field) @frappe.whitelist() diff --git a/lms/lms/doctype/lms_settings/lms_settings.json b/lms/lms/doctype/lms_settings/lms_settings.json index d1984e6e..3f3f3b4b 100644 --- a/lms/lms/doctype/lms_settings/lms_settings.json +++ b/lms/lms/doctype/lms_settings/lms_settings.json @@ -12,6 +12,8 @@ "column_break_zdel", "allow_guest_access", "enable_learning_paths", + "prevent_skipping_videos", + "column_break_bjis", "unsplash_access_key", "livecode_url", "section_break_szgq", @@ -72,7 +74,6 @@ "default": "https://livecode.dev.fossunited.org", "fieldname": "livecode_url", "fieldtype": "Data", - "hidden": 1, "label": "LiveCode URL" }, { @@ -405,13 +406,23 @@ "fieldname": "certified_members", "fieldtype": "Check", "label": "Certified Members" + }, + { + "default": "0", + "fieldname": "prevent_skipping_videos", + "fieldtype": "Check", + "label": "Prevent Skipping Videos" + }, + { + "fieldname": "column_break_bjis", + "fieldtype": "Column Break" } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2025-05-30 19:02:51.381668", + "modified": "2025-07-01 17:01:58.466697", "modified_by": "sayali@frappe.io", "module": "LMS", "name": "LMS Settings",