"""Custom page renderers for LMS app. Handles rendering of profile pages. """ import re import frappe from frappe.website.page_renderers.base_renderer import BaseRenderer from frappe.website.page_renderers.document_page import DocumentPage from frappe.website.page_renderers.list_page import ListPage from frappe.website.page_renderers.not_found_page import NotFoundPage from frappe.website.page_renderers.print_page import PrintPage from frappe.website.page_renderers.redirect_page import RedirectPage from frappe.website.page_renderers.static_page import StaticPage from frappe.website.page_renderers.template_page import TemplatePage from frappe.website.page_renderers.web_form import WebFormPage def get_profile_url(username): """Returns the profile URL given username. The default URL prefix for profiles is /users, but tha can be customized. This functions looks at the current value from the config and generates the URL for the profile. """ return get_profile_url_prefix() + username def get_profile_url_prefix(): hooks = frappe.get_hooks("profile_url_prefix") or ["/users/"] return hooks[-1] RE_INVALID_USERNAME = re.compile("[@!#$%^&*()<>?/\\|}{~:-]") class ProfileRedirectPage(BaseRenderer): """Renderer to redirect /profile_/foo to /foo. This is useful to redirect to profile pages from javascript as there is no easy to find the profile prefix. """ def can_render(self): return self.path.startswith("profile_/") def render(self): username = self.path[len("profile_/") :] frappe.flags.redirect_location = get_profile_url_prefix() + username return RedirectPage(self.path).render() class ProfilePage(BaseRenderer): def __init__(self, path, http_status_code): super().__init__(path, http_status_code) self.renderer = None def can_render(self): """if "." in self.path: return False""" # has prefix and path starts with prefix? prefix = get_profile_url_prefix().lstrip("/") if prefix and not self.path.startswith(prefix): return False # not a userpage? username = self.get_username() """ if RE_INVALID_USERNAME.search(username): return False """ # if there is prefix then we can allow all usernames if prefix: return True # if we are having top-level usernames, then give preference to # the existing website_route_rules, web pages, web forms etc. # Don't handle any of the exsiting website_route_rules routes = [rule["to_route"] for rule in frappe.get_hooks("website_route_rules")] if self.path in routes: return False # if any of the existing renders can render, let them do renderers = [StaticPage, WebFormPage, DocumentPage, TemplatePage, ListPage, PrintPage] for renderer in renderers: renderer_instance = renderer(self.path, 200) if renderer_instance.can_render(): self.renderer = renderer_instance return True return True def get_username(self): prefix = get_profile_url_prefix().lstrip("/") return self.path[len(prefix) :] def render(self): if self.renderer: return self.renderer.render() else: username = self.get_username() return render_portal_page("profiles/profile", username=username) def render_portal_page(path, **kwargs): frappe.form_dict.update(kwargs) page = TemplatePage(path) return page.render() class CoursePage(BaseRenderer): def __init__(self, path, http_status_code): super().__init__(path, http_status_code) self.renderer = None def can_render(self): return self.path.startswith("course") def render(self): if "learn" in self.path: prefix = self.path.split("/learn")[0] course_name = prefix.split("/")[1] lesson_index = self.path.split("/learn/")[1] chapter_number = lesson_index.split(".")[0] lesson_number = lesson_index.split(".")[1] frappe.flags.redirect_location = ( f"/lms/courses/{course_name}/learn/{chapter_number}-{lesson_number}" ) return RedirectPage(self.path).render() elif len(self.path.split("/")) > 1: course_name = self.path.split("/")[1] frappe.flags.redirect_location = f"/lms/courses/{course_name}" return RedirectPage(self.path).render() else: frappe.flags.redirect_location = "/lms/courses" return RedirectPage(self.path).render()