From c826e9535e21856bda98d94030abd10ac129da5d Mon Sep 17 00:00:00 2001 From: Raizaaa <89463672+raizasafeel@users.noreply.github.com> Date: Thu, 14 May 2026 10:43:44 +0530 Subject: [PATCH 01/10] fix: system manager permissions are set properly on install --- lms/install.py | 31 +++----- lms/patches.txt | 1 + .../v2_0/restore_system_manager_perms.py | 77 +++++++++++++++++++ 3 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 lms/patches/v2_0/restore_system_manager_perms.py diff --git a/lms/install.py b/lms/install.py index a343fb0e32..05cc8fd98b 100644 --- a/lms/install.py +++ b/lms/install.py @@ -1,4 +1,5 @@ import frappe +from frappe.permissions import add_permission, update_permission_property from lms.lms.api import give_discussions_permission @@ -200,26 +201,16 @@ def give_event_permission(): def create_role(doctype, role, permlevel, write=0, create=0): - if not frappe.db.exists("Custom DocPerm", {"parent": doctype, "role": role, "permlevel": permlevel}): - if not write and not create: - if role in ["Moderator", "System Manager"]: - write = 1 - if role == "Moderator": - create = 1 - doc = frappe.new_doc("Custom DocPerm") - doc.update( - { - "doctype": "Custom DocPerm", - "parent": doctype, - "role": role, - "read": 1, - "select": 1, - "write": write, - "create": create, - "permlevel": permlevel, - } - ) - doc.save() + if frappe.db.exists("Custom DocPerm", {"parent": doctype, "role": role, "permlevel": permlevel}): + return + + add_permission(doctype, role, permlevel) + update_permission_property(doctype, role, permlevel, "select", 1) + + if role in ["Moderator", "System Manager"] or write == 1: + update_permission_property(doctype, role, permlevel, "write", 1) + if role == "Moderator" or create == 1: + update_permission_property(doctype, role, permlevel, "create", 1) def delete_lms_roles(): diff --git a/lms/patches.txt b/lms/patches.txt index 8056c04fd9..a7ad3dde84 100644 --- a/lms/patches.txt +++ b/lms/patches.txt @@ -124,3 +124,4 @@ lms.patches.v2_0.set_conferencing_provider_for_zoom lms.patches.v2_0.sync_evaluator_roles lms.patches.v2_0.fix_livecode_url_default lms.patches.v2_0.give_event_permission #10-03-2026 +lms.patches.v2_0.restore_system_manager_perms #13-05-2026 diff --git a/lms/patches/v2_0/restore_system_manager_perms.py b/lms/patches/v2_0/restore_system_manager_perms.py new file mode 100644 index 0000000000..c6c901854b --- /dev/null +++ b/lms/patches/v2_0/restore_system_manager_perms.py @@ -0,0 +1,77 @@ +import frappe + + +def execute(): + """ + Restore system manager perms on doctypes + """ + doctypes = ["User", "Event", "Discussion Topic", "Discussion Reply"] + + perm_fields = ( + "read", + "write", + "create", + "delete", + "submit", + "cancel", + "amend", + "report", + "export", + "import", + "print", + "email", + "share", + "select", + ) + + for doctype in doctypes: + if not frappe.db.exists("Custom DocPerm", {"parent": doctype}): + continue + + standard_perms = frappe.get_all( + "DocPerm", + fields=["permlevel", "if_owner", *perm_fields], + filters={"parent": doctype, "role": "System Manager"}, + ) + + for standard_perm in standard_perms: + permlevel = standard_perm.permlevel or 0 + if_owner = standard_perm.if_owner or 0 + + custom_perm_name = frappe.db.exists( + "Custom DocPerm", + { + "parent": doctype, + "role": "System Manager", + "permlevel": permlevel, + "if_owner": if_owner, + }, + ) + + if custom_perm_name: + custom_perm = frappe.get_doc("Custom DocPerm", custom_perm_name) + else: + custom_perm = frappe.new_doc("Custom DocPerm") + custom_perm.update( + { + "parent": doctype, + "parenttype": "DocType", + "parentfield": "permissions", + "role": "System Manager", + "permlevel": permlevel, + "if_owner": if_owner, + } + ) + + is_dirty = False + for field in perm_fields: + if standard_perm.get(field) and not custom_perm.get(field): + custom_perm.set(field, 1) + is_dirty = True + + if not custom_perm_name: + custom_perm.insert(ignore_permissions=True) + elif is_dirty: + custom_perm.save(ignore_permissions=True) + + frappe.clear_cache() From e0a898825ebf924f376ff8985c4248fc48139cf5 Mon Sep 17 00:00:00 2001 From: Raizaaa <89463672+raizasafeel@users.noreply.github.com> Date: Fri, 15 May 2026 11:24:49 +0530 Subject: [PATCH 02/10] fix: unpublished certificates are not visible to website users --- lms/lms/doctype/lms_certificate/lms_certificate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/lms/doctype/lms_certificate/lms_certificate.py b/lms/lms/doctype/lms_certificate/lms_certificate.py index a859dafc40..80637efd72 100644 --- a/lms/lms/doctype/lms_certificate/lms_certificate.py +++ b/lms/lms/doctype/lms_certificate/lms_certificate.py @@ -148,7 +148,7 @@ def on_update(self): def has_website_permission(doc, ptype, user, verbose=False): - if ptype in ["read", "print"]: + if ptype in ["read", "print"] and doc.published: return True if doc.member == user and ptype == "create": return True From 9e4f1d831f62363c18e59df7626cf0e7493312f7 Mon Sep 17 00:00:00 2001 From: Raizaaa <89463672+raizasafeel@users.noreply.github.com> Date: Fri, 15 May 2026 12:50:46 +0530 Subject: [PATCH 03/10] fix: batch errors are now rendered --- frontend/src/pages/Batches/components/NewBatchModal.vue | 5 +++-- lms/lms/doctype/lms_batch/lms_batch.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/Batches/components/NewBatchModal.vue b/frontend/src/pages/Batches/components/NewBatchModal.vue index 3b00fe5a1f..0d5f6e22e2 100644 --- a/frontend/src/pages/Batches/components/NewBatchModal.vue +++ b/frontend/src/pages/Batches/components/NewBatchModal.vue @@ -120,7 +120,7 @@ import { Button, Dialog, FormControl, TextEditor, toast } from 'frappe-ui' import { useOnboarding, useTelemetry } from 'frappe-ui/frappe' import { computed, inject, onMounted, onBeforeUnmount, ref } from 'vue' import { useRouter } from 'vue-router' -import { sanitizeHTML, createLMSCategory } from '@/utils' +import { sanitizeHTML, createLMSCategory, cleanError } from '@/utils' import MultiSelect from '@/components/Controls/MultiSelect.vue' import Link from '@/components/Controls/Link.vue' import NewMemberModal from '@/components/Modals/NewMemberModal.vue' @@ -214,7 +214,8 @@ const saveBatch = (close: () => void = () => {}) => { } }, onError(err: any) { - toast.error(cleanError(err.messages?.[0])) + const message = err?.messages?.[0] + toast.error(message ? cleanError(message) : __('Error creating batch')) console.error(err) }, } diff --git a/lms/lms/doctype/lms_batch/lms_batch.py b/lms/lms/doctype/lms_batch/lms_batch.py index 5738fdf102..1ffaec6e33 100644 --- a/lms/lms/doctype/lms_batch/lms_batch.py +++ b/lms/lms/doctype/lms_batch/lms_batch.py @@ -27,6 +27,7 @@ class LMSBatch(Document): def validate(self): + self._validate_mandatory() self.validate_seats_left() self.validate_batch_end_date() self.validate_batch_time() @@ -43,7 +44,7 @@ def on_update(self): frappe.enqueue(send_notification_for_published_batch, batch=self) def autoname(self): - if not self.name: + if not self.name and self.title: self.name = generate_slug(self.title, "LMS Batch") def validate_batch_end_date(self): From 5699b694d86d2e0f81251b5f49a83d24e1e9aacb Mon Sep 17 00:00:00 2001 From: Raizaaa <89463672+raizasafeel@users.noreply.github.com> Date: Fri, 15 May 2026 15:12:22 +0530 Subject: [PATCH 04/10] fix: wrong progress doesn't block certification --- frontend/src/components/CourseCardOverlay.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/CourseCardOverlay.vue b/frontend/src/components/CourseCardOverlay.vue index 309d1783a8..1e2941d3f4 100644 --- a/frontend/src/components/CourseCardOverlay.vue +++ b/frontend/src/components/CourseCardOverlay.vue @@ -230,7 +230,7 @@ const is_instructor = () => { const canGetCertificate = computed(() => { if ( props.course.data?.enable_certification && - props.course.data?.membership?.progress == 100 + props.course.data?.membership?.progress >= 100 ) { return true } From 1cc3739ba6da10615e154c49fe3631f99baaf620 Mon Sep 17 00:00:00 2001 From: frappe-pr-bot Date: Fri, 15 May 2026 16:35:13 +0000 Subject: [PATCH 05/10] chore: update POT file --- lms/locale/main.pot | 465 +++++++++++++++++++++++++------------------- 1 file changed, 261 insertions(+), 204 deletions(-) diff --git a/lms/locale/main.pot b/lms/locale/main.pot index d47a1e79a0..eaa9a3ed55 100644 --- a/lms/locale/main.pot +++ b/lms/locale/main.pot @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Learning VERSION\n" "Report-Msgid-Bugs-To: jannat@frappe.io\n" -"POT-Creation-Date: 2026-05-08 16:29+0000\n" -"PO-Revision-Date: 2026-05-08 16:29+0000\n" +"POT-Creation-Date: 2026-05-15 16:35+0000\n" +"PO-Revision-Date: 2026-05-15 16:35+0000\n" "Last-Translator: jannat@frappe.io\n" "Language-Team: jannat@frappe.io\n" "MIME-Version: 1.0\n" @@ -338,15 +338,15 @@ msgstr "" msgid "Administrator" msgstr "" -#: frontend/src/pages/Batches/Batches.vue:314 +#: frontend/src/pages/Batches/Batches.vue:315 msgid "All" msgstr "" -#: frontend/src/pages/Batches/Batches.vue:51 +#: frontend/src/pages/Batches/Batches.vue:53 msgid "All Batches" msgstr "" -#: frontend/src/pages/Courses/Courses.vue:36 lms/lms/widgets/BreadCrumb.html:3 +#: frontend/src/pages/Courses/Courses.vue:37 lms/lms/widgets/BreadCrumb.html:3 msgid "All Courses" msgstr "" @@ -486,12 +486,12 @@ msgstr "" msgid "Application Form Link" msgstr "" -#: frontend/src/pages/JobApplications.vue:14 +#: frontend/src/pages/JobApplications.vue:13 #: frontend/src/pages/JobApplications.vue:25 msgid "Applications" msgstr "" -#: frontend/src/pages/JobApplications.vue:340 +#: frontend/src/pages/JobApplications.vue:341 msgid "Applied On" msgstr "" @@ -522,7 +522,7 @@ msgstr "" msgid "Apps" msgstr "" -#: frontend/src/pages/Batches/Batches.vue:325 +#: frontend/src/pages/Batches/Batches.vue:326 msgid "Archived" msgstr "" @@ -642,7 +642,7 @@ msgstr "" msgid "Assignment Submission" msgstr "" -#: frontend/src/pages/AssignmentSubmissionList.vue:222 +#: frontend/src/pages/AssignmentSubmissionList.vue:223 msgid "Assignment Submissions" msgstr "" @@ -675,12 +675,12 @@ msgstr "" #: frontend/src/components/Settings/Badges.vue:170 #: frontend/src/components/Sidebar/AppSidebar.vue:619 -#: frontend/src/pages/Assignments.vue:284 -#: frontend/src/pages/Assignments.vue:291 lms/www/_lms.py:280 +#: frontend/src/pages/Assignments.vue:307 +#: frontend/src/pages/Assignments.vue:314 lms/www/_lms.py:280 msgid "Assignments" msgstr "" -#: frontend/src/pages/Assignments.vue:279 +#: frontend/src/pages/Assignments.vue:302 msgid "Assignments deleted successfully" msgstr "" @@ -972,8 +972,8 @@ msgstr "" #. Label of the batches (Check) field in DocType 'LMS Settings' #. Label of a Workspace Sidebar Item #: frontend/src/pages/Batches/BatchDetail.vue:235 -#: frontend/src/pages/Batches/Batches.vue:346 -#: frontend/src/pages/Batches/Batches.vue:353 +#: frontend/src/pages/Batches/Batches.vue:347 +#: frontend/src/pages/Batches/Batches.vue:354 #: lms/lms/doctype/lms_settings/lms_settings.json #: lms/workspace_sidebar/learning.json lms/www/_lms.py:129 msgid "Batches" @@ -1041,6 +1041,10 @@ msgstr "" msgid "Business Owner" msgstr "" +#: frontend/src/pages/PersonaForm.vue:106 +msgid "Business Owner / Team Lead" +msgstr "" + #: frontend/src/components/CourseCardOverlay.vue:54 msgid "Buy this course" msgstr "" @@ -1065,7 +1069,7 @@ msgstr "" msgid "Cancelled" msgstr "" -#: lms/lms/api.py:2359 +#: lms/lms/api.py:2378 msgid "Cannot search for roles: {0}" msgstr "" @@ -1084,9 +1088,9 @@ msgstr "" #. Label of the category (Data) field in DocType 'LMS Category' #. Label of the category (Link) field in DocType 'LMS Course' #: frontend/src/pages/Batches/BatchForm.vue:81 -#: frontend/src/pages/Batches/Batches.vue:75 +#: frontend/src/pages/Batches/Batches.vue:76 #: frontend/src/pages/Batches/components/NewBatchModal.vue:51 -#: frontend/src/pages/CertifiedParticipants.vue:38 +#: frontend/src/pages/CertifiedParticipants.vue:37 #: frontend/src/pages/Courses/CourseForm.vue:23 #: frontend/src/pages/Courses/Courses.vue:56 #: frontend/src/pages/Courses/NewCourseModal.vue:21 @@ -1164,10 +1168,10 @@ msgstr "" #: frontend/src/components/Sidebar/AppSidebar.vue:623 #: frontend/src/pages/Batches/BatchForm.vue:91 #: frontend/src/pages/Batches/BatchForm.vue:113 -#: frontend/src/pages/Batches/Batches.vue:85 +#: frontend/src/pages/Batches/Batches.vue:84 #: frontend/src/pages/Courses/CourseCertification.vue:10 #: frontend/src/pages/Courses/CourseCertification.vue:135 -#: frontend/src/pages/Courses/Courses.vue:66 lms/fixtures/custom_field.json +#: frontend/src/pages/Courses/Courses.vue:65 lms/fixtures/custom_field.json #: lms/lms/doctype/certification/certification.json #: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_enrollment/lms_enrollment.json @@ -1211,9 +1215,9 @@ msgid "Certified" msgstr "" #. Label of the certified_members (Check) field in DocType 'LMS Settings' -#: frontend/src/pages/CertifiedParticipants.vue:18 -#: frontend/src/pages/CertifiedParticipants.vue:257 -#: frontend/src/pages/CertifiedParticipants.vue:264 +#: frontend/src/pages/CertifiedParticipants.vue:20 +#: frontend/src/pages/CertifiedParticipants.vue:279 +#: frontend/src/pages/CertifiedParticipants.vue:286 #: frontend/src/pages/Statistics.vue:40 #: lms/lms/doctype/lms_settings/lms_settings.json msgid "Certified Members" @@ -1251,11 +1255,11 @@ msgstr "" msgid "Chapter added successfully" msgstr "" -#: frontend/src/components/CourseOutline.vue:368 +#: frontend/src/components/CourseOutline.vue:376 msgid "Chapter deleted successfully" msgstr "" -#: frontend/src/components/CourseOutline.vue:302 +#: frontend/src/components/CourseOutline.vue:309 msgid "Chapter moved successfully" msgstr "" @@ -1325,7 +1329,7 @@ msgstr "" msgid "Choices" msgstr "" -#: frontend/src/components/Quiz.vue:880 +#: frontend/src/components/Quiz.vue:883 msgid "Choose all answers that apply" msgstr "" @@ -1337,7 +1341,7 @@ msgstr "" msgid "Choose an icon" msgstr "" -#: frontend/src/components/Quiz.vue:881 +#: frontend/src/components/Quiz.vue:884 msgid "Choose one answer" msgstr "" @@ -1389,7 +1393,7 @@ msgstr "" #. Option for the 'Status' (Select) field in DocType 'Job Opportunity' #. Option in a Select field in the job-opportunity Web Form -#: frontend/src/pages/JobForm.vue:310 frontend/src/pages/Jobs.vue:293 +#: frontend/src/pages/JobForm.vue:310 frontend/src/pages/Jobs.vue:314 #: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/job/web_form/job_opportunity/job_opportunity.json msgid "Closed" @@ -1458,10 +1462,14 @@ msgstr "" msgid "Common keywords that will be used for all pages" msgstr "" -#: frontend/src/pages/Lesson.vue:932 +#: frontend/src/pages/Lesson.vue:944 msgid "Community" msgstr "" +#: frontend/src/pages/PersonaForm.vue:104 +msgid "Community Manager" +msgstr "" + #. Label of the company (Data) field in DocType 'LMS Job Application' #. Label of the company (Data) field in DocType 'Work Experience' #: lms/job/doctype/lms_job_application/lms_job_application.json @@ -1608,7 +1616,7 @@ msgstr "" msgid "Confirm Enrollment" msgstr "" -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:231 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:247 msgid "Confirm Your Action" msgstr "" @@ -1675,7 +1683,7 @@ msgstr "" #. Option for the 'Type' (Select) field in DocType 'Job Opportunity' #. Option in a Select field in the job-opportunity Web Form -#: frontend/src/pages/JobForm.vue:294 frontend/src/pages/Jobs.vue:305 +#: frontend/src/pages/JobForm.vue:294 frontend/src/pages/Jobs.vue:326 #: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/job/web_form/job_opportunity/job_opportunity.json msgid "Contract" @@ -1702,7 +1710,7 @@ msgstr "" #. Label of the country (Link) field in DocType 'Job Opportunity' #. Label of the country (Link) field in DocType 'Payment Country' #: frontend/src/pages/Billing.vue:145 frontend/src/pages/JobForm.vue:79 -#: frontend/src/pages/Jobs.vue:75 lms/fixtures/custom_field.json +#: frontend/src/pages/Jobs.vue:73 lms/fixtures/custom_field.json #: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/lms/doctype/payment_country/payment_country.json msgid "Country" @@ -1769,7 +1777,7 @@ msgstr "" #: frontend/src/pages/Batches/components/BatchDashboard.vue:129 #: frontend/src/pages/Batches/components/BulkCertificates.vue:38 #: frontend/src/pages/Programs/ProgramForm.vue:196 -#: frontend/src/pages/Programs/Programs.vue:35 +#: frontend/src/pages/Programs/Programs.vue:41 #: frontend/src/pages/Search/Search.vue:233 #: lms/lms/doctype/batch_course/batch_course.json #: lms/lms/doctype/course_chapter/course_chapter.json @@ -1980,10 +1988,10 @@ msgstr "" #: frontend/src/pages/Courses/CourseCertification.vue:127 #: frontend/src/pages/Courses/CourseDetail.vue:224 #: frontend/src/pages/Courses/Courses.vue:374 -#: frontend/src/pages/Courses/Courses.vue:381 frontend/src/pages/Lesson.vue:572 +#: frontend/src/pages/Courses/Courses.vue:381 frontend/src/pages/Lesson.vue:574 #: frontend/src/pages/LessonForm.vue:480 #: frontend/src/pages/Programs/ProgramForm.vue:49 -#: frontend/src/pages/Programs/Programs.vue:35 +#: frontend/src/pages/Programs/Programs.vue:41 #: frontend/src/pages/SCORMChapter.vue:234 #: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_settings/lms_settings.json @@ -2020,11 +2028,12 @@ msgstr "" #: frontend/src/components/Controls/Link.vue:52 #: frontend/src/components/Controls/Link.vue:54 #: frontend/src/components/Modals/ChapterModal.vue:9 -#: frontend/src/pages/Assignments.vue:19 -#: frontend/src/pages/Batches/Batches.vue:33 +#: frontend/src/pages/Assignments.vue:20 +#: frontend/src/pages/Batches/Batches.vue:34 #: frontend/src/pages/Courses/Courses.vue:18 frontend/src/pages/Jobs.vue:25 #: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:34 -#: frontend/src/pages/Quizzes.vue:10 +#: frontend/src/pages/Programs/Programs.vue:15 +#: frontend/src/pages/Quizzes.vue:11 msgid "Create" msgstr "" @@ -2062,7 +2071,7 @@ msgstr "" msgid "Create a Live Class" msgstr "" -#: frontend/src/pages/Quizzes.vue:104 +#: frontend/src/pages/Quizzes.vue:120 msgid "Create a Quiz" msgstr "" @@ -2235,8 +2244,8 @@ msgid "Degree Type" msgstr "" #: frontend/src/components/Controls/ChildTable.vue:63 -#: frontend/src/components/CourseOutline.vue:314 -#: frontend/src/components/CourseOutline.vue:380 +#: frontend/src/components/CourseOutline.vue:321 +#: frontend/src/components/CourseOutline.vue:388 #: frontend/src/components/DiscussionReplies.vue:41 #: frontend/src/components/Settings/Badges.vue:178 #: frontend/src/components/Settings/Coupons/CouponList.vue:137 @@ -2244,7 +2253,7 @@ msgstr "" #: frontend/src/pages/Courses/CourseDetail.vue:213 #: frontend/src/pages/Courses/CourseForm.vue:530 #: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseForm.vue:71 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:237 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:253 #: frontend/src/pages/Programs/ProgramForm.vue:230 #: frontend/src/pages/Programs/ProgramForm.vue:567 msgid "Delete" @@ -2262,7 +2271,7 @@ msgstr "" msgid "Delete Program" msgstr "" -#: frontend/src/components/CourseOutline.vue:374 +#: frontend/src/components/CourseOutline.vue:382 msgid "Delete this chapter?" msgstr "" @@ -2270,7 +2279,7 @@ msgstr "" msgid "Delete this coupon?" msgstr "" -#: frontend/src/components/CourseOutline.vue:308 +#: frontend/src/components/CourseOutline.vue:315 msgid "Delete this lesson?" msgstr "" @@ -2278,7 +2287,7 @@ msgstr "" msgid "Deleting the course will also delete all its chapters and lessons. Are you sure you want to delete this course?" msgstr "" -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:232 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:248 msgid "Deleting these exercises will permanently remove them from the system, along with all associated submissions. This action is irreversible. Are you sure you want to proceed?" msgstr "" @@ -2286,15 +2295,15 @@ msgstr "" msgid "Deleting this batch will also delete all its data including enrolled students, linked courses, assessments, feedback and discussions. Are you sure you want to continue?" msgstr "" -#: frontend/src/components/CourseOutline.vue:375 +#: frontend/src/components/CourseOutline.vue:383 msgid "Deleting this chapter will also delete all its lessons and permanently remove it from the course. This action cannot be undone. Are you sure you want to continue?" msgstr "" -#: frontend/src/components/CourseOutline.vue:309 +#: frontend/src/components/CourseOutline.vue:316 msgid "Deleting this lesson will permanently remove it from the course. This action cannot be undone. Are you sure you want to continue?" msgstr "" -#: lms/lms/api.py:777 +#: lms/lms/api.py:796 msgid "Deletion not allowed for {0}" msgstr "" @@ -2552,7 +2561,7 @@ msgstr "" #. Option for the 'Send Notification for Published Batches' (Select) field in #. DocType 'LMS Settings' #: frontend/src/components/Modals/NewMemberModal.vue:21 -#: frontend/src/pages/JobApplications.vue:334 +#: frontend/src/pages/JobApplications.vue:335 #: lms/lms/doctype/lms_settings/lms_settings.json #: lms/templates/signup-form.html:10 msgid "Email" @@ -2589,7 +2598,7 @@ msgid "Email is required" msgstr "" #: frontend/src/components/ContactUsEmail.vue:57 -#: frontend/src/pages/JobApplications.vue:294 +#: frontend/src/pages/JobApplications.vue:295 msgid "Email sent successfully" msgstr "" @@ -2713,7 +2722,7 @@ msgstr "" msgid "Enroll for Free" msgstr "" -#: frontend/src/pages/Batches/Batches.vue:328 +#: frontend/src/pages/Batches/Batches.vue:329 #: frontend/src/pages/Batches/components/AdminBatchDashboard.vue:5 #: frontend/src/pages/Courses/CourseDashboard.vue:5 #: frontend/src/pages/Courses/Courses.vue:338 @@ -2812,7 +2821,7 @@ msgstr "" msgid "Error creating live class. Please try again. {0}" msgstr "" -#: frontend/src/pages/Quizzes.vue:255 +#: frontend/src/pages/Quizzes.vue:282 msgid "Error creating quiz: {0}" msgstr "" @@ -2962,7 +2971,7 @@ msgstr "" #. Label of the exercise (Link) field in DocType 'LMS Programming Exercise #. Submission' -#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:278 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:279 #: lms/lms/doctype/lms_programming_exercise_submission/lms_programming_exercise_submission.json msgid "Exercise" msgstr "" @@ -2973,7 +2982,7 @@ msgstr "" msgid "Exercise Title" msgstr "" -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:255 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:271 msgid "Exercise deleted successfully" msgstr "" @@ -3200,12 +3209,16 @@ msgstr "" #. Option for the 'Type' (Select) field in DocType 'Job Opportunity' #. Option in a Select field in the job-opportunity Web Form -#: frontend/src/pages/JobForm.vue:295 frontend/src/pages/Jobs.vue:306 +#: frontend/src/pages/JobForm.vue:295 frontend/src/pages/Jobs.vue:327 #: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/job/web_form/job_opportunity/job_opportunity.json msgid "Freelance" msgstr "" +#: frontend/src/pages/PersonaForm.vue:97 +msgid "Freelancer / Consultant" +msgstr "" + #. Option for the 'User Category' (Select) field in DocType 'User' #: lms/fixtures/custom_field.json lms/templates/signup-form.html:27 msgid "Freelancer/Just looking" @@ -3240,7 +3253,7 @@ msgstr "" #. Label of the full_name (Data) field in DocType 'Course Evaluator' #. Label of the full_name (Data) field in DocType 'LMS Program Member' -#: frontend/src/pages/JobApplications.vue:328 +#: frontend/src/pages/JobApplications.vue:329 #: lms/lms/doctype/course_evaluator/course_evaluator.json #: lms/lms/doctype/lms_program_member/lms_program_member.json #: lms/templates/signup-form.html:5 @@ -3249,7 +3262,7 @@ msgstr "" #. Option for the 'Type' (Select) field in DocType 'Job Opportunity' #. Option in a Select field in the job-opportunity Web Form -#: frontend/src/pages/JobForm.vue:292 frontend/src/pages/Jobs.vue:303 +#: frontend/src/pages/JobForm.vue:292 frontend/src/pages/Jobs.vue:324 #: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/job/web_form/job_opportunity/job_opportunity.json msgid "Full Time" @@ -3302,7 +3315,7 @@ msgstr "" #: frontend/src/components/CertificationLinks.vue:34 #: frontend/src/components/CertificationLinks.vue:50 #: frontend/src/components/CourseCard.vue:126 -#: frontend/src/pages/CertifiedParticipants.vue:11 +#: frontend/src/pages/CertifiedParticipants.vue:12 msgid "Get Certified" msgstr "" @@ -3417,6 +3430,10 @@ msgstr "" msgid "Green" msgstr "" +#: frontend/src/pages/PersonaForm.vue:98 +msgid "HR / L&D Professional" +msgstr "" + #: lms/templates/signup-form.html:56 msgid "Have an account? Login" msgstr "" @@ -3507,7 +3524,7 @@ msgstr "" #. Option for the 'Open to' (Select) field in DocType 'User' #: frontend/src/components/UserAvatar.vue:18 -#: frontend/src/pages/CertifiedParticipants.vue:52 +#: frontend/src/pages/CertifiedParticipants.vue:49 #: frontend/src/pages/Profile.vue:70 lms/fixtures/custom_field.json msgid "Hiring" msgstr "" @@ -3522,7 +3539,7 @@ msgid "Host" msgstr "" #. Option for the 'Work Mode' (Select) field in DocType 'Job Opportunity' -#: frontend/src/pages/JobForm.vue:302 frontend/src/pages/Jobs.vue:314 +#: frontend/src/pages/JobForm.vue:302 frontend/src/pages/Jobs.vue:335 #: lms/job/doctype/job_opportunity/job_opportunity.json msgid "Hybrid" msgstr "" @@ -3616,7 +3633,7 @@ msgstr "" msgid "Image search powered by" msgstr "" -#: lms/lms/doctype/lms_quiz/lms_quiz.py:243 +#: lms/lms/doctype/lms_quiz/lms_quiz.py:246 msgid "Image: Corrupted Data Stream" msgstr "" @@ -3625,7 +3642,7 @@ msgstr "" msgid "Import" msgstr "" -#: frontend/src/pages/Batches/Batches.vue:17 +#: frontend/src/pages/Batches/Batches.vue:18 msgid "Import Batch" msgstr "" @@ -3775,15 +3792,15 @@ msgstr "" msgid "Invalid course ZIP: Missing course.json" msgstr "" -#: lms/lms/api.py:1031 +#: lms/lms/api.py:1050 msgid "Invalid course or chapter name" msgstr "" -#: lms/lms/api.py:780 +#: lms/lms/api.py:799 msgid "Invalid document name" msgstr "" -#: lms/lms/api.py:1038 +#: lms/lms/api.py:1057 msgid "Invalid file path in package" msgstr "" @@ -3898,9 +3915,9 @@ msgstr "" #. Label of the jobs (Check) field in DocType 'LMS Settings' #. Label of the jobs_tab (Tab Break) field in DocType 'LMS Settings' -#: frontend/src/pages/JobApplications.vue:9 frontend/src/pages/JobDetail.vue:10 -#: frontend/src/pages/JobForm.vue:317 frontend/src/pages/Jobs.vue:8 -#: frontend/src/pages/Jobs.vue:321 +#: frontend/src/pages/JobApplications.vue:8 frontend/src/pages/JobDetail.vue:10 +#: frontend/src/pages/JobForm.vue:317 frontend/src/pages/Jobs.vue:6 +#: frontend/src/pages/Jobs.vue:342 #: lms/lms/doctype/lms_settings/lms_settings.json msgid "Jobs" msgstr "" @@ -4229,7 +4246,7 @@ msgstr "" #. Label of the language (Select) field in DocType 'LMS Programming Exercise' #: frontend/src/components/Modals/EditProfile.vue:69 #: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseForm.vue:27 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:295 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:319 #: lms/lms/doctype/lms_programming_exercise/lms_programming_exercise.json msgid "Language" msgstr "" @@ -4314,11 +4331,11 @@ msgstr "" msgid "Lesson created successfully" msgstr "" -#: frontend/src/components/CourseOutline.vue:273 +#: frontend/src/components/CourseOutline.vue:280 msgid "Lesson deleted successfully" msgstr "" -#: frontend/src/components/CourseOutline.vue:288 +#: frontend/src/components/CourseOutline.vue:295 msgid "Lesson moved successfully" msgstr "" @@ -4382,6 +4399,10 @@ msgstr "" msgid "Live Class" msgstr "" +#: lms/lms/doctype/lms_quiz/lms_quiz.py:287 +msgid "Live answer checking is not enabled for this quiz." +msgstr "" + #. Label of the livecode_url (Data) field in DocType 'LMS Settings' #: lms/lms/doctype/lms_settings/lms_settings.json msgid "LiveCode URL" @@ -4390,17 +4411,17 @@ msgstr "" #: frontend/src/components/Settings/Evaluators.vue:110 #: frontend/src/components/Settings/Members.vue:79 #: frontend/src/components/Settings/Transactions/TransactionList.vue:102 -#: frontend/src/pages/Assignments.vue:114 -#: frontend/src/pages/Batches/Batches.vue:109 +#: frontend/src/pages/Assignments.vue:122 +#: frontend/src/pages/Batches/Batches.vue:108 #: frontend/src/pages/Batches/components/AdminBatchDashboard.vue:119 -#: frontend/src/pages/CertifiedParticipants.vue:115 +#: frontend/src/pages/CertifiedParticipants.vue:122 #: frontend/src/pages/Courses/CourseDashboard.vue:119 -#: frontend/src/pages/Courses/Courses.vue:89 -#: frontend/src/pages/JobApplications.vue:105 frontend/src/pages/Jobs.vue:121 +#: frontend/src/pages/Courses/Courses.vue:88 +#: frontend/src/pages/JobApplications.vue:105 frontend/src/pages/Jobs.vue:128 #: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:133 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:126 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:134 #: frontend/src/pages/QuizSubmissionList.vue:39 -#: frontend/src/pages/Quizzes.vue:93 +#: frontend/src/pages/Quizzes.vue:104 msgid "Load More" msgstr "" @@ -4502,7 +4523,7 @@ msgstr "" msgid "Mark" msgstr "" -#: frontend/src/pages/Notifications.vue:14 +#: frontend/src/pages/Notifications.vue:12 msgid "Mark all as read" msgstr "" @@ -4549,7 +4570,7 @@ msgid "Marks to Deduct" msgstr "" #. Label of the max_attempts (Int) field in DocType 'LMS Quiz' -#: frontend/src/pages/Quizzes.vue:292 lms/lms/doctype/lms_quiz/lms_quiz.json +#: frontend/src/pages/Quizzes.vue:319 lms/lms/doctype/lms_quiz/lms_quiz.json msgid "Max Attempts" msgstr "" @@ -4610,10 +4631,10 @@ msgstr "" #: frontend/src/components/Settings/Transactions/TransactionDetails.vue:55 #: frontend/src/components/Settings/ZoomSettings.vue:182 #: frontend/src/pages/AssignmentSubmissionList.vue:14 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:272 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:273 #: frontend/src/pages/Programs/ProgramProgressSummary.vue:126 #: frontend/src/pages/QuizSubmission.vue:31 -#: frontend/src/pages/QuizSubmissionList.vue:91 +#: frontend/src/pages/QuizSubmissionList.vue:92 #: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json #: lms/lms/doctype/lms_badge_assignment/lms_badge_assignment.json #: lms/lms/doctype/lms_batch_enrollment/lms_batch_enrollment.json @@ -4801,7 +4822,7 @@ msgstr "" msgid "Message" msgstr "" -#: frontend/src/pages/JobApplications.vue:290 +#: frontend/src/pages/JobApplications.vue:291 msgid "Message is required" msgstr "" @@ -4832,7 +4853,7 @@ msgstr "" msgid "Meta Tags" msgstr "" -#: lms/lms/api.py:1573 +#: lms/lms/api.py:1592 msgid "Meta tags should be a list." msgstr "" @@ -4884,7 +4905,7 @@ msgstr "" msgid "Moderator" msgstr "" -#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:290 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:291 msgid "Modified" msgstr "" @@ -4962,14 +4983,13 @@ msgstr "" #: frontend/src/components/Settings/PaymentGateways.vue:16 #: frontend/src/components/Settings/ZoomSettings.vue:17 #: frontend/src/pages/Courses/Courses.vue:322 -#: frontend/src/pages/Programs/Programs.vue:10 #: lms/lms/doctype/lms_badge/lms_badge.json msgid "New" msgstr "" -#: frontend/src/pages/Batches/Batches.vue:10 +#: frontend/src/pages/Batches/Batches.vue:11 #: frontend/src/pages/Batches/components/NewBatchModal.vue:5 -#: frontend/src/pages/Notifications.vue:94 lms/www/_lms.py:158 +#: frontend/src/pages/Notifications.vue:92 lms/www/_lms.py:158 msgid "New Batch" msgstr "" @@ -4979,7 +4999,7 @@ msgstr "" #: frontend/src/pages/Courses/Courses.vue:346 #: frontend/src/pages/Courses/NewCourseModal.vue:5 -#: frontend/src/pages/Notifications.vue:93 lms/www/_lms.py:102 +#: frontend/src/pages/Notifications.vue:91 lms/www/_lms.py:102 msgid "New Course" msgstr "" @@ -5023,11 +5043,11 @@ msgstr "" msgid "New Zoom Account" msgstr "" -#: lms/lms/utils.py:472 +#: lms/lms/utils.py:459 msgid "New comment in batch {0}" msgstr "" -#: lms/lms/utils.py:463 +#: lms/lms/utils.py:450 msgid "New reply on the topic {0} in course {1}" msgstr "" @@ -5140,7 +5160,7 @@ msgstr "" msgid "No quizzes added yet." msgstr "" -#: frontend/src/pages/Notifications.vue:152 +#: frontend/src/pages/Notifications.vue:150 msgid "No read notifications" msgstr "" @@ -5166,7 +5186,7 @@ msgstr "" msgid "No submissions" msgstr "" -#: frontend/src/pages/Notifications.vue:151 +#: frontend/src/pages/Notifications.vue:149 msgid "No unread notifications" msgstr "" @@ -5225,7 +5245,7 @@ msgstr "" msgid "Note" msgstr "" -#: frontend/src/pages/Lesson.vue:921 +#: frontend/src/pages/Lesson.vue:933 msgid "Notes" msgstr "" @@ -5243,7 +5263,7 @@ msgstr "" msgid "Notifications" msgstr "" -#: frontend/src/pages/Notifications.vue:159 +#: frontend/src/pages/Notifications.vue:157 msgid "Notifications you have read will appear here." msgstr "" @@ -5281,11 +5301,15 @@ msgid "On site" msgstr "" #. Option for the 'Work Mode' (Select) field in DocType 'Job Opportunity' -#: frontend/src/pages/Jobs.vue:313 +#: frontend/src/pages/Jobs.vue:334 #: lms/job/doctype/job_opportunity/job_opportunity.json msgid "On-site" msgstr "" +#: frontend/src/pages/PersonaForm.vue:122 +msgid "Onboarding and educating my users/community" +msgstr "" + #: lms/templates/emails/certification.html:16 msgid "Once again, congratulations on this significant accomplishment." msgstr "" @@ -5325,7 +5349,7 @@ msgstr "" msgid "Only show batches that offer a certificate" msgstr "" -#: frontend/src/pages/Courses/Courses.vue:62 +#: frontend/src/pages/Courses/Courses.vue:61 msgid "Only show courses that offer a certificate" msgstr "" @@ -5339,7 +5363,7 @@ msgstr "" #. Option for the 'Status' (Select) field in DocType 'Job Opportunity' #. Option in a Select field in the job-opportunity Web Form -#: frontend/src/pages/JobForm.vue:309 frontend/src/pages/Jobs.vue:287 +#: frontend/src/pages/JobForm.vue:309 frontend/src/pages/Jobs.vue:308 #: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/job/web_form/job_opportunity/job_opportunity.json msgid "Open" @@ -5371,7 +5395,7 @@ msgid "Open to" msgstr "" #: frontend/src/components/UserAvatar.vue:11 -#: frontend/src/pages/CertifiedParticipants.vue:46 +#: frontend/src/pages/CertifiedParticipants.vue:44 #: frontend/src/pages/Profile.vue:69 msgid "Open to Work" msgstr "" @@ -5429,6 +5453,11 @@ msgstr "" msgid "Original Amount" msgstr "" +#: frontend/src/pages/PersonaForm.vue:109 +#: frontend/src/pages/PersonaForm.vue:129 +msgid "Other" +msgstr "" + #. Option for the 'User Category' (Select) field in DocType 'User' #: lms/fixtures/custom_field.json lms/templates/signup-form.html:28 msgid "Others" @@ -5507,7 +5536,7 @@ msgstr "" #. Option for the 'Type' (Select) field in DocType 'Job Opportunity' #. Option in a Select field in the job-opportunity Web Form -#: frontend/src/pages/JobForm.vue:293 frontend/src/pages/Jobs.vue:304 +#: frontend/src/pages/JobForm.vue:293 frontend/src/pages/Jobs.vue:325 #: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/job/web_form/job_opportunity/job_opportunity.json msgid "Part Time" @@ -5539,7 +5568,7 @@ msgstr "" #. Label of the passing_percentage (Int) field in DocType 'LMS Quiz' #. Label of the passing_percentage (Int) field in DocType 'LMS Quiz Submission' -#: frontend/src/pages/QuizForm.vue:78 frontend/src/pages/Quizzes.vue:285 +#: frontend/src/pages/QuizForm.vue:78 frontend/src/pages/Quizzes.vue:312 #: lms/lms/doctype/lms_quiz/lms_quiz.json #: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json msgid "Passing Percentage" @@ -5672,7 +5701,7 @@ msgstr "" #. Label of the percentage (Int) field in DocType 'LMS Quiz Submission' #: frontend/src/pages/Courses/StudentCourseProgress.vue:88 #: frontend/src/pages/QuizSubmission.vue:44 -#: frontend/src/pages/QuizSubmissionList.vue:102 +#: frontend/src/pages/QuizSubmissionList.vue:103 #: lms/lms/doctype/lms_coupon/lms_coupon.json #: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json msgid "Percentage" @@ -5730,7 +5759,7 @@ msgstr "" msgid "Please enable the zoom account to use this feature." msgstr "" -#: frontend/src/components/CourseOutline.vue:397 +#: frontend/src/components/CourseOutline.vue:405 msgid "Please enroll for this course to view this lesson" msgstr "" @@ -5794,11 +5823,11 @@ msgstr "" msgid "Please login to continue with payment." msgstr "" -#: lms/lms/utils.py:2086 +#: lms/lms/utils.py:2200 msgid "Please login to view program details." msgstr "" -#: lms/lms/utils.py:2051 +#: lms/lms/utils.py:2165 msgid "Please login to view programs." msgstr "" @@ -6050,7 +6079,7 @@ msgstr "" msgid "Profile Image" msgstr "" -#: frontend/src/pages/Programs/Programs.vue:18 +#: frontend/src/pages/Programs/Programs.vue:24 msgid "Program" msgstr "" @@ -6095,7 +6124,7 @@ msgid "Programming Exercise Submission" msgstr "" #: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmission.vue:427 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:302 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:303 msgid "Programming Exercise Submissions" msgstr "" @@ -6112,17 +6141,17 @@ msgid "Programming Exercise updated successfully" msgstr "" #. Label of the programming_exercises (Check) field in DocType 'LMS Settings' -#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:312 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:313 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:321 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:313 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:337 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:345 #: lms/lms/doctype/lms_settings/lms_settings.json msgid "Programming Exercises" msgstr "" #: frontend/src/pages/Programs/ProgramDetail.vue:123 -#: frontend/src/pages/Programs/Programs.vue:18 -#: frontend/src/pages/Programs/Programs.vue:113 -#: frontend/src/pages/Programs/Programs.vue:119 lms/www/_lms.py:302 +#: frontend/src/pages/Programs/Programs.vue:24 +#: frontend/src/pages/Programs/Programs.vue:120 +#: frontend/src/pages/Programs/Programs.vue:126 lms/www/_lms.py:302 msgid "Programs" msgstr "" @@ -6245,6 +6274,10 @@ msgstr "" msgid "Question added successfully" msgstr "" +#: lms/lms/doctype/lms_quiz/lms_quiz.py:283 +msgid "Question not found in this quiz." +msgstr "" + #: frontend/src/components/Modals/Question.vue:333 msgid "Question updated successfully" msgstr "" @@ -6294,8 +6327,8 @@ msgid "Quiz Submission" msgstr "" #: frontend/src/pages/QuizSubmission.vue:131 -#: frontend/src/pages/QuizSubmissionList.vue:111 -#: frontend/src/pages/QuizSubmissionList.vue:116 +#: frontend/src/pages/QuizSubmissionList.vue:112 +#: frontend/src/pages/QuizSubmissionList.vue:117 msgid "Quiz Submissions" msgstr "" @@ -6308,7 +6341,7 @@ msgstr "" msgid "Quiz Title" msgstr "" -#: frontend/src/pages/Quizzes.vue:243 +#: frontend/src/pages/Quizzes.vue:270 msgid "Quiz created successfully" msgstr "" @@ -6326,12 +6359,12 @@ msgid "Quiz will appear at the bottom of the lesson." msgstr "" #: frontend/src/components/Sidebar/AppSidebar.vue:618 -#: frontend/src/pages/QuizForm.vue:388 frontend/src/pages/Quizzes.vue:318 -#: frontend/src/pages/Quizzes.vue:328 lms/www/_lms.py:258 +#: frontend/src/pages/QuizForm.vue:388 frontend/src/pages/Quizzes.vue:345 +#: frontend/src/pages/Quizzes.vue:355 lms/www/_lms.py:258 msgid "Quizzes" msgstr "" -#: frontend/src/pages/Quizzes.vue:266 +#: frontend/src/pages/Quizzes.vue:293 msgid "Quizzes deleted successfully" msgstr "" @@ -6424,7 +6457,7 @@ msgid "Related Courses" msgstr "" #. Option for the 'Work Mode' (Select) field in DocType 'Job Opportunity' -#: frontend/src/pages/JobForm.vue:303 frontend/src/pages/Jobs.vue:315 +#: frontend/src/pages/JobForm.vue:303 frontend/src/pages/Jobs.vue:336 #: lms/job/doctype/job_opportunity/job_opportunity.json msgid "Remote" msgstr "" @@ -6609,7 +6642,7 @@ msgstr "" #: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseModal.vue:9 #: frontend/src/pages/Programs/ProgramForm.vue:233 #: frontend/src/pages/QuizForm.vue:43 frontend/src/pages/QuizSubmission.vue:14 -#: frontend/src/pages/Quizzes.vue:108 +#: frontend/src/pages/Quizzes.vue:124 #: lms/job/web_form/job_opportunity/job_opportunity.json msgid "Save" msgstr "" @@ -6634,10 +6667,14 @@ msgstr "" msgid "Scheduled Flow" msgstr "" +#: frontend/src/pages/PersonaForm.vue:100 +msgid "School / University Admin" +msgstr "" + #. Label of the score (Int) field in DocType 'LMS Quiz Submission' #: frontend/src/pages/Courses/StudentCourseProgress.vue:85 #: frontend/src/pages/QuizSubmission.vue:39 -#: frontend/src/pages/QuizSubmissionList.vue:96 +#: frontend/src/pages/QuizSubmissionList.vue:97 #: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json msgid "Score" msgstr "" @@ -6649,7 +6686,7 @@ msgstr "" #: frontend/src/components/Settings/Evaluators.vue:57 #: frontend/src/components/Settings/Members.vue:25 -#: frontend/src/pages/Courses/Courses.vue:46 frontend/src/pages/Jobs.vue:59 +#: frontend/src/pages/Courses/Courses.vue:47 frontend/src/pages/Jobs.vue:60 #: frontend/src/pages/Search/Search.vue:5 #: frontend/src/pages/Search/Search.vue:250 msgid "Search" @@ -6660,12 +6697,12 @@ msgstr "" msgid "Search by Member" msgstr "" -#: frontend/src/pages/CertifiedParticipants.vue:26 +#: frontend/src/pages/CertifiedParticipants.vue:28 msgid "Search by Name" msgstr "" -#: frontend/src/pages/Assignments.vue:33 -#: frontend/src/pages/Batches/Batches.vue:65 +#: frontend/src/pages/Assignments.vue:36 +#: frontend/src/pages/Batches/Batches.vue:67 #: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:48 msgid "Search by Title" msgstr "" @@ -6752,6 +6789,10 @@ msgstr "" msgid "Select from questions you have already created" msgstr "" +#: frontend/src/pages/PersonaForm.vue:126 +msgid "Selling courses and earning income" +msgstr "" + #: frontend/src/components/ContactUsEmail.vue:33 #: frontend/src/pages/JobApplications.vue:127 msgid "Send" @@ -6761,7 +6802,7 @@ msgstr "" msgid "Send Confirmation Email" msgstr "" -#: frontend/src/pages/JobApplications.vue:318 +#: frontend/src/pages/JobApplications.vue:319 msgid "Send Email" msgstr "" @@ -6861,7 +6902,7 @@ msgid "Show Answer" msgstr "" #. Label of the show_answers (Check) field in DocType 'LMS Quiz' -#: frontend/src/pages/QuizForm.vue:93 frontend/src/pages/Quizzes.vue:299 +#: frontend/src/pages/QuizForm.vue:93 frontend/src/pages/Quizzes.vue:326 #: lms/lms/doctype/lms_quiz/lms_quiz.json msgid "Show Answers" msgstr "" @@ -6945,7 +6986,7 @@ msgstr "" msgid "Skills" msgstr "" -#: frontend/src/pages/PersonaForm.vue:51 lms/templates/onboarding_header.html:6 +#: frontend/src/pages/PersonaForm.vue:46 lms/templates/onboarding_header.html:6 msgid "Skip" msgstr "" @@ -6965,6 +7006,10 @@ msgstr "" msgid "Slots" msgstr "" +#: frontend/src/pages/PersonaForm.vue:103 +msgid "Software Developer" +msgstr "" + #: frontend/src/pages/Batches/components/BatchCard.vue:27 #: frontend/src/pages/Batches/components/BatchOverlay.vue:32 msgid "Sold Out" @@ -7079,7 +7124,7 @@ msgstr "" #: frontend/src/components/Settings/ZoomSettings.vue:192 #: frontend/src/pages/AssignmentSubmissionList.vue:19 #: frontend/src/pages/JobForm.vue:61 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:284 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:285 #: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/job/web_form/job_opportunity/job_opportunity.json #: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json @@ -7151,7 +7196,7 @@ msgid "Subject" msgstr "" #: frontend/src/pages/Batches/components/AnnouncementModal.vue:98 -#: frontend/src/pages/JobApplications.vue:287 +#: frontend/src/pages/JobApplications.vue:288 msgid "Subject is required" msgstr "" @@ -7176,7 +7221,7 @@ msgstr "" msgid "Submissions" msgstr "" -#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:258 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmissions.vue:259 msgid "Submissions deleted successfully" msgstr "" @@ -7192,7 +7237,7 @@ msgstr "" msgid "Submit Feedback" msgstr "" -#: frontend/src/pages/PersonaForm.vue:43 +#: frontend/src/pages/PersonaForm.vue:39 msgid "Submit and Continue" msgstr "" @@ -7218,7 +7263,7 @@ msgstr "" msgid "Sunday" msgstr "" -#: lms/lms/api.py:1064 +#: lms/lms/api.py:1083 msgid "Suspicious pattern found in {0}: {1}" msgstr "" @@ -7283,6 +7328,10 @@ msgstr "" msgid "Tap" msgstr "" +#: frontend/src/pages/PersonaForm.vue:114 +msgid "Teaching students in a school/university" +msgstr "" + #. Option for the 'Color' (Select) field in DocType 'LMS Course' #: lms/lms/doctype/lms_course/lms_course.json msgid "Teal" @@ -7354,7 +7403,7 @@ msgstr "" msgid "The Google Meet account does not have a Google Calendar configured. Please set up a Google Calendar first." msgstr "" -#: lms/lms/utils.py:2322 +#: lms/lms/utils.py:2436 msgid "The batch does not exist." msgstr "" @@ -7362,7 +7411,7 @@ msgstr "" msgid "The batch you have enrolled for is starting tomorrow. Please be prepared and be on time for the session." msgstr "" -#: lms/lms/utils.py:1801 +#: lms/lms/utils.py:1915 msgid "The coupon code '{0}' is invalid." msgstr "" @@ -7386,7 +7435,7 @@ msgstr "" msgid "The last day to schedule your evaluations is " msgstr "" -#: lms/lms/utils.py:2306 +#: lms/lms/utils.py:2420 msgid "The lesson does not exist." msgstr "" @@ -7402,7 +7451,7 @@ msgstr "" msgid "The slot is already booked by another participant." msgstr "" -#: lms/lms/utils.py:1499 lms/lms/utils.py:2003 +#: lms/lms/utils.py:1613 lms/lms/utils.py:2117 msgid "The specified batch does not exist." msgstr "" @@ -7464,15 +7513,15 @@ msgstr "" msgid "This class has ended" msgstr "" -#: lms/lms/utils.py:1830 +#: lms/lms/utils.py:1944 msgid "This coupon has expired." msgstr "" -#: lms/lms/utils.py:1833 +#: lms/lms/utils.py:1947 msgid "This coupon has reached its maximum usage limit." msgstr "" -#: lms/lms/utils.py:1842 +#: lms/lms/utils.py:1956 msgid "This coupon is not applicable to this {0}." msgstr "" @@ -7480,7 +7529,7 @@ msgstr "" msgid "This course has:" msgstr "" -#: lms/lms/utils.py:1761 +#: lms/lms/utils.py:1875 msgid "This course is free." msgstr "" @@ -7636,17 +7685,17 @@ msgstr "" #: frontend/src/components/Modals/DiscussionModal.vue:18 #: frontend/src/components/Modals/LiveClassModal.vue:23 #: frontend/src/components/Settings/BadgeForm.vue:15 -#: frontend/src/pages/Assignments.vue:242 +#: frontend/src/pages/Assignments.vue:265 #: frontend/src/pages/Batches/BatchForm.vue:20 #: frontend/src/pages/Batches/components/NewBatchModal.vue:14 #: frontend/src/pages/Courses/CourseForm.vue:16 #: frontend/src/pages/Courses/NewCourseModal.vue:14 #: frontend/src/pages/JobForm.vue:25 frontend/src/pages/LessonForm.vue:21 #: frontend/src/pages/ProgrammingExercises/ProgrammingExerciseForm.vue:22 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:289 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:313 #: frontend/src/pages/Programs/ProgramForm.vue:25 -#: frontend/src/pages/QuizForm.vue:56 frontend/src/pages/Quizzes.vue:120 -#: frontend/src/pages/Quizzes.vue:272 +#: frontend/src/pages/QuizForm.vue:56 frontend/src/pages/Quizzes.vue:136 +#: frontend/src/pages/Quizzes.vue:299 #: lms/lms/doctype/course_chapter/course_chapter.json #: lms/lms/doctype/course_lesson/course_lesson.json #: lms/lms/doctype/lms_assignment/lms_assignment.json @@ -7682,7 +7731,7 @@ msgstr "" msgid "To Date" msgstr "" -#: lms/lms/utils.py:1775 +#: lms/lms/utils.py:1889 msgid "To join this batch, please contact the Administrator." msgstr "" @@ -7695,7 +7744,7 @@ msgid "Total" msgstr "" #. Label of the total_marks (Int) field in DocType 'LMS Quiz' -#: frontend/src/pages/QuizForm.vue:73 frontend/src/pages/Quizzes.vue:278 +#: frontend/src/pages/QuizForm.vue:73 frontend/src/pages/Quizzes.vue:305 #: lms/lms/doctype/lms_quiz/lms_quiz.json msgid "Total Marks" msgstr "" @@ -7709,10 +7758,18 @@ msgstr "" msgid "Total Signups" msgstr "" +#: frontend/src/pages/PersonaForm.vue:96 +msgid "Trainer / Instructor" +msgstr "" + #: frontend/src/components/Modals/FeedbackModal.vue:11 msgid "Training Feedback" msgstr "" +#: frontend/src/pages/PersonaForm.vue:118 +msgid "Training employees in my company" +msgstr "" + #: frontend/src/components/Settings/Transactions/TransactionDetails.vue:10 msgid "Transaction Details" msgstr "" @@ -7759,10 +7816,10 @@ msgstr "" #. Label of the type (Select) field in DocType 'LMS Quiz Question' #: frontend/src/components/Modals/AssessmentModal.vue:22 #: frontend/src/components/Modals/Question.vue:41 -#: frontend/src/pages/Assignments.vue:39 frontend/src/pages/Assignments.vue:248 +#: frontend/src/pages/Assignments.vue:41 frontend/src/pages/Assignments.vue:271 #: frontend/src/pages/Batches/components/Assessments.vue:213 -#: frontend/src/pages/JobForm.vue:30 frontend/src/pages/Jobs.vue:85 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:55 +#: frontend/src/pages/JobForm.vue:30 frontend/src/pages/Jobs.vue:82 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:54 #: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/job/web_form/job_opportunity/job_opportunity.json #: lms/lms/doctype/lms_assignment/lms_assignment.json @@ -7777,7 +7834,7 @@ msgstr "" msgid "Type '/' for commands or select text to format" msgstr "" -#: frontend/src/components/Quiz.vue:882 +#: frontend/src/components/Quiz.vue:885 msgid "Type your answer" msgstr "" @@ -7833,7 +7890,7 @@ msgstr "" msgid "Under Review" msgstr "" -#: frontend/src/pages/Batches/Batches.vue:326 +#: frontend/src/pages/Batches/Batches.vue:327 #: frontend/src/pages/Courses/Courses.vue:336 msgid "Unpublished" msgstr "" @@ -7859,7 +7916,7 @@ msgstr "" #. Option for the 'Status' (Select) field in DocType 'LMS Certificate Request' #. Label of the upcoming (Check) field in DocType 'LMS Course' -#: frontend/src/pages/Batches/Batches.vue:324 +#: frontend/src/pages/Batches/Batches.vue:325 #: frontend/src/pages/Courses/CourseForm.vue:129 #: frontend/src/pages/Courses/Courses.vue:326 #: lms/lms/doctype/lms_certificate_request/lms_certificate_request.json @@ -7890,9 +7947,9 @@ msgstr "" msgid "Update Password" msgstr "" -#: frontend/src/pages/Assignments.vue:255 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:302 -#: frontend/src/pages/Quizzes.vue:306 +#: frontend/src/pages/Assignments.vue:278 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:326 +#: frontend/src/pages/Quizzes.vue:333 msgid "Updated On" msgstr "" @@ -7970,7 +8027,7 @@ msgstr "" msgid "User Skill" msgstr "" -#: lms/lms/api.py:1817 +#: lms/lms/api.py:1836 msgid "User does not have permission to access this user's profile details." msgstr "" @@ -8025,7 +8082,7 @@ msgstr "" msgid "View Certificate" msgstr "" -#: frontend/src/pages/JobApplications.vue:312 +#: frontend/src/pages/JobApplications.vue:313 msgid "View Resume" msgstr "" @@ -8102,7 +8159,7 @@ msgstr "" msgid "What are Instructor Notes?" msgstr "" -#: frontend/src/pages/PersonaForm.vue:32 +#: frontend/src/pages/PersonaForm.vue:28 msgid "What best describes your role?" msgstr "" @@ -8145,7 +8202,7 @@ msgid "Work Experience Details" msgstr "" #. Label of the work_mode (Select) field in DocType 'Job Opportunity' -#: frontend/src/pages/JobForm.vue:37 frontend/src/pages/Jobs.vue:93 +#: frontend/src/pages/JobForm.vue:37 frontend/src/pages/Jobs.vue:89 #: lms/job/doctype/job_opportunity/job_opportunity.json msgid "Work Mode" msgstr "" @@ -8182,31 +8239,31 @@ msgstr "" msgid "You are already enrolled for this course." msgstr "" -#: lms/lms/utils.py:1295 +#: lms/lms/utils.py:1409 msgid "You are not authorized to view the assessments of this batch." msgstr "" -#: lms/lms/utils.py:1497 +#: lms/lms/utils.py:1611 msgid "You are not authorized to view the chart data of this batch." msgstr "" -#: lms/lms/utils.py:2093 +#: lms/lms/utils.py:2207 msgid "You are not authorized to view the details of this program." msgstr "" -#: lms/lms/utils.py:1707 +#: lms/lms/utils.py:1821 msgid "You are not authorized to view the discussion replies for this topic." msgstr "" -#: lms/lms/utils.py:1660 +#: lms/lms/utils.py:1774 msgid "You are not authorized to view the discussion topics for this item." msgstr "" -#: lms/lms/utils.py:1261 +#: lms/lms/utils.py:1375 msgid "You are not authorized to view the question details." msgstr "" -#: lms/lms/utils.py:1406 +#: lms/lms/utils.py:1520 msgid "You are not authorized to view the students of this batch." msgstr "" @@ -8255,7 +8312,7 @@ msgstr "" msgid "You cannot enroll in an unpublished course." msgstr "" -#: lms/lms/utils.py:2154 +#: lms/lms/utils.py:2268 msgid "You cannot enroll in an unpublished program." msgstr "" @@ -8271,35 +8328,35 @@ msgstr "" msgid "You cannot schedule evaluations for past slots." msgstr "" -#: lms/lms/utils.py:2334 +#: lms/lms/utils.py:2448 msgid "You do not have access to this batch." msgstr "" -#: lms/lms/utils.py:2317 +#: lms/lms/utils.py:2431 msgid "You do not have access to this course." msgstr "" -#: lms/lms/api.py:875 lms/lms/doctype/lms_batch/lms_batch.py:365 +#: lms/lms/api.py:894 lms/lms/doctype/lms_batch/lms_batch.py:365 msgid "You do not have permission to access announcements for this batch." msgstr "" -#: lms/lms/api.py:2316 +#: lms/lms/api.py:2335 msgid "You do not have permission to access badges." msgstr "" -#: lms/lms/api.py:1176 +#: lms/lms/api.py:1195 msgid "You do not have permission to access heatmap data." msgstr "" -#: lms/lms/api.py:2155 +#: lms/lms/api.py:2174 msgid "You do not have permission to access lesson completion stats." msgstr "" -#: lms/lms/api.py:2195 +#: lms/lms/api.py:2214 msgid "You do not have permission to access this course's assessment data." msgstr "" -#: lms/lms/api.py:1718 +#: lms/lms/api.py:1737 msgid "You do not have permission to access this course's progress data." msgstr "" @@ -8307,7 +8364,7 @@ msgstr "" msgid "You do not have permission to access this page." msgstr "" -#: lms/lms/api.py:1393 lms/lms/api.py:1402 +#: lms/lms/api.py:1412 lms/lms/api.py:1421 msgid "You do not have permission to cancel this evaluation." msgstr "" @@ -8315,35 +8372,35 @@ msgstr "" msgid "You do not have permission to create a live class." msgstr "" -#: lms/lms/api.py:945 +#: lms/lms/api.py:964 msgid "You do not have permission to delete this batch." msgstr "" -#: lms/lms/api.py:1130 +#: lms/lms/api.py:1149 msgid "You do not have permission to delete this chapter." msgstr "" -#: lms/lms/api.py:905 +#: lms/lms/api.py:924 msgid "You do not have permission to delete this course." msgstr "" -#: lms/lms/api.py:534 +#: lms/lms/api.py:553 msgid "You do not have permission to delete this lesson." msgstr "" -#: lms/lms/api.py:2396 +#: lms/lms/api.py:2415 msgid "You do not have permission to export this course." msgstr "" -#: lms/lms/api.py:617 lms/lms/api.py:993 +#: lms/lms/api.py:636 lms/lms/api.py:1012 msgid "You do not have permission to modify this chapter." msgstr "" -#: lms/lms/api.py:554 +#: lms/lms/api.py:573 msgid "You do not have permission to modify this lesson." msgstr "" -#: lms/lms/api.py:1462 +#: lms/lms/api.py:1481 msgid "You do not have permission to modify this role." msgstr "" @@ -8355,11 +8412,11 @@ msgstr "" msgid "You do not have permission to set up calendar events for this evaluation." msgstr "" -#: lms/lms/api.py:1601 lms/lms/api.py:1605 +#: lms/lms/api.py:1620 lms/lms/api.py:1624 msgid "You do not have permission to update meta tags." msgstr "" -#: lms/lms/api.py:1642 +#: lms/lms/api.py:1661 msgid "You do not have permission to update this submission." msgstr "" @@ -8469,7 +8526,7 @@ msgstr "" msgid "You will have to get {0}% correct answers in order to pass the quiz." msgstr "" -#: frontend/src/pages/Notifications.vue:158 +#: frontend/src/pages/Notifications.vue:156 msgid "You're all caught up! Check back later for updates." msgstr "" @@ -8627,11 +8684,11 @@ msgstr "" msgid "cancel your application" msgstr "" -#: frontend/src/pages/CertifiedParticipants.vue:96 +#: frontend/src/pages/CertifiedParticipants.vue:92 msgid "certificate" msgstr "" -#: frontend/src/pages/CertifiedParticipants.vue:95 +#: frontend/src/pages/CertifiedParticipants.vue:91 msgid "certificates" msgstr "" @@ -8716,12 +8773,12 @@ msgstr "" msgid "matches" msgstr "" -#: frontend/src/pages/Programs/Programs.vue:42 +#: frontend/src/pages/Programs/Programs.vue:48 #: frontend/src/pages/Programs/StudentPrograms.vue:36 msgid "member" msgstr "" -#: frontend/src/pages/Programs/Programs.vue:42 +#: frontend/src/pages/Programs/Programs.vue:48 #: frontend/src/pages/Programs/StudentPrograms.vue:36 msgid "members" msgstr "" @@ -8730,11 +8787,11 @@ msgstr "" msgid "minutes" msgstr "" -#: frontend/src/pages/Assignments.vue:118 -#: frontend/src/pages/CertifiedParticipants.vue:119 -#: frontend/src/pages/JobApplications.vue:109 frontend/src/pages/Jobs.vue:125 -#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:130 -#: frontend/src/pages/Quizzes.vue:97 +#: frontend/src/pages/Assignments.vue:128 +#: frontend/src/pages/CertifiedParticipants.vue:128 +#: frontend/src/pages/JobApplications.vue:109 frontend/src/pages/Jobs.vue:134 +#: frontend/src/pages/ProgrammingExercises/ProgrammingExercises.vue:140 +#: frontend/src/pages/Quizzes.vue:110 msgid "of" msgstr "" @@ -8787,7 +8844,7 @@ msgstr "" msgid "you can" msgstr "" -#: frontend/src/pages/Assignments.vue:28 +#: frontend/src/pages/Assignments.vue:30 msgid "{0} Assignments" msgstr "" @@ -8795,11 +8852,11 @@ msgstr "" msgid "{0} Exercises" msgstr "" -#: frontend/src/pages/Quizzes.vue:16 +#: frontend/src/pages/Quizzes.vue:21 msgid "{0} Quizzes" msgstr "" -#: lms/lms/api.py:796 lms/lms/api.py:804 +#: lms/lms/api.py:815 lms/lms/api.py:823 msgid "{0} Settings not found" msgstr "" @@ -8843,7 +8900,7 @@ msgstr "" msgid "{0} is your evaluator" msgstr "" -#: lms/lms/utils.py:565 +#: lms/lms/utils.py:552 msgid "{0} mentioned you in a comment" msgstr "" @@ -8851,15 +8908,15 @@ msgstr "" msgid "{0} mentioned you in a comment in your batch." msgstr "" -#: lms/lms/utils.py:518 lms/lms/utils.py:524 +#: lms/lms/utils.py:505 lms/lms/utils.py:511 msgid "{0} mentioned you in a comment in {1}" msgstr "" -#: lms/lms/api.py:858 +#: lms/lms/api.py:877 msgid "{0} not found" msgstr "" -#: frontend/src/pages/Jobs.vue:35 +#: frontend/src/pages/Jobs.vue:36 msgid "{0} {1} Jobs" msgstr "" From 847b15e37bea6c159f4e1f5839ae78c4099d2d13 Mon Sep 17 00:00:00 2001 From: Raizaaa <89463672+raizasafeel@users.noreply.github.com> Date: Tue, 19 May 2026 10:33:47 +0530 Subject: [PATCH 06/10] fix: job application page is now visible --- frontend/src/pages/JobApplications.vue | 40 +++++++++++++++++--------- lms/lms/api.py | 25 ++++++++++++++++ 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/frontend/src/pages/JobApplications.vue b/frontend/src/pages/JobApplications.vue index 7549889280..a108185389 100644 --- a/frontend/src/pages/JobApplications.vue +++ b/frontend/src/pages/JobApplications.vue @@ -209,21 +209,27 @@ const props = defineProps({ const applications = createListResource({ doctype: 'LMS Job Application', - fields: [ - 'name', - 'user.user_image as user_image', - 'user.full_name as full_name', - 'user.email as email', - 'creation', - 'resume', - 'job.job_title as job_title', - ], + fields: ['name', 'user', 'creation', 'resume', 'job_title'], filters: { job: props.job, }, auto: true, }) +const users = createResource({ + url: 'lms.lms.api.get_application_users', + makeParams: () => ({ + user_names: (applications.data || []).map((a) => a.user), + }), +}) + +watch( + () => applications.data, + (rows) => { + if (rows?.length) users.submit() + } +) + const totalApplications = createResource({ url: 'frappe.client.get_count', params: { @@ -354,11 +360,17 @@ const applicationColumns = computed(() => { const applicantRows = computed(() => { if (!applications.data) return [] - return applications.data.map((application) => ({ - ...application, - full_name: application.full_name, - applied_on: dayjs(application.creation).format('DD MMM YYYY'), - })) + const userMap = Object.fromEntries((users.data || []).map((u) => [u.name, u])) + return applications.data.map((application) => { + const user = userMap[application.user] || {} + return { + ...application, + user_image: user.user_image, + full_name: user.full_name, + email: user.email, + applied_on: dayjs(application.creation).format('DD MMM YYYY'), + } + }) }) usePageMeta(() => { diff --git a/lms/lms/api.py b/lms/lms/api.py index 9caf3ca368..a3fc051c4f 100644 --- a/lms/lms/api.py +++ b/lms/lms/api.py @@ -231,6 +231,31 @@ def get_job_details(job: str): ) +@frappe.whitelist() +def get_application_users(user_names: list | str): + # temp function workaround:reverting once upstream restores dotted-field JOINs in `frappe.client.get_list` + if isinstance(user_names, str): + user_names = json.loads(user_names) + if not user_names: + return [] + + visible = frappe.get_list( + "LMS Job Application", + filters={"user": ["in", user_names]}, + fields=["user"], + pluck="user", + ) + visible_user_names = list(set(visible)) + if not visible_user_names: + return [] + + return frappe.get_all( + "User", + filters={"name": ["in", visible_user_names]}, + fields=["name", "user_image", "full_name", "email"], + ) + + def sanitize_job_filters(filters, or_filters): ALLOWED_FILTERS = ("status", "type", "work_mode", "country") ALLOWED_OR_FILTERS = ("job_title", "company_name", "location") From 9081245dccb1c977e392dfda03e624c335681834 Mon Sep 17 00:00:00 2001 From: Raizaaa <89463672+raizasafeel@users.noreply.github.com> Date: Thu, 14 May 2026 14:29:33 +0530 Subject: [PATCH 07/10] fix: race condition caused duplicate course progress --- .../doctype/course_lesson/course_lesson.py | 41 ++++++++++------- .../lms_course_progress.py | 12 +++++ .../test_lms_course_progress.py | 27 +++++++++++ lms/patches.txt | 1 + .../v2_0/delete_duplicate_course_progress.py | 45 +++++++++++++++++++ 5 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 lms/patches/v2_0/delete_duplicate_course_progress.py diff --git a/lms/lms/doctype/course_lesson/course_lesson.py b/lms/lms/doctype/course_lesson/course_lesson.py index 74d2eecd05..927b132847 100644 --- a/lms/lms/doctype/course_lesson/course_lesson.py +++ b/lms/lms/doctype/course_lesson/course_lesson.py @@ -98,25 +98,32 @@ def save_progress(lesson: str, course: str, scorm_details: dict = None): scorm_details = frappe._dict(**scorm_details) if not progress_already_exists and quiz_completed and assignment_completed and not scorm_details: - frappe.get_doc( - { - "doctype": "LMS Course Progress", - "lesson": lesson, - "status": "Complete", - "member": frappe.session.user, - } - ).save(ignore_permissions=True) + try: + frappe.get_doc( + { + "doctype": "LMS Course Progress", + "lesson": lesson, + "status": "Complete", + "member": frappe.session.user, + } + ).save(ignore_permissions=True) + except frappe.UniqueValidationError: + # concurrent request created the progress doc + pass elif scorm_details and not lesson_already_completed and not progress_already_exists: # Create new SCORM progress - frappe.get_doc( - { - "doctype": "LMS Course Progress", - "lesson": lesson, - "status": "Complete" if scorm_details.is_complete else "Partially Complete", - "member": frappe.session.user, - "scorm_content": "" if scorm_details.is_complete else scorm_details.scorm_content, - } - ).save(ignore_permissions=True) + try: + frappe.get_doc( + { + "doctype": "LMS Course Progress", + "lesson": lesson, + "status": "Complete" if scorm_details.is_complete else "Partially Complete", + "member": frappe.session.user, + "scorm_content": "" if scorm_details.is_complete else scorm_details.scorm_content, + } + ).save(ignore_permissions=True) + except frappe.UniqueValidationError: + pass elif scorm_details and not lesson_already_completed and progress_already_exists: # Update Existing SCORM Progress frappe.db.set_value( diff --git a/lms/lms/doctype/lms_course_progress/lms_course_progress.py b/lms/lms/doctype/lms_course_progress/lms_course_progress.py index 1bfaee528f..e9d9633f41 100644 --- a/lms/lms/doctype/lms_course_progress/lms_course_progress.py +++ b/lms/lms/doctype/lms_course_progress/lms_course_progress.py @@ -2,12 +2,24 @@ # For license information, please see license.txt import frappe +from frappe import _ from frappe.model.document import Document from lms.lms.utils import recalculate_course_progress class LMSCourseProgress(Document): + def before_insert(self): + if ( + self.member + and self.lesson + and frappe.db.exists("LMS Course Progress", {"member": self.member, "lesson": self.lesson}) + ): + frappe.throw( + _("Progress is already recorded for this lesson."), + frappe.UniqueValidationError, + ) + def on_update(self): recalculate_course_progress(self.course, self.member) diff --git a/lms/lms/doctype/lms_course_progress/test_lms_course_progress.py b/lms/lms/doctype/lms_course_progress/test_lms_course_progress.py index 04e0ab1373..61b82b4121 100644 --- a/lms/lms/doctype/lms_course_progress/test_lms_course_progress.py +++ b/lms/lms/doctype/lms_course_progress/test_lms_course_progress.py @@ -1,6 +1,8 @@ # Copyright (c) 2021, FOSS United and Contributors # See license.txt +import frappe + from lms.lms.test_helpers import BaseTestUtils @@ -37,3 +39,28 @@ def test_manual_progress_recalculates_enrollment(self): self.enrollment.reload() self.assertEqual(self.enrollment.progress, 100) + + def test_duplicate_progress_is_rejected(self): + """Duplicate progress row for the same (member, lesson) not allowed""" + self._create_lesson_progress(self.student.email, self.course.name, self.lessons[0].name) + + lms_course_progress = frappe.new_doc("LMS Course Progress") + lms_course_progress.update( + { + "member": self.student.email, + "course": self.course.name, + "lesson": self.lessons[0].name, + "status": "Complete", + } + ) + with self.assertRaises(frappe.UniqueValidationError): + lms_course_progress.insert(ignore_permissions=True) + + count = frappe.db.count( + "LMS Course Progress", + {"member": self.student.email, "lesson": self.lessons[0].name}, + ) + self.assertEqual(count, 1) + + self.enrollment.reload() + self.assertEqual(self.enrollment.progress, 25) diff --git a/lms/patches.txt b/lms/patches.txt index a7ad3dde84..a9b35a6f4a 100644 --- a/lms/patches.txt +++ b/lms/patches.txt @@ -124,4 +124,5 @@ lms.patches.v2_0.set_conferencing_provider_for_zoom lms.patches.v2_0.sync_evaluator_roles lms.patches.v2_0.fix_livecode_url_default lms.patches.v2_0.give_event_permission #10-03-2026 +lms.patches.v2_0.delete_duplicate_course_progress #13-05-2026 lms.patches.v2_0.restore_system_manager_perms #13-05-2026 diff --git a/lms/patches/v2_0/delete_duplicate_course_progress.py b/lms/patches/v2_0/delete_duplicate_course_progress.py new file mode 100644 index 0000000000..8e0d58eaa8 --- /dev/null +++ b/lms/patches/v2_0/delete_duplicate_course_progress.py @@ -0,0 +1,45 @@ +import frappe +from frappe.query_builder.functions import Count + +from lms.lms.utils import recalculate_course_progress + + +def execute(): + """ + Remove duplicates from course progress caused by race condition + """ + CourseProgress = frappe.qb.DocType("LMS Course Progress") + duplicate_groups = ( + frappe.qb.from_(CourseProgress) + .select(CourseProgress.member, CourseProgress.lesson) + .where(CourseProgress.member.isnotnull() & CourseProgress.lesson.isnotnull()) + .groupby(CourseProgress.member, CourseProgress.lesson) + .having(Count(CourseProgress.name) > 1) + ).run(as_dict=True) + + affected = set() + for group in duplicate_groups: + rows = frappe.get_all( + "LMS Course Progress", + filters={"member": group.member, "lesson": group.lesson}, + fields=["name", "course", "status", "creation"], + ) + # keep row with the most completion and earlier creation + status_rank = {"Complete": 2, "Partially Complete": 1, "Incomplete": 0} + rows.sort(key=lambda row: row.creation) + rows.sort(key=lambda row: status_rank.get(row.status, -1), reverse=True) + for row in rows[1:]: + frappe.db.delete("LMS Course Progress", {"name": row.name}) + if row.course: + affected.add((row.course, group.member)) + + for course, member in affected: + try: + recalculate_course_progress(course, member) + except Exception: + frappe.log_error( + title="dedupe_course_progress: recalculate failed", + message=f"course={course} member={member}", + ) + + frappe.db.add_unique("LMS Course Progress", ["member", "lesson"]) From 6be29a0cf2587c18c52a099eea82c0d9acd278ee Mon Sep 17 00:00:00 2001 From: Raizaaa <89463672+raizasafeel@users.noreply.github.com> Date: Tue, 19 May 2026 12:49:37 +0530 Subject: [PATCH 08/10] chore: refine rtl semgrep, add space-x/ divide-x checks --- .github/semgrep/tailwind-rtl.yml | 54 +++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/.github/semgrep/tailwind-rtl.yml b/.github/semgrep/tailwind-rtl.yml index ae1af13066..517696f2dd 100644 --- a/.github/semgrep/tailwind-rtl.yml +++ b/.github/semgrep/tailwind-rtl.yml @@ -1,4 +1,4 @@ -# Local run: semgrep scan --config .github/semgrep/tailwind-rtl.yml --metrics off . +# Local run: semgrep scan --config .github/semgrep/tailwind-rtl.yml rules: - id: tailwind-physical-class message: | @@ -6,17 +6,41 @@ rules: languages: [generic] severity: WARNING paths: { include: ["*.vue"] } - pattern-regex: | - (?x) - (?:^|[\s'"`{(\[,]) !?(?:[a-z][a-z0-9-]*:)*-? - (?: - (?:scroll-)?[mp][lr]-(?:auto|px|full|\d+(?:[./]\d+)?|\[[^\]]+\]) - | (?:left|right)-(?:auto|px|full|\d+(?:[./]\d+)?|\[[^\]]+\]) - | text-(?:left|right) - | float-(?:left|right) - | clear-(?:left|right) - | (?:border|rounded)-[lr](?:-[A-Za-z0-9\[\]./#%_-]+)? - | rounded-(?:tl|tr|bl|br)(?:-[A-Za-z0-9\[\]./#%_-]+)? - | space-x-(?:auto|px|reverse|\d+(?:[./]\d+)?|\[[^\]]+\]) - ) - !? (?=$|[\s'"`})\],>;]) + patterns: + - pattern-either: + - pattern-regex: | + (?x) + (?:^|[\s"'`(\[{,]) + !? + (?:[^\s"':]+:)* + -? + (?: + (?:scroll-)?[mp][lr]-(?:auto|px|full|\d+(?:[./]\d+)?|\[[^\]]+\]) + | (?:left|right)-(?:auto|px|full|\d+(?:[./]\d+)?|\[[^\]]+\]) + | (?:text|float|clear)-(?:left|right) + | (?:border|rounded)-[lr](?:-[A-Za-z0-9\[\]./#%_-]+)? + | rounded-(?:tl|tr|bl|br)(?:-[A-Za-z0-9\[\]./#%_-]+)? + ) + !? + (?=$|[\s'"`})\],>;]) + - pattern-not-regex: '\b(?:rtl|ltr):' + + - id: tailwind-space-x-needs-reverse-or-gap + message: | + RTL: `space-x-*` does not flip in RTL. add `rtl:space-x-reverse` or `gap-x-* ms-*` + languages: [generic] + severity: WARNING + paths: { include: ["*.vue"] } + patterns: + - pattern-regex: 'class\s*[:=]\s*"[^"]*\bspace-x-\S+[^"]*"' + - pattern-not-regex: '\brtl:space-x-reverse\b' + + - id: tailwind-divide-x-needs-reverse + message: | + RTL: `divide-x-*` does not flip in RTL, sdd `rtl:divide-x-reverse`. + languages: [generic] + severity: WARNING + paths: { include: ["*.vue"] } + patterns: + - pattern-regex: 'class\s*[:=]\s*"[^"]*\bdivide-x(?:-\S+)?[^"]*"' + - pattern-not-regex: '\brtl:divide-x-reverse\b' From 4703d4dde3468819bce12b78959ac4c1098aae1c Mon Sep 17 00:00:00 2001 From: Raizaaa <89463672+raizasafeel@users.noreply.github.com> Date: Tue, 19 May 2026 12:31:52 +0530 Subject: [PATCH 09/10] fix: drop member email from certified participants response --- lms/lms/api.py | 1 + lms/lms/test_api.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lms/lms/api.py b/lms/lms/api.py index a3fc051c4f..8a26fefd44 100644 --- a/lms/lms/api.py +++ b/lms/lms/api.py @@ -417,6 +417,7 @@ def get_certified_participants( for participant in participants: details = get_certified_participant_details(participant.member) participant.update(details) + participant.pop("member", None) return participants diff --git a/lms/lms/test_api.py b/lms/lms/test_api.py index e54284376e..96b0548ec6 100644 --- a/lms/lms/test_api.py +++ b/lms/lms/test_api.py @@ -24,7 +24,8 @@ def test_certified_participants_with_category(self): filters = {"category": "Utility Course"} certified_participants = get_certified_participants(filters=filters) self.assertEqual(len(certified_participants), 1) - self.assertEqual(certified_participants[0].member, self.student1.email) + self.assertEqual(certified_participants[0].full_name, self.student1.full_name) + self.assertNotIn("member", certified_participants[0]) filters = {"category": "Nonexistent Category"} certified_participants_no_match = get_certified_participants(filters=filters) From 688876c21c9c7a94d6eda38d25ce7ec980b7d5a7 Mon Sep 17 00:00:00 2001 From: Raizaaa <89463672+raizasafeel@users.noreply.github.com> Date: Wed, 20 May 2026 12:21:57 +0530 Subject: [PATCH 10/10] fix: mobile layout scroll --- frontend/src/components/Layouts/MobileLayout.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Layouts/MobileLayout.vue b/frontend/src/components/Layouts/MobileLayout.vue index f4c423e997..374ff7a6a8 100644 --- a/frontend/src/components/Layouts/MobileLayout.vue +++ b/frontend/src/components/Layouts/MobileLayout.vue @@ -1,7 +1,7 @@