Merge remote-tracking branch 'upstream/develop' into fix/course-deletion

This commit is contained in:
raizasafeel
2026-01-22 16:33:07 +05:30
91 changed files with 11278 additions and 8036 deletions

View File

@@ -1,3 +1,5 @@
import json
import frappe
ALLOWED_PATHS = [
@@ -12,6 +14,15 @@ ALLOWED_PATHS = [
"/api/method/frappe.integrations.oauth2.authorize",
"/api/method/frappe.integrations.oauth2.approve",
"/api/method/frappe.integrations.oauth2.get_token",
"/api/method/frappe.www.login.login_via_google",
"/api/method/frappe.www.login.login_via_github",
"/api/method/frappe.www.login.login_via_facebook",
"/api/method/frappe.www.login.login_via_frappe",
"/api/method/frappe.www.login.login_via_office365",
"/api/method/frappe.www.login.login_via_salesforce",
"/api/method/frappe.www.login.login_via_fairlogin",
"/api/method/frappe.www.login.login_via_keycloak",
"/api/method/frappe.www.login.custom",
"/api/method/frappe.integrations.oauth2.openid_profile",
"/api/method/frappe.website.doctype.web_page_view.web_page_view.make_view_log",
"/api/method/upload_file",
@@ -33,10 +44,14 @@ ALLOWED_PATHS = [
"/api/method/frappe.utils.print_format.download_pdf",
"/api/method/frappe.desk.search.search_link",
"/api/method/frappe.core.doctype.communication.email.make",
"/api/method/frappe.core.doctype.user.user.reset_password",
]
def authenticate():
if not frappe.conf.get("block_endpoints"):
return
if frappe.form_dict.cmd:
path = f"/api/method/{frappe.form_dict.cmd}"
else:
@@ -48,10 +63,39 @@ def authenticate():
if not path.startswith("/api/"):
return
print("path", path)
if path.startswith("/lms") or path.startswith("/api/method/lms."):
return
if is_server_script_path(path):
return
if is_custom_app_endpoint(path):
return
if path in ALLOWED_PATHS:
return
frappe.throw(f"Access not allowed for this URL: {path}", frappe.PermissionError)
def is_server_script_path(path):
endpoint = path.split("/api/method/")[-1]
if frappe.db.exists("Server Script", {"script_type": "API", "api_method": endpoint, "disabled": 0}):
return True
return False
def is_custom_app_endpoint(path):
allowed_custom_endpoints = frappe.conf.get("allowed_custom_endpoints", [])
if isinstance(allowed_custom_endpoints, str):
try:
parsed = json.loads(allowed_custom_endpoints)
allowed_custom_endpoints = parsed if isinstance(parsed, list) else [allowed_custom_endpoints]
except Exception:
allowed_custom_endpoints = [allowed_custom_endpoints]
for endpoint in allowed_custom_endpoints:
if endpoint in path:
return True
return False

View File

@@ -257,12 +257,12 @@
"length": 0,
"link_filters": null,
"mandatory_depends_on": null,
"modified": "2025-12-24 12:56:32.110405",
"modified": "2025-12-24 12:56:32.110406",
"module": null,
"name": "User-open_to",
"no_copy": 0,
"non_negative": 0,
"options": "\nOpportunities\nHiring",
"options": "\nWork\nHiring",
"permlevel": 0,
"precision": "",
"print_hide": 0,

View File

@@ -331,8 +331,8 @@ def get_certification_query(filters):
)
if field == "member_name":
query = query.where(Certificate.member_name.like(value[1]))
if field == "open_to_opportunities":
query = query.where(User.open_to == "Opportunities")
if field == "open_to_work":
query = query.where(User.open_to == "Work")
if field == "hiring":
query = query.where(User.open_to == "Hiring")
return query

View File

@@ -3,14 +3,6 @@
frappe.ui.form.on("LMS Batch", {
onload: function (frm) {
frm.set_query("student", "students", function (doc) {
return {
filters: {
ignore_user_type: 1,
},
};
});
frm.set_query("reference_doctype", "timetable", function () {
let doctypes = ["Course Lesson", "LMS Quiz", "LMS Assignment"];
return {

View File

@@ -37,7 +37,7 @@ class LMSBatch(Document):
def on_update(self):
if self.has_value_changed("published") and self.published:
frappe.enqueue(send_notification_for_published_batch, batch=self, now=True)
frappe.enqueue(send_notification_for_published_batch, batch=self)
def autoname(self):
if not self.name:

View File

@@ -166,14 +166,14 @@ class TestLMSUtils(BaseTestUtils):
certified_participants_no_match = get_certified_participants(filters=filters)
self.assertEqual(len(certified_participants_no_match), 0)
def test_certified_participants_with_open_to_opportunities(self):
filters = {"open_to_opportunities": 1}
certified_participants_open_to_oppo = get_certified_participants(filters=filters)
self.assertEqual(len(certified_participants_open_to_oppo), 0)
def test_certified_participants_with_open_to_work(self):
filters = {"open_to_work": 1}
certified_participants_open_to_work = get_certified_participants(filters=filters)
self.assertEqual(len(certified_participants_open_to_work), 0)
frappe.db.set_value("User", self.student1.email, "open_to", "Opportunities")
certified_participants_open_to_oppo = get_certified_participants(filters=filters)
self.assertEqual(len(certified_participants_open_to_oppo), 1)
frappe.db.set_value("User", self.student1.email, "open_to", "Work")
certified_participants_open_to_work = get_certified_participants(filters=filters)
self.assertEqual(len(certified_participants_open_to_work), 1)
frappe.db.set_value("User", self.student1.email, "open_to", "")
def test_certified_participants_with_open_to_hiring(self):

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

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

File diff suppressed because it is too large Load Diff

View File

@@ -114,4 +114,5 @@ lms.patches.v2_0.count_in_program
lms.patches.v2_0.fix_scorm_lesson_reference_idx #02-09-2025
lms.patches.v2_0.certified_members_to_certifications #05-10-2025
lms.patches.v2_0.fix_job_application_resume_urls
lms.patches.v2_0.open_to_opportunities
lms.patches.v2_0.open_to_opportunities
lms.patches.v2_0.open_to_work

View File

@@ -0,0 +1,12 @@
import frappe
def execute():
open_to_field_exists = frappe.db.exists("Custom Field", {"dt": "User", "fieldname": "open_to"})
if not open_to_field_exists:
return
open_to_opportunities = frappe.get_all("User", {"open_to": "Opportunities"}, ["name"])
for user in open_to_opportunities:
frappe.db.set_value("User", user.name, "open_to", "Work")

View File

@@ -11,24 +11,16 @@ class TestAuth(FrappeAPITestCase, BaseTestUtils):
self.normal_user = self._create_user("normal-user@example.com", "Normal", "User", ["LMS Student"])
def test_allowed_path(self):
site_url = frappe.utils.get_site_url(frappe.local.site)
headers = {"Authorization": "Bearer set_test_example_user"}
url = site_url + "/api/method/lms.lms.utils.get_courses"
response = self.get(
url,
headers=headers,
)
self.assertNotEqual(response.json.get("exc_type"), "PermissionError")
frappe.form_dict.cmd = "ping"
frappe.session.user = self.normal_user.name
authenticate()
frappe.session.user = "Administrator"
def test_not_allowed_path(self):
site_url = frappe.utils.get_site_url(frappe.local.site)
headers = {"Authorization": "Bearer set_test_example_user"}
url = site_url + "/api/method/frappe.auth.get_logged_user"
response = self.get(
url,
headers=headers,
)
self.assertEqual(response.json.get("exc_type"), "PermissionError")
frappe.form_dict.cmd = "frappe.auth.get_logged_user"
frappe.session.user = self.normal_user.name
self.assertRaises(frappe.PermissionError, authenticate)
frappe.session.user = "Administrator"
def tearDown(self):
BaseTestUtils.tearDown(self)