Merge pull request #1440 from frappe/develop
chore: merge 'develop' into 'main'
This commit is contained in:
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@@ -68,7 +68,6 @@ declare module 'vue' {
|
||||
NoPermission: typeof import('./src/components/NoPermission.vue')['default']
|
||||
NoSidebarLayout: typeof import('./src/components/NoSidebarLayout.vue')['default']
|
||||
NotPermitted: typeof import('./src/components/NotPermitted.vue')['default']
|
||||
OnboardingBanner: typeof import('./src/components/OnboardingBanner.vue')['default']
|
||||
PageModal: typeof import('./src/components/Modals/PageModal.vue')['default']
|
||||
PaymentSettings: typeof import('./src/components/PaymentSettings.vue')['default']
|
||||
ProgressBar: typeof import('./src/components/ProgressBar.vue')['default']
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="mb-4">
|
||||
<Button @click="openFileSelector" :loading="uploading">
|
||||
{{
|
||||
uploading ? `Uploading ${progress}%` : 'Upload an zip file'
|
||||
uploading ? `Uploading ${progress}%` : 'Upload an ZIP file'
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -350,6 +350,20 @@ const tabsStructure = computed(() => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'SEO',
|
||||
icon: 'Search',
|
||||
fields: [
|
||||
{
|
||||
label: 'Meta Description',
|
||||
name: 'meta_description',
|
||||
type: 'textarea',
|
||||
rows: 5,
|
||||
description:
|
||||
"This description will be shown on lists and pages that don't have meta description",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
<template>
|
||||
<div v-if="showOnboardingBanner && onboardingDetails.data">
|
||||
<Tooltip :text="__('Skip Onboarding')" placement="left">
|
||||
<X
|
||||
class="w-4 h-4 stroke-1 absolute top-2 right-2 cursor-pointer mr-1"
|
||||
@click="skipOnboarding.reload()"
|
||||
/>
|
||||
</Tooltip>
|
||||
<div class="flex items-center justify-evenly bg-surface-gray-2 p-10">
|
||||
<div
|
||||
@click="redirectToCourseForm()"
|
||||
class="flex items-center space-x-2"
|
||||
:class="{
|
||||
'cursor-pointer': !onboardingDetails.data.course_created?.length,
|
||||
}"
|
||||
>
|
||||
<span
|
||||
v-if="onboardingDetails.data.course_created?.length"
|
||||
class="py-1 px-1 bg-surface-white rounded-full"
|
||||
>
|
||||
<Check class="h-4 w-4 stroke-2 text-ink-green-3" />
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
class="font-semibold bg-surface-white px-2 py-1 rounded-full"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
<span class="text-lg font-semibold">
|
||||
{{ __('Create a course') }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@click="redirectToChapterForm()"
|
||||
class="flex items-center space-x-2"
|
||||
:class="{
|
||||
'cursor-pointer':
|
||||
onboardingDetails.data.course_created?.length &&
|
||||
!onboardingDetails.data.chapter_created?.length,
|
||||
'text-ink-gray-3': !onboardingDetails.data.course_created?.length,
|
||||
}"
|
||||
>
|
||||
<span
|
||||
v-if="onboardingDetails.data.chapter_created?.length"
|
||||
class="py-1 px-1 bg-surface-white rounded-full"
|
||||
>
|
||||
<Check class="h-4 w-4 stroke-2 text-ink-green-3" />
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
class="font-semibold bg-surface-white px-2 py-1 rounded-full"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
<span class="text-lg font-semibold">
|
||||
{{ __('Add a chapter') }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@click="redirectToLessonForm()"
|
||||
class="flex items-center space-x-2"
|
||||
:class="{
|
||||
'cursor-pointer':
|
||||
onboardingDetails.data.course_created?.length &&
|
||||
onboardingDetails.data.chapter_created?.length,
|
||||
'text-ink-gray-3':
|
||||
!onboardingDetails.data.course_created?.length ||
|
||||
!onboardingDetails.data.chapter_created?.length,
|
||||
}"
|
||||
>
|
||||
<span
|
||||
v-if="onboardingDetails.data.lesson_created?.length"
|
||||
class="py-1 px-1 bg-surface-white rounded-full"
|
||||
>
|
||||
<Check class="h-4 w-4 stroke-2 text-ink-green-3" />
|
||||
</span>
|
||||
<span class="font-semibold bg-surface-white px-2 py-1 rounded-full">
|
||||
3
|
||||
</span>
|
||||
<span class="text-lg font-semibold">
|
||||
{{ __('Add a lesson') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { Check, X } from 'lucide-vue-next'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useSettings } from '@/stores/settings'
|
||||
import { createResource, Tooltip } from 'frappe-ui'
|
||||
|
||||
const showOnboardingBanner = ref(false)
|
||||
const settings = useSettings()
|
||||
const onboardingDetails = settings.onboardingDetails
|
||||
const router = useRouter()
|
||||
|
||||
watch(onboardingDetails, () => {
|
||||
if (!onboardingDetails.data?.is_onboarded) {
|
||||
showOnboardingBanner.value = true
|
||||
} else {
|
||||
showOnboardingBanner.value = false
|
||||
}
|
||||
})
|
||||
|
||||
const redirectToCourseForm = () => {
|
||||
if (onboardingDetails.data?.course_created.length) {
|
||||
return
|
||||
} else {
|
||||
router.push({ name: 'CourseForm', params: { courseName: 'new' } })
|
||||
}
|
||||
}
|
||||
|
||||
const redirectToChapterForm = () => {
|
||||
if (!onboardingDetails.data?.course_created.length) {
|
||||
return
|
||||
} else {
|
||||
router.push({
|
||||
name: 'CourseForm',
|
||||
params: {
|
||||
courseName: onboardingDetails.data?.first_course,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const redirectToLessonForm = () => {
|
||||
if (!onboardingDetails.data?.course_created.length) {
|
||||
return
|
||||
} else if (!onboardingDetails.data?.chapter_created.length) {
|
||||
return
|
||||
} else {
|
||||
router.push({
|
||||
name: 'LessonForm',
|
||||
params: {
|
||||
courseName: onboardingDetails.data?.first_course,
|
||||
chapterNumber: 1,
|
||||
lessonNumber: 1,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const skipOnboarding = createResource({
|
||||
url: 'frappe.client.set_value',
|
||||
makeParams() {
|
||||
return {
|
||||
doctype: 'LMS Settings',
|
||||
name: 'LMS Settings',
|
||||
fieldname: 'is_onboarding_complete',
|
||||
value: 1,
|
||||
}
|
||||
},
|
||||
onSuccess(data) {
|
||||
onboardingDetails.reload()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -8,98 +8,109 @@
|
||||
{{ __('Save') }}
|
||||
</Button>
|
||||
</header>
|
||||
<div class="w-1/2 mx-auto py-5">
|
||||
<div class="w-3/4 mx-auto py-5">
|
||||
<div class="">
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
{{ __('Details') }}
|
||||
</div>
|
||||
<div class="space-y-4 mb-4">
|
||||
<FormControl
|
||||
v-model="batch.title"
|
||||
:label="__('Title')"
|
||||
:required="true"
|
||||
class="w-full"
|
||||
/>
|
||||
<div class="flex items-center space-x-5">
|
||||
<div class="space-y-10 mb-4">
|
||||
<div class="grid grid-cols-2 gap-10">
|
||||
<FormControl
|
||||
v-model="batch.published"
|
||||
type="checkbox"
|
||||
:label="__('Published')"
|
||||
v-model="batch.title"
|
||||
:label="__('Title')"
|
||||
:required="true"
|
||||
class="w-full"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.allow_self_enrollment"
|
||||
type="checkbox"
|
||||
:label="__('Allow self enrollment')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.certification"
|
||||
type="checkbox"
|
||||
:label="__('Certification')"
|
||||
<MultiSelect
|
||||
v-model="instructors"
|
||||
doctype="User"
|
||||
:label="__('Instructors')"
|
||||
:required="true"
|
||||
:filters="{ ignore_user_type: 1 }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-xs text-ink-gray-5 mb-2">
|
||||
{{ __('Meta Image') }}
|
||||
</div>
|
||||
<FileUploader
|
||||
v-if="!batch.image"
|
||||
:fileTypes="['image/*']"
|
||||
:validateFile="validateFile"
|
||||
@success="(file) => saveImage(file)"
|
||||
>
|
||||
<template v-slot="{ file, progress, uploading, openFileSelector }">
|
||||
<div class="flex items-center">
|
||||
<div class="border rounded-md w-fit py-5 px-20">
|
||||
<Image class="size-5 stroke-1 text-ink-gray-7" />
|
||||
|
||||
<div class="grid grid-cols-2 gap-10">
|
||||
<div class="flex flex-col space-y-5">
|
||||
<FormControl
|
||||
v-model="batch.published"
|
||||
type="checkbox"
|
||||
:label="__('Published')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.allow_self_enrollment"
|
||||
type="checkbox"
|
||||
:label="__('Allow self enrollment')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.certification"
|
||||
type="checkbox"
|
||||
:label="__('Certification')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-xs text-ink-gray-5 mb-2">
|
||||
{{ __('Meta Image') }}
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<Button @click="openFileSelector">
|
||||
{{ __('Upload') }}
|
||||
</Button>
|
||||
<div class="mt-2 text-ink-gray-5 text-sm">
|
||||
{{
|
||||
__(
|
||||
'Appears when the batch URL is shared on any online platform'
|
||||
)
|
||||
}}
|
||||
<FileUploader
|
||||
v-if="!batch.image"
|
||||
:fileTypes="['image/*']"
|
||||
:validateFile="validateFile"
|
||||
@success="(file) => saveImage(file)"
|
||||
>
|
||||
<template
|
||||
v-slot="{ file, progress, uploading, openFileSelector }"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div class="border rounded-md w-fit py-5 px-20">
|
||||
<Image class="size-5 stroke-1 text-ink-gray-7" />
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<Button @click="openFileSelector">
|
||||
{{ __('Upload') }}
|
||||
</Button>
|
||||
<div class="mt-2 text-ink-gray-5 text-sm">
|
||||
{{
|
||||
__(
|
||||
'Appears when the batch URL is shared on any online platform'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<div v-else class="mb-4">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
:src="batch.image.file_url"
|
||||
class="border rounded-md w-40"
|
||||
/>
|
||||
<div class="ml-4">
|
||||
<Button @click="removeImage()">
|
||||
{{ __('Remove') }}
|
||||
</Button>
|
||||
<div class="mt-2 text-ink-gray-5 text-sm">
|
||||
{{
|
||||
__(
|
||||
'Appears when the batch URL is shared on any online platform'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<div v-else class="mb-4">
|
||||
<div class="flex items-center">
|
||||
<img :src="batch.image.file_url" class="border rounded-md w-40" />
|
||||
<div class="ml-4">
|
||||
<Button @click="removeImage()">
|
||||
{{ __('Remove') }}
|
||||
</Button>
|
||||
<div class="mt-2 text-ink-gray-5 text-sm">
|
||||
{{
|
||||
__(
|
||||
'Appears when the batch URL is shared on any online platform'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MultiSelect
|
||||
v-model="instructors"
|
||||
doctype="User"
|
||||
:label="__('Instructors')"
|
||||
:required="true"
|
||||
:filters="{ ignore_user_type: 1 }"
|
||||
/>
|
||||
|
||||
<div class="my-10">
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
{{ __('Date and Time') }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-10">
|
||||
<div class="grid grid-cols-3 gap-10">
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.start_date"
|
||||
@@ -115,14 +126,6 @@
|
||||
class="mb-4"
|
||||
:required="true"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.timezone"
|
||||
:label="__('Timezone')"
|
||||
type="text"
|
||||
:placeholder="__('Example: IST (+5:30)')"
|
||||
class="mb-4"
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
@@ -140,6 +143,16 @@
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.timezone"
|
||||
:label="__('Timezone')"
|
||||
type="text"
|
||||
:placeholder="__('Example: IST (+5:30)')"
|
||||
class="mb-4"
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -147,7 +160,7 @@
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
{{ __('Settings') }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-10">
|
||||
<div class="grid grid-cols-3 gap-10">
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.seat_count"
|
||||
@@ -162,11 +175,6 @@
|
||||
type="date"
|
||||
class="mb-4"
|
||||
/>
|
||||
<Link
|
||||
doctype="Email Template"
|
||||
:label="__('Email Template')"
|
||||
v-model="batch.confirmation_email_template"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
@@ -191,6 +199,13 @@
|
||||
v-model="batch.category"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Link
|
||||
doctype="Email Template"
|
||||
:label="__('Email Template')"
|
||||
v-model="batch.confirmation_email_template"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -198,17 +213,16 @@
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
{{ __('Payment') }}
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.paid_batch"
|
||||
type="checkbox"
|
||||
:label="__('Paid Batch')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.paid_batch"
|
||||
type="checkbox"
|
||||
:label="__('Paid Batch')"
|
||||
/>
|
||||
<div class="grid grid-cols-3 gap-10 mt-4">
|
||||
<FormControl
|
||||
v-model="batch.amount"
|
||||
:label="__('Amount')"
|
||||
type="number"
|
||||
class="my-4"
|
||||
/>
|
||||
<Link
|
||||
doctype="Currency"
|
||||
@@ -445,7 +459,7 @@ const createNewBatch = () => {
|
||||
})
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
showToast('Message', err.messages?.[0] || err, 'alert-circle')
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -464,7 +478,7 @@ const editBatchDetails = () => {
|
||||
})
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
showToast('Message', err.messages?.[0] || err, 'alert-circle')
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -310,11 +310,7 @@ const course = reactive({
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (
|
||||
props.courseName == 'new' &&
|
||||
!user.data?.is_moderator &&
|
||||
!user.data?.is_instructor
|
||||
) {
|
||||
if (!user.data?.is_moderator && !user.data?.is_instructor) {
|
||||
router.push({ name: 'Courses' })
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,7 @@ export const sessionStore = defineStore('lms-session', () => {
|
||||
brand.name = data.app_name
|
||||
brand.logo = data.app_logo
|
||||
brand.favicon =
|
||||
data.favicon?.file_url ||
|
||||
'/assets/lms/frontend/public/learning.svg'
|
||||
data.favicon?.file_url || '/assets/lms/frontend/learning.svg'
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ export function showToast(title, text, icon, iconClasses = null) {
|
||||
icon: icon,
|
||||
iconClasses: iconClasses,
|
||||
position: icon == 'check' ? 'bottom-right' : 'top-center',
|
||||
timeout: 5,
|
||||
timeout: icon != 'check' ? 10 : 5,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "2.26.0"
|
||||
__version__ = "2.27.0"
|
||||
|
||||
@@ -246,7 +246,7 @@ on_login = "lms.lms.user.on_login"
|
||||
add_to_apps_screen = [
|
||||
{
|
||||
"name": "lms",
|
||||
"logo": "/assets/lms/images/lms-logo.png",
|
||||
"logo": "/assets/lms/frontend/learning.svg",
|
||||
"title": "Learning",
|
||||
"route": "/lms",
|
||||
"has_permission": "lms.lms.api.check_app_permission",
|
||||
|
||||
@@ -53,7 +53,12 @@ class LMSBatch(Document):
|
||||
if self.paid_batch:
|
||||
installed_apps = frappe.get_installed_apps()
|
||||
if "payments" not in installed_apps:
|
||||
frappe.throw(_("Please install the Payments app to create a paid batches."))
|
||||
documentation_link = "https://docs.frappe.io/learning/setting-up-payment-gateway"
|
||||
frappe.throw(
|
||||
_(
|
||||
"Please install the Payments App to create a paid batch. Refer to the documentation for more details. {0}"
|
||||
).format(documentation_link)
|
||||
)
|
||||
|
||||
def validate_amount_and_currency(self):
|
||||
if self.paid_batch and (not self.amount or not self.currency):
|
||||
|
||||
@@ -50,7 +50,12 @@ class LMSCourse(Document):
|
||||
if self.paid_course:
|
||||
installed_apps = frappe.get_installed_apps()
|
||||
if "payments" not in installed_apps:
|
||||
frappe.throw(_("Please install the Payments app to create a paid courses."))
|
||||
documentation_link = "https://docs.frappe.io/learning/setting-up-payment-gateway"
|
||||
frappe.throw(
|
||||
_(
|
||||
"Please install the Payments App to create a paid course. Refer to the documentation for more details. {0}"
|
||||
).format(documentation_link)
|
||||
)
|
||||
|
||||
def validate_certification(self):
|
||||
if self.enable_certification and self.paid_certificate:
|
||||
|
||||
@@ -20,10 +20,11 @@ frappe.ui.form.on("LMS Settings", {
|
||||
frm.get_field("payments_app_is_not_installed").html(`
|
||||
<div class="alert alert-warning">
|
||||
Please install the
|
||||
<a target="_blank" style="color: var(--alert-text-warning); background: var(--alert-bg-warning);" href="https://frappecloud.com/marketplace/apps/payments">
|
||||
Payments app
|
||||
</a>
|
||||
to enable payment gateway.
|
||||
<a target="_blank" style="text-decoration: underline; color: var(--alert-text-warning); background: var(--alert-bg-warning);" href="https://frappecloud.com/marketplace/apps/payments">Payments app</a>
|
||||
to enable payment gateway. Refer to the
|
||||
<a target="_blank" style="text-decoration: underline; color: var(--alert-text-warning); background: var(--alert-bg-warning);" href="https://docs.frappe.io/learning/setting-up-payment-gateway">Documentation</a>
|
||||
for more information.
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"general_tab",
|
||||
"default_home",
|
||||
"send_calendar_invite_for_evaluations",
|
||||
"is_onboarding_complete",
|
||||
"column_break_zdel",
|
||||
"allow_guest_access",
|
||||
"enable_learning_paths",
|
||||
@@ -60,7 +59,9 @@
|
||||
"batch_confirmation_template",
|
||||
"column_break_uwsp",
|
||||
"assignment_submission_template",
|
||||
"payment_reminder_template"
|
||||
"payment_reminder_template",
|
||||
"seo_tab",
|
||||
"meta_description"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -107,13 +108,6 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Identify User Persona"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_onboarding_complete",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Onboarding Complete",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "default_home",
|
||||
@@ -372,14 +366,25 @@
|
||||
"fieldname": "disable_signup",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Signup"
|
||||
},
|
||||
{
|
||||
"fieldname": "seo_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "SEO"
|
||||
},
|
||||
{
|
||||
"description": "This description will be shown on lists and pages without meta description",
|
||||
"fieldname": "meta_description",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Meta Description"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2025-04-07 18:05:52.000651",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2025-04-10 16:17:00.658698",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Settings",
|
||||
"owner": "Administrator",
|
||||
|
||||
@@ -8,7 +8,6 @@ import requests
|
||||
from frappe import _
|
||||
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_result
|
||||
from frappe.desk.doctype.notification_log.notification_log import make_notification_logs
|
||||
from frappe.desk.search import get_user_groups
|
||||
from frappe.desk.notifications import extract_mentions
|
||||
from frappe.utils import (
|
||||
add_months,
|
||||
@@ -20,7 +19,6 @@ from frappe.utils import (
|
||||
format_date,
|
||||
get_datetime,
|
||||
getdate,
|
||||
validate_phone_number,
|
||||
get_fullname,
|
||||
pretty_date,
|
||||
get_time_str,
|
||||
@@ -1390,6 +1388,13 @@ def get_batch_details(batch):
|
||||
batch_details.instructors = get_instructors(batch)
|
||||
batch_details.accept_enrollments = batch_details.start_date > getdate()
|
||||
|
||||
if (
|
||||
not batch_details.accept_enrollments
|
||||
and batch_details.start_date == getdate()
|
||||
and get_time_str(batch_details.start_time) > nowtime()
|
||||
):
|
||||
batch_details.accept_enrollments = True
|
||||
|
||||
batch_details.courses = frappe.get_all(
|
||||
"Batch Course", filters={"parent": batch}, fields=["course", "title", "evaluator"]
|
||||
)
|
||||
|
||||
390
lms/locale/ar.po
390
lms/locale/ar.po
File diff suppressed because it is too large
Load Diff
392
lms/locale/bs.po
392
lms/locale/bs.po
File diff suppressed because it is too large
Load Diff
394
lms/locale/de.po
394
lms/locale/de.po
File diff suppressed because it is too large
Load Diff
392
lms/locale/eo.po
392
lms/locale/eo.po
File diff suppressed because it is too large
Load Diff
390
lms/locale/es.po
390
lms/locale/es.po
File diff suppressed because it is too large
Load Diff
394
lms/locale/fa.po
394
lms/locale/fa.po
File diff suppressed because it is too large
Load Diff
398
lms/locale/fr.po
398
lms/locale/fr.po
File diff suppressed because it is too large
Load Diff
390
lms/locale/hr.po
390
lms/locale/hr.po
File diff suppressed because it is too large
Load Diff
398
lms/locale/hu.po
398
lms/locale/hu.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
500
lms/locale/pl.po
500
lms/locale/pl.po
File diff suppressed because it is too large
Load Diff
793
lms/locale/pt.po
793
lms/locale/pt.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
390
lms/locale/ru.po
390
lms/locale/ru.po
File diff suppressed because it is too large
Load Diff
6261
lms/locale/sr_CS.po
Normal file
6261
lms/locale/sr_CS.po
Normal file
File diff suppressed because it is too large
Load Diff
392
lms/locale/sv.po
392
lms/locale/sv.po
File diff suppressed because it is too large
Load Diff
388
lms/locale/th.po
388
lms/locale/th.po
File diff suppressed because it is too large
Load Diff
392
lms/locale/tr.po
392
lms/locale/tr.po
File diff suppressed because it is too large
Load Diff
392
lms/locale/zh.po
392
lms/locale/zh.po
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,6 @@ import re
|
||||
from bs4 import BeautifulSoup
|
||||
from frappe import _
|
||||
from frappe.utils.telemetry import capture
|
||||
from frappe.utils import cint
|
||||
|
||||
no_cache = 1
|
||||
|
||||
@@ -15,22 +14,24 @@ def get_context():
|
||||
or "/assets/lms/frontend/favicon.png"
|
||||
)
|
||||
title = frappe.db.get_single_value("Website Settings", "app_name") or "Frappe Learning"
|
||||
description = frappe.db.get_single_value("LMS Settings", "meta_description")
|
||||
csrf_token = frappe.sessions.get_csrf_token()
|
||||
frappe.db.commit()
|
||||
|
||||
context = frappe._dict()
|
||||
context.csrf_token = csrf_token
|
||||
context.meta = get_meta(app_path, title, favicon)
|
||||
context.meta = get_meta(app_path, title, favicon, description)
|
||||
capture("active_site", "lms")
|
||||
context.title = title
|
||||
context.favicon = favicon
|
||||
return context
|
||||
|
||||
|
||||
def get_meta(app_path, title, favicon):
|
||||
meta = {}
|
||||
def get_meta(app_path, title, favicon, description):
|
||||
meta = frappe._dict()
|
||||
|
||||
if app_path:
|
||||
meta = get_meta_from_document(app_path, favicon)
|
||||
meta = get_meta_from_document(app_path)
|
||||
|
||||
route_meta = frappe.get_all("Website Meta Tag", {"parent": app_path}, ["key", "value"])
|
||||
|
||||
@@ -47,22 +48,32 @@ def get_meta(app_path, title, favicon):
|
||||
elif row.key == "link":
|
||||
meta["link"] = row.value
|
||||
|
||||
if not meta.get("title"):
|
||||
meta["title"] = title
|
||||
|
||||
if not meta.get("description"):
|
||||
meta["description"] = description
|
||||
|
||||
if not meta.get("image"):
|
||||
meta["image"] = favicon
|
||||
|
||||
if not meta.get("keywords"):
|
||||
meta["keywords"] = ""
|
||||
|
||||
if not meta:
|
||||
meta = {
|
||||
"title": title,
|
||||
"image": favicon,
|
||||
"description": "Easy to use Learning Management System",
|
||||
"description": description,
|
||||
}
|
||||
|
||||
return meta
|
||||
|
||||
|
||||
def get_meta_from_document(app_path, favicon):
|
||||
def get_meta_from_document(app_path):
|
||||
if app_path == "courses":
|
||||
return {
|
||||
"title": _("Course List"),
|
||||
"image": favicon,
|
||||
"description": "This page lists all the courses published on our website",
|
||||
"keywords": "All Courses, Courses, Learn",
|
||||
"link": "/courses",
|
||||
}
|
||||
@@ -72,7 +83,6 @@ def get_meta_from_document(app_path, favicon):
|
||||
return {
|
||||
"title": _("New Course"),
|
||||
"image": frappe.db.get_single_value("Website Settings", "banner_image"),
|
||||
"description": "Create a new course",
|
||||
"keywords": "New Course, Create Course",
|
||||
"link": "/lms/courses/new/edit",
|
||||
}
|
||||
@@ -99,8 +109,6 @@ def get_meta_from_document(app_path, favicon):
|
||||
if app_path == "batches":
|
||||
return {
|
||||
"title": _("Batches"),
|
||||
"image": favicon,
|
||||
"description": "This page lists all the batches published on our website",
|
||||
"keywords": "All Batches, Batches, Learn",
|
||||
"link": "/batches",
|
||||
}
|
||||
@@ -130,8 +138,6 @@ def get_meta_from_document(app_path, favicon):
|
||||
if "new/edit" in app_path:
|
||||
return {
|
||||
"title": _("New Batch"),
|
||||
"image": favicon,
|
||||
"description": "Create a new batch",
|
||||
"keywords": "New Batch, Create Batch",
|
||||
"link": "/lms/batches/new/edit",
|
||||
}
|
||||
@@ -157,8 +163,6 @@ def get_meta_from_document(app_path, favicon):
|
||||
if app_path == "job-openings":
|
||||
return {
|
||||
"title": _("Job Openings"),
|
||||
"image": favicon,
|
||||
"description": "This page lists all the job openings published on our website",
|
||||
"keywords": "Job Openings, Jobs, Vacancies",
|
||||
"link": "/job-openings",
|
||||
}
|
||||
@@ -171,6 +175,11 @@ def get_meta_from_document(app_path, favicon):
|
||||
["job_title", "company_logo", "description"],
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
if job_opening.description:
|
||||
soup = BeautifulSoup(job_opening.description, "html.parser")
|
||||
job_opening.description = soup.get_text()
|
||||
|
||||
return {
|
||||
"title": job_opening.job_title,
|
||||
"image": job_opening.company_logo,
|
||||
@@ -182,8 +191,6 @@ def get_meta_from_document(app_path, favicon):
|
||||
if app_path == "statistics":
|
||||
return {
|
||||
"title": _("Statistics"),
|
||||
"image": favicon,
|
||||
"description": "This page lists all the statistics of this platform",
|
||||
"keywords": "Enrollment Count, Completion, Signups",
|
||||
"link": "/statistics",
|
||||
}
|
||||
@@ -231,8 +238,6 @@ def get_meta_from_document(app_path, favicon):
|
||||
if app_path == "quizzes":
|
||||
return {
|
||||
"title": _("Quizzes"),
|
||||
"image": favicon,
|
||||
"description": _("Test your knowledge with interactive quizzes and more."),
|
||||
"keywords": "Quizzes, interactive quizzes, online quizzes",
|
||||
"link": "/quizzes",
|
||||
}
|
||||
@@ -248,8 +253,6 @@ def get_meta_from_document(app_path, favicon):
|
||||
if quiz:
|
||||
return {
|
||||
"title": quiz.title,
|
||||
"image": favicon,
|
||||
"description": "Test your knowledge with interactive quizzes.",
|
||||
"keywords": quiz.title,
|
||||
"link": f"/quizzes/{quiz_name}",
|
||||
}
|
||||
@@ -257,8 +260,6 @@ def get_meta_from_document(app_path, favicon):
|
||||
if app_path == "assignments":
|
||||
return {
|
||||
"title": _("Assignments"),
|
||||
"image": favicon,
|
||||
"description": _("Test your knowledge with interactive assignments and more."),
|
||||
"keywords": "Assignments, interactive assignments, online assignments",
|
||||
"link": "/assignments",
|
||||
}
|
||||
@@ -274,8 +275,6 @@ def get_meta_from_document(app_path, favicon):
|
||||
if assignment:
|
||||
return {
|
||||
"title": assignment.title,
|
||||
"image": favicon,
|
||||
"description": "Test your knowledge with interactive assignments.",
|
||||
"keywords": assignment.title,
|
||||
"link": f"/assignments/{assignment_name}",
|
||||
}
|
||||
@@ -283,8 +282,8 @@ def get_meta_from_document(app_path, favicon):
|
||||
if app_path == "programs":
|
||||
return {
|
||||
"title": _("Programs"),
|
||||
"image": favicon,
|
||||
"description": "This page lists all the programs published on our website",
|
||||
"keywords": "All Programs, Programs, Learn",
|
||||
"link": "/programs",
|
||||
}
|
||||
|
||||
return {}
|
||||
|
||||
Reference in New Issue
Block a user