mirror of
https://github.com/frappe/lms.git
synced 2026-04-19 22:52:29 +03:00
@@ -10,7 +10,7 @@
|
||||
{{ course.data.price }}
|
||||
</div>
|
||||
<div v-if="!readOnlyMode">
|
||||
<div v-if="course.data.membership" class="space-y-2">
|
||||
<div v-if="course.data.membership" class="space-y-2 mb-8">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'Lesson',
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
}"
|
||||
>
|
||||
<Button variant="solid" size="md" class="w-full">
|
||||
<Button variant="solid" size="md" class="w-full mb-8">
|
||||
<template #prefix>
|
||||
<CreditCard class="size-4 stroke-1.5" />
|
||||
</template>
|
||||
@@ -67,7 +67,7 @@
|
||||
v-else-if="!isAdmin"
|
||||
@click="enrollStudent()"
|
||||
variant="solid"
|
||||
class="w-full"
|
||||
class="w-full mb-8"
|
||||
size="md"
|
||||
>
|
||||
<template #prefix>
|
||||
@@ -91,10 +91,7 @@
|
||||
</Button>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
class="font-medium text-ink-gray-9"
|
||||
:class="{ 'mt-8': !readOnlyMode }"
|
||||
>
|
||||
<div class="font-medium text-ink-gray-9">
|
||||
{{ __('This course has:') }}
|
||||
</div>
|
||||
<div class="flex items-center text-ink-gray-9">
|
||||
|
||||
@@ -48,7 +48,7 @@ const apps = createResource({
|
||||
name: 'frappe',
|
||||
logo: '/assets/lms/images/desk.png',
|
||||
title: __('Desk'),
|
||||
route: '/desk/lms',
|
||||
route: '/desk/learning',
|
||||
},
|
||||
]
|
||||
data.map((app) => {
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
:label="__('Preview Video')"
|
||||
:placeholder="
|
||||
__(
|
||||
'Paste the youtube link of a short video introducing the course'
|
||||
'Paste a YouTube link of a short video introducing the course'
|
||||
)
|
||||
"
|
||||
@input="makeFormDirty()"
|
||||
|
||||
@@ -694,7 +694,6 @@ export const escapeHTML = (text) => {
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'`': '`',
|
||||
'=': '=',
|
||||
}
|
||||
|
||||
return String(text).replace(
|
||||
@@ -707,6 +706,19 @@ export const sanitizeHTML = (text) => {
|
||||
text = DOMPurify.sanitize(decodeEntities(text), {
|
||||
ALLOWED_TAGS: [
|
||||
'b',
|
||||
'br',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'table',
|
||||
'thead',
|
||||
'tbody',
|
||||
'tr',
|
||||
'th',
|
||||
'td',
|
||||
'i',
|
||||
'em',
|
||||
'strong',
|
||||
@@ -717,6 +729,7 @@ export const sanitizeHTML = (text) => {
|
||||
'ol',
|
||||
'li',
|
||||
'img',
|
||||
'blockquote',
|
||||
],
|
||||
ALLOWED_ATTR: ['href', 'target', 'src'],
|
||||
})
|
||||
|
||||
@@ -164,7 +164,8 @@ def is_certified(course):
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_certificate(course: str):
|
||||
if is_certified(course):
|
||||
certificate = is_certified(course)
|
||||
if certificate:
|
||||
return frappe.db.get_value(
|
||||
"LMS Certificate", certificate, ["name", "course", "template"], as_dict=True
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ frappe.ui.form.on("LMS Certificate Request", {
|
||||
frappe.call({
|
||||
method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.setup_calendar_event",
|
||||
args: {
|
||||
eval: frm.doc,
|
||||
eval_name: frm.doc.name,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -161,16 +161,20 @@ def schedule_evals():
|
||||
},
|
||||
["name", "member", "member_name", "evaluator", "date", "start_time", "end_time"],
|
||||
)
|
||||
for eval in evals:
|
||||
setup_calendar_event(eval)
|
||||
for evaluation in evals:
|
||||
setup_calendar_event(evaluation.name)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def setup_calendar_event(eval: str):
|
||||
if isinstance(eval, str):
|
||||
eval = frappe._dict(json.loads(eval))
|
||||
def setup_calendar_event(eval_name: str):
|
||||
evaluation = frappe.db.get_value(
|
||||
"LMS Certificate Request",
|
||||
eval_name,
|
||||
["name", "member", "member_name", "evaluator", "date", "start_time", "end_time"],
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
is_member = eval.member == frappe.session.user
|
||||
is_member = evaluation.member == frappe.session.user
|
||||
roles = frappe.get_roles(frappe.session.user)
|
||||
is_admin = "Moderator" in roles or "Batch Evaluator" in roles
|
||||
|
||||
@@ -180,29 +184,31 @@ def setup_calendar_event(eval: str):
|
||||
frappe.PermissionError,
|
||||
)
|
||||
|
||||
calendar = frappe.db.get_value("Google Calendar", {"user": eval.evaluator, "enable": 1}, "name")
|
||||
calendar = frappe.db.get_value("Google Calendar", {"user": evaluation.evaluator, "enable": 1}, "name")
|
||||
|
||||
if calendar:
|
||||
event = create_event(eval)
|
||||
add_participants(eval, event)
|
||||
update_meeting_details(eval, event, calendar)
|
||||
event = create_event(evaluation)
|
||||
add_participants(evaluation, event)
|
||||
update_meeting_details(evaluation, event, calendar)
|
||||
|
||||
|
||||
def create_event(eval: dict):
|
||||
def create_event(evaluation: dict):
|
||||
event = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Event",
|
||||
"subject": f"Evaluation of {eval.member_name}",
|
||||
"starts_on": f"{eval.date} {eval.start_time}",
|
||||
"ends_on": f"{eval.date} {eval.end_time}",
|
||||
"subject": f"Evaluation of {evaluation.member_name}",
|
||||
"starts_on": f"{evaluation.date} {evaluation.start_time}",
|
||||
"ends_on": f"{evaluation.date} {evaluation.end_time}",
|
||||
"reference_doctype": "LMS Certificate Request",
|
||||
"reference_docname": evaluation.name,
|
||||
}
|
||||
)
|
||||
event.save()
|
||||
return event
|
||||
|
||||
|
||||
def add_participants(eval: dict, event: Document):
|
||||
participants = [eval.member, eval.evaluator]
|
||||
def add_participants(evaluation: dict, event: Document):
|
||||
participants = [evaluation.member, evaluation.evaluator]
|
||||
for participant in participants:
|
||||
contact_name = frappe.db.get_value("Contact", {"email_id": participant}, "name")
|
||||
frappe.get_doc(
|
||||
@@ -218,7 +224,7 @@ def add_participants(eval: dict, event: Document):
|
||||
).save()
|
||||
|
||||
|
||||
def update_meeting_details(eval: dict, event: Document, calendar: str):
|
||||
def update_meeting_details(evaluation: dict, event: Document, calendar: str):
|
||||
event.reload()
|
||||
event.update(
|
||||
{
|
||||
@@ -230,7 +236,9 @@ def update_meeting_details(eval: dict, event: Document, calendar: str):
|
||||
|
||||
event.save()
|
||||
event.reload()
|
||||
frappe.db.set_value("LMS Certificate Request", eval.name, "google_meet_link", event.google_meet_link)
|
||||
frappe.db.set_value(
|
||||
"LMS Certificate Request", evaluation.name, "google_meet_link", event.google_meet_link
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -47,7 +47,9 @@ class LMSCourse(Document):
|
||||
).save(ignore_permissions=True)
|
||||
|
||||
def validate_video_link(self):
|
||||
if self.video_link and "/" in self.video_link:
|
||||
if self.video_link and "watch?v=" in self.video_link:
|
||||
self.video_link = self.video_link.split("watch?v=")[-1]
|
||||
elif self.video_link and "/" in self.video_link:
|
||||
self.video_link = self.video_link.split("/")[-1]
|
||||
|
||||
def validate_status(self):
|
||||
|
||||
116
lms/workspace_sidebar/lms.json
Normal file
116
lms/workspace_sidebar/lms.json
Normal file
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"app": "lms",
|
||||
"creation": "2025-11-24 14:35:18.461657",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace Sidebar",
|
||||
"header_icon": "education",
|
||||
"idx": 0,
|
||||
"items": [
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Home",
|
||||
"link_to": "Learning",
|
||||
"link_type": "Workspace",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Users",
|
||||
"link_to": "User",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Course",
|
||||
"link_to": "LMS Course",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Enrollments",
|
||||
"link_to": "LMS Enrollment",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Batch",
|
||||
"link_to": "LMS Batch",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Batch Enrollment",
|
||||
"link_to": "LMS Batch Enrollment",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Evaluation Request",
|
||||
"link_to": "LMS Certificate Request",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Evaluation",
|
||||
"link_to": "LMS Certificate Evaluation",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"child": 0,
|
||||
"collapsible": 1,
|
||||
"indent": 0,
|
||||
"keep_closed": 0,
|
||||
"label": "Certificate",
|
||||
"link_to": "LMS Certificate",
|
||||
"link_type": "DocType",
|
||||
"show_arrow": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2026-03-02 13:07:37.040316",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS",
|
||||
"owner": "Administrator",
|
||||
"standard": 1,
|
||||
"title": "LMS"
|
||||
}
|
||||
Reference in New Issue
Block a user