Merge pull request #1440 from frappe/develop

chore: merge 'develop' into 'main'
This commit is contained in:
Jannat Patel
2025-04-16 18:55:21 +05:30
committed by GitHub
35 changed files with 10521 additions and 3810 deletions

View File

@@ -68,7 +68,6 @@ declare module 'vue' {
NoPermission: typeof import('./src/components/NoPermission.vue')['default'] NoPermission: typeof import('./src/components/NoPermission.vue')['default']
NoSidebarLayout: typeof import('./src/components/NoSidebarLayout.vue')['default'] NoSidebarLayout: typeof import('./src/components/NoSidebarLayout.vue')['default']
NotPermitted: typeof import('./src/components/NotPermitted.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'] PageModal: typeof import('./src/components/Modals/PageModal.vue')['default']
PaymentSettings: typeof import('./src/components/PaymentSettings.vue')['default'] PaymentSettings: typeof import('./src/components/PaymentSettings.vue')['default']
ProgressBar: typeof import('./src/components/ProgressBar.vue')['default'] ProgressBar: typeof import('./src/components/ProgressBar.vue')['default']

View File

@@ -38,7 +38,7 @@
<div class="mb-4"> <div class="mb-4">
<Button @click="openFileSelector" :loading="uploading"> <Button @click="openFileSelector" :loading="uploading">
{{ {{
uploading ? `Uploading ${progress}%` : 'Upload an zip file' uploading ? `Uploading ${progress}%` : 'Upload an ZIP file'
}} }}
</Button> </Button>
</div> </div>

View File

@@ -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",
},
],
},
], ],
}, },
] ]

View File

@@ -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>

View File

@@ -8,98 +8,109 @@
{{ __('Save') }} {{ __('Save') }}
</Button> </Button>
</header> </header>
<div class="w-1/2 mx-auto py-5"> <div class="w-3/4 mx-auto py-5">
<div class=""> <div class="">
<div class="text-lg font-semibold mb-4"> <div class="text-lg font-semibold mb-4">
{{ __('Details') }} {{ __('Details') }}
</div> </div>
<div class="space-y-4 mb-4"> <div class="space-y-10 mb-4">
<FormControl <div class="grid grid-cols-2 gap-10">
v-model="batch.title"
:label="__('Title')"
:required="true"
class="w-full"
/>
<div class="flex items-center space-x-5">
<FormControl <FormControl
v-model="batch.published" v-model="batch.title"
type="checkbox" :label="__('Title')"
:label="__('Published')" :required="true"
class="w-full"
/> />
<FormControl <MultiSelect
v-model="batch.allow_self_enrollment" v-model="instructors"
type="checkbox" doctype="User"
:label="__('Allow self enrollment')" :label="__('Instructors')"
/> :required="true"
<FormControl :filters="{ ignore_user_type: 1 }"
v-model="batch.certification"
type="checkbox"
:label="__('Certification')"
/> />
</div> </div>
</div>
</div> <div class="grid grid-cols-2 gap-10">
<div class="mb-4"> <div class="flex flex-col space-y-5">
<div class="text-xs text-ink-gray-5 mb-2"> <FormControl
{{ __('Meta Image') }} v-model="batch.published"
</div> type="checkbox"
<FileUploader :label="__('Published')"
v-if="!batch.image" />
:fileTypes="['image/*']" <FormControl
:validateFile="validateFile" v-model="batch.allow_self_enrollment"
@success="(file) => saveImage(file)" type="checkbox"
> :label="__('Allow self enrollment')"
<template v-slot="{ file, progress, uploading, openFileSelector }"> />
<div class="flex items-center"> <FormControl
<div class="border rounded-md w-fit py-5 px-20"> v-model="batch.certification"
<Image class="size-5 stroke-1 text-ink-gray-7" /> type="checkbox"
:label="__('Certification')"
/>
</div>
<div>
<div class="text-xs text-ink-gray-5 mb-2">
{{ __('Meta Image') }}
</div> </div>
<div class="ml-4"> <FileUploader
<Button @click="openFileSelector"> v-if="!batch.image"
{{ __('Upload') }} :fileTypes="['image/*']"
</Button> :validateFile="validateFile"
<div class="mt-2 text-ink-gray-5 text-sm"> @success="(file) => saveImage(file)"
{{ >
__( <template
'Appears when the batch URL is shared on any online platform' 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> </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> </div>
</div> </div>
<MultiSelect
v-model="instructors"
doctype="User"
:label="__('Instructors')"
:required="true"
:filters="{ ignore_user_type: 1 }"
/>
<div class="my-10"> <div class="my-10">
<div class="text-lg font-semibold mb-4"> <div class="text-lg font-semibold mb-4">
{{ __('Date and Time') }} {{ __('Date and Time') }}
</div> </div>
<div class="grid grid-cols-2 gap-10"> <div class="grid grid-cols-3 gap-10">
<div> <div>
<FormControl <FormControl
v-model="batch.start_date" v-model="batch.start_date"
@@ -115,14 +126,6 @@
class="mb-4" class="mb-4"
:required="true" :required="true"
/> />
<FormControl
v-model="batch.timezone"
:label="__('Timezone')"
type="text"
:placeholder="__('Example: IST (+5:30)')"
class="mb-4"
:required="true"
/>
</div> </div>
<div> <div>
<FormControl <FormControl
@@ -140,6 +143,16 @@
:required="true" :required="true"
/> />
</div> </div>
<div>
<FormControl
v-model="batch.timezone"
:label="__('Timezone')"
type="text"
:placeholder="__('Example: IST (+5:30)')"
class="mb-4"
:required="true"
/>
</div>
</div> </div>
</div> </div>
@@ -147,7 +160,7 @@
<div class="text-lg font-semibold mb-4"> <div class="text-lg font-semibold mb-4">
{{ __('Settings') }} {{ __('Settings') }}
</div> </div>
<div class="grid grid-cols-2 gap-10"> <div class="grid grid-cols-3 gap-10">
<div> <div>
<FormControl <FormControl
v-model="batch.seat_count" v-model="batch.seat_count"
@@ -162,11 +175,6 @@
type="date" type="date"
class="mb-4" class="mb-4"
/> />
<Link
doctype="Email Template"
:label="__('Email Template')"
v-model="batch.confirmation_email_template"
/>
</div> </div>
<div> <div>
<FormControl <FormControl
@@ -191,6 +199,13 @@
v-model="batch.category" v-model="batch.category"
/> />
</div> </div>
<div>
<Link
doctype="Email Template"
:label="__('Email Template')"
v-model="batch.confirmation_email_template"
/>
</div>
</div> </div>
</div> </div>
@@ -198,17 +213,16 @@
<div class="text-lg font-semibold mb-4"> <div class="text-lg font-semibold mb-4">
{{ __('Payment') }} {{ __('Payment') }}
</div> </div>
<div> <FormControl
<FormControl v-model="batch.paid_batch"
v-model="batch.paid_batch" type="checkbox"
type="checkbox" :label="__('Paid Batch')"
:label="__('Paid Batch')" />
/> <div class="grid grid-cols-3 gap-10 mt-4">
<FormControl <FormControl
v-model="batch.amount" v-model="batch.amount"
:label="__('Amount')" :label="__('Amount')"
type="number" type="number"
class="my-4"
/> />
<Link <Link
doctype="Currency" doctype="Currency"
@@ -445,7 +459,7 @@ const createNewBatch = () => {
}) })
}, },
onError(err) { 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) { onError(err) {
showToast('Error', err.messages?.[0] || err, 'x') showToast('Message', err.messages?.[0] || err, 'alert-circle')
}, },
} }
) )

View File

@@ -310,11 +310,7 @@ const course = reactive({
}) })
onMounted(() => { onMounted(() => {
if ( if (!user.data?.is_moderator && !user.data?.is_instructor) {
props.courseName == 'new' &&
!user.data?.is_moderator &&
!user.data?.is_instructor
) {
router.push({ name: 'Courses' }) router.push({ name: 'Courses' })
} }

View File

@@ -50,8 +50,7 @@ export const sessionStore = defineStore('lms-session', () => {
brand.name = data.app_name brand.name = data.app_name
brand.logo = data.app_logo brand.logo = data.app_logo
brand.favicon = brand.favicon =
data.favicon?.file_url || data.favicon?.file_url || '/assets/lms/frontend/learning.svg'
'/assets/lms/frontend/public/learning.svg'
}, },
}) })

View File

@@ -109,7 +109,7 @@ export function showToast(title, text, icon, iconClasses = null) {
icon: icon, icon: icon,
iconClasses: iconClasses, iconClasses: iconClasses,
position: icon == 'check' ? 'bottom-right' : 'top-center', position: icon == 'check' ? 'bottom-right' : 'top-center',
timeout: 5, timeout: icon != 'check' ? 10 : 5,
}) })
} }

View File

@@ -1 +1 @@
__version__ = "2.26.0" __version__ = "2.27.0"

View File

@@ -246,7 +246,7 @@ on_login = "lms.lms.user.on_login"
add_to_apps_screen = [ add_to_apps_screen = [
{ {
"name": "lms", "name": "lms",
"logo": "/assets/lms/images/lms-logo.png", "logo": "/assets/lms/frontend/learning.svg",
"title": "Learning", "title": "Learning",
"route": "/lms", "route": "/lms",
"has_permission": "lms.lms.api.check_app_permission", "has_permission": "lms.lms.api.check_app_permission",

View File

@@ -53,7 +53,12 @@ class LMSBatch(Document):
if self.paid_batch: if self.paid_batch:
installed_apps = frappe.get_installed_apps() installed_apps = frappe.get_installed_apps()
if "payments" not in 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): def validate_amount_and_currency(self):
if self.paid_batch and (not self.amount or not self.currency): if self.paid_batch and (not self.amount or not self.currency):

View File

@@ -50,7 +50,12 @@ class LMSCourse(Document):
if self.paid_course: if self.paid_course:
installed_apps = frappe.get_installed_apps() installed_apps = frappe.get_installed_apps()
if "payments" not in 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): def validate_certification(self):
if self.enable_certification and self.paid_certificate: if self.enable_certification and self.paid_certificate:

View File

@@ -20,10 +20,11 @@ frappe.ui.form.on("LMS Settings", {
frm.get_field("payments_app_is_not_installed").html(` frm.get_field("payments_app_is_not_installed").html(`
<div class="alert alert-warning"> <div class="alert alert-warning">
Please install the Please install the
<a target="_blank" style="color: var(--alert-text-warning); background: var(--alert-bg-warning);" href="https://frappecloud.com/marketplace/apps/payments"> <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>
Payments app to enable payment gateway. Refer to the
</a> <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>
to enable payment gateway. for more information.
</div>
`); `);
}, },
}); });

View File

@@ -8,7 +8,6 @@
"general_tab", "general_tab",
"default_home", "default_home",
"send_calendar_invite_for_evaluations", "send_calendar_invite_for_evaluations",
"is_onboarding_complete",
"column_break_zdel", "column_break_zdel",
"allow_guest_access", "allow_guest_access",
"enable_learning_paths", "enable_learning_paths",
@@ -60,7 +59,9 @@
"batch_confirmation_template", "batch_confirmation_template",
"column_break_uwsp", "column_break_uwsp",
"assignment_submission_template", "assignment_submission_template",
"payment_reminder_template" "payment_reminder_template",
"seo_tab",
"meta_description"
], ],
"fields": [ "fields": [
{ {
@@ -107,13 +108,6 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Identify User Persona" "label": "Identify User Persona"
}, },
{
"default": "0",
"fieldname": "is_onboarding_complete",
"fieldtype": "Check",
"label": "Is Onboarding Complete",
"read_only": 1
},
{ {
"default": "0", "default": "0",
"fieldname": "default_home", "fieldname": "default_home",
@@ -372,14 +366,25 @@
"fieldname": "disable_signup", "fieldname": "disable_signup",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Disable Signup" "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, "grid_page_length": 50,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2025-04-07 18:05:52.000651", "modified": "2025-04-10 16:17:00.658698",
"modified_by": "Administrator", "modified_by": "sayali@frappe.io",
"module": "LMS", "module": "LMS",
"name": "LMS Settings", "name": "LMS Settings",
"owner": "Administrator", "owner": "Administrator",

View File

@@ -8,7 +8,6 @@ import requests
from frappe import _ from frappe import _
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_result 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.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.desk.notifications import extract_mentions
from frappe.utils import ( from frappe.utils import (
add_months, add_months,
@@ -20,7 +19,6 @@ from frappe.utils import (
format_date, format_date,
get_datetime, get_datetime,
getdate, getdate,
validate_phone_number,
get_fullname, get_fullname,
pretty_date, pretty_date,
get_time_str, get_time_str,
@@ -1390,6 +1388,13 @@ def get_batch_details(batch):
batch_details.instructors = get_instructors(batch) batch_details.instructors = get_instructors(batch)
batch_details.accept_enrollments = batch_details.start_date > getdate() 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_details.courses = frappe.get_all(
"Batch Course", filters={"parent": batch}, fields=["course", "title", "evaluator"] "Batch Course", filters={"parent": batch}, fields=["course", "title", "evaluator"]
) )

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6261
lms/locale/sr_CS.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@ import re
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from frappe import _ from frappe import _
from frappe.utils.telemetry import capture from frappe.utils.telemetry import capture
from frappe.utils import cint
no_cache = 1 no_cache = 1
@@ -15,22 +14,24 @@ def get_context():
or "/assets/lms/frontend/favicon.png" or "/assets/lms/frontend/favicon.png"
) )
title = frappe.db.get_single_value("Website Settings", "app_name") or "Frappe Learning" 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() csrf_token = frappe.sessions.get_csrf_token()
frappe.db.commit() frappe.db.commit()
context = frappe._dict() context = frappe._dict()
context.csrf_token = csrf_token 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") capture("active_site", "lms")
context.title = title context.title = title
context.favicon = favicon context.favicon = favicon
return context return context
def get_meta(app_path, title, favicon): def get_meta(app_path, title, favicon, description):
meta = {} meta = frappe._dict()
if app_path: 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"]) 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": elif row.key == "link":
meta["link"] = row.value 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: if not meta:
meta = { meta = {
"title": title, "title": title,
"image": favicon, "image": favicon,
"description": "Easy to use Learning Management System", "description": description,
} }
return meta return meta
def get_meta_from_document(app_path, favicon): def get_meta_from_document(app_path):
if app_path == "courses": if app_path == "courses":
return { return {
"title": _("Course List"), "title": _("Course List"),
"image": favicon,
"description": "This page lists all the courses published on our website",
"keywords": "All Courses, Courses, Learn", "keywords": "All Courses, Courses, Learn",
"link": "/courses", "link": "/courses",
} }
@@ -72,7 +83,6 @@ def get_meta_from_document(app_path, favicon):
return { return {
"title": _("New Course"), "title": _("New Course"),
"image": frappe.db.get_single_value("Website Settings", "banner_image"), "image": frappe.db.get_single_value("Website Settings", "banner_image"),
"description": "Create a new course",
"keywords": "New Course, Create Course", "keywords": "New Course, Create Course",
"link": "/lms/courses/new/edit", "link": "/lms/courses/new/edit",
} }
@@ -99,8 +109,6 @@ def get_meta_from_document(app_path, favicon):
if app_path == "batches": if app_path == "batches":
return { return {
"title": _("Batches"), "title": _("Batches"),
"image": favicon,
"description": "This page lists all the batches published on our website",
"keywords": "All Batches, Batches, Learn", "keywords": "All Batches, Batches, Learn",
"link": "/batches", "link": "/batches",
} }
@@ -130,8 +138,6 @@ def get_meta_from_document(app_path, favicon):
if "new/edit" in app_path: if "new/edit" in app_path:
return { return {
"title": _("New Batch"), "title": _("New Batch"),
"image": favicon,
"description": "Create a new batch",
"keywords": "New Batch, Create Batch", "keywords": "New Batch, Create Batch",
"link": "/lms/batches/new/edit", "link": "/lms/batches/new/edit",
} }
@@ -157,8 +163,6 @@ def get_meta_from_document(app_path, favicon):
if app_path == "job-openings": if app_path == "job-openings":
return { return {
"title": _("Job Openings"), "title": _("Job Openings"),
"image": favicon,
"description": "This page lists all the job openings published on our website",
"keywords": "Job Openings, Jobs, Vacancies", "keywords": "Job Openings, Jobs, Vacancies",
"link": "/job-openings", "link": "/job-openings",
} }
@@ -171,6 +175,11 @@ def get_meta_from_document(app_path, favicon):
["job_title", "company_logo", "description"], ["job_title", "company_logo", "description"],
as_dict=True, as_dict=True,
) )
if job_opening.description:
soup = BeautifulSoup(job_opening.description, "html.parser")
job_opening.description = soup.get_text()
return { return {
"title": job_opening.job_title, "title": job_opening.job_title,
"image": job_opening.company_logo, "image": job_opening.company_logo,
@@ -182,8 +191,6 @@ def get_meta_from_document(app_path, favicon):
if app_path == "statistics": if app_path == "statistics":
return { return {
"title": _("Statistics"), "title": _("Statistics"),
"image": favicon,
"description": "This page lists all the statistics of this platform",
"keywords": "Enrollment Count, Completion, Signups", "keywords": "Enrollment Count, Completion, Signups",
"link": "/statistics", "link": "/statistics",
} }
@@ -231,8 +238,6 @@ def get_meta_from_document(app_path, favicon):
if app_path == "quizzes": if app_path == "quizzes":
return { return {
"title": _("Quizzes"), "title": _("Quizzes"),
"image": favicon,
"description": _("Test your knowledge with interactive quizzes and more."),
"keywords": "Quizzes, interactive quizzes, online quizzes", "keywords": "Quizzes, interactive quizzes, online quizzes",
"link": "/quizzes", "link": "/quizzes",
} }
@@ -248,8 +253,6 @@ def get_meta_from_document(app_path, favicon):
if quiz: if quiz:
return { return {
"title": quiz.title, "title": quiz.title,
"image": favicon,
"description": "Test your knowledge with interactive quizzes.",
"keywords": quiz.title, "keywords": quiz.title,
"link": f"/quizzes/{quiz_name}", "link": f"/quizzes/{quiz_name}",
} }
@@ -257,8 +260,6 @@ def get_meta_from_document(app_path, favicon):
if app_path == "assignments": if app_path == "assignments":
return { return {
"title": _("Assignments"), "title": _("Assignments"),
"image": favicon,
"description": _("Test your knowledge with interactive assignments and more."),
"keywords": "Assignments, interactive assignments, online assignments", "keywords": "Assignments, interactive assignments, online assignments",
"link": "/assignments", "link": "/assignments",
} }
@@ -274,8 +275,6 @@ def get_meta_from_document(app_path, favicon):
if assignment: if assignment:
return { return {
"title": assignment.title, "title": assignment.title,
"image": favicon,
"description": "Test your knowledge with interactive assignments.",
"keywords": assignment.title, "keywords": assignment.title,
"link": f"/assignments/{assignment_name}", "link": f"/assignments/{assignment_name}",
} }
@@ -283,8 +282,8 @@ def get_meta_from_document(app_path, favicon):
if app_path == "programs": if app_path == "programs":
return { return {
"title": _("Programs"), "title": _("Programs"),
"image": favicon,
"description": "This page lists all the programs published on our website",
"keywords": "All Programs, Programs, Learn", "keywords": "All Programs, Programs, Learn",
"link": "/programs", "link": "/programs",
} }
return {}