From 035a674cff2c207bab72767ce7572ef6b08b3fa3 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 7 Sep 2021 15:44:55 +0530 Subject: [PATCH 1/3] feat: added support for making profile urls to be top-level Made the profile_url_prefix customizable by adding it in the hooks. Issue #192 --- community/hooks.py | 15 ++++-- community/page_renderers.py | 98 +++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 community/page_renderers.py diff --git a/community/hooks.py b/community/hooks.py index 488cbe9e..264b1609 100644 --- a/community/hooks.py +++ b/community/hooks.py @@ -141,15 +141,16 @@ website_route_rules = [ {"from_route": "/courses//learn/.", "to_route": "batch/learn"}, {"from_route": "/courses//progress", "to_route": "batch/progress"}, {"from_route": "/courses//join", "to_route": "batch/join"}, - {"from_route": "/user/", "to_route": "profiles/profile"}, ] website_redirects = [ {"source": "/update-profile", "target": "/edit-profile"}, ] -update_website_context = 'community.widgets.update_website_context' - +update_website_context = [ + 'community.widgets.update_website_context', + 'community.page_renderers.update_website_context' +] ## Specify the additional tabs to be included in the user profile page. ## Each entry must be a subclass of community.community.plugins.ProfileTab # profile_tabs = [] @@ -170,3 +171,11 @@ community_markdown_macro_renderers = { "YouTubeVideo": "community.plugins.youtube_video_renderer", "Video": "community.plugins.video_renderer" } + +# page_renderer to manage profile pages +page_renderer = [ + "community.page_renderers.ProfilePage" +] + +# set this to "/" to have profiles on the top-level +profile_url_prefix = "/users/" diff --git a/community/page_renderers.py b/community/page_renderers.py new file mode 100644 index 00000000..c0fa00b6 --- /dev/null +++ b/community/page_renderers.py @@ -0,0 +1,98 @@ +"""Custom page renderers for Community app. + +Handles rendering of profile pages. +""" +import re +import frappe +from frappe.website.page_renderers.base_renderer import BaseRenderer +from frappe.website.page_renderers.template_page import TemplatePage + +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 update_website_context(context): + """Adds get_profile_url to context + + Specified in the hooks. + """ + context.get_profile_url = get_profile_url + +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_USERNAME = re.compile("[a-zA-Z0-9_]{4,}") + +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 not RE_USERNAME.match(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() From 77c4b53b712e9af2f47389e44a53c86f8ea2bd62 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 7 Sep 2021 16:30:14 +0530 Subject: [PATCH 2/3] feat: added redirect rule to redirect /profile_/foo to the profile url Hack to allow redirecting to profile url from JS as it doesn't know the `profile_url_prefix`. Issue #192 --- community/hooks.py | 1 + community/page_renderers.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/community/hooks.py b/community/hooks.py index 264b1609..7bfb7a70 100644 --- a/community/hooks.py +++ b/community/hooks.py @@ -174,6 +174,7 @@ community_markdown_macro_renderers = { # page_renderer to manage profile pages page_renderer = [ + "community.page_renderers.ProfileRedirectPage", "community.page_renderers.ProfilePage" ] diff --git a/community/page_renderers.py b/community/page_renderers.py index c0fa00b6..7cb0c75d 100644 --- a/community/page_renderers.py +++ b/community/page_renderers.py @@ -40,6 +40,20 @@ def get_profile_url_prefix(): RE_USERNAME = re.compile("[a-zA-Z0-9_]{4,}") +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) From a0b77f5d08b72f6fffd4daef02d3dda07913d267 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 7 Sep 2021 16:57:35 +0530 Subject: [PATCH 3/3] feat: added get_profile_url function to get the profile url in templates This takes care of generating the correct profile URL depending on the `profile_url_prefix` setting. Issue #192 --- community/community/widgets/Avatar.html | 2 +- community/hooks.py | 7 ++++++- community/lms/web_form/profile/profile.js | 2 +- community/lms/widgets/CourseTeaser.html | 2 +- community/lms/widgets/InstructorSection.html | 2 +- community/lms/widgets/MemberCard.html | 2 +- community/lms/widgets/Reviews.html | 2 +- community/page_renderers.py | 7 ------- community/templates/discussions/reply_card.html | 2 +- 9 files changed, 13 insertions(+), 15 deletions(-) diff --git a/community/community/widgets/Avatar.html b/community/community/widgets/Avatar.html index 59f59a91..6450ac9f 100644 --- a/community/community/widgets/Avatar.html +++ b/community/community/widgets/Avatar.html @@ -1,5 +1,5 @@ {% set color = member.get_palette() %} - + {% if member.user_image %} diff --git a/community/hooks.py b/community/hooks.py index 7bfb7a70..40e0e8ba 100644 --- a/community/hooks.py +++ b/community/hooks.py @@ -149,8 +149,13 @@ website_redirects = [ update_website_context = [ 'community.widgets.update_website_context', - 'community.page_renderers.update_website_context' ] +jinja = { + "methods": [ + "community.page_renderers.get_profile_url" + ], + "filters": [] +} ## Specify the additional tabs to be included in the user profile page. ## Each entry must be a subclass of community.community.plugins.ProfileTab # profile_tabs = [] diff --git a/community/lms/web_form/profile/profile.js b/community/lms/web_form/profile/profile.js index c9869a5c..8a707633 100644 --- a/community/lms/web_form/profile/profile.js +++ b/community/lms/web_form/profile/profile.js @@ -8,7 +8,7 @@ frappe.ready(function () { frappe.web_form.after_save = () => { setTimeout(() => { - window.location.href = `/user/${frappe.web_form.get_value(["username"])}`; + window.location.href = `/profile_/${frappe.web_form.get_value(["username"])}`; }) } }) diff --git a/community/lms/widgets/CourseTeaser.html b/community/lms/widgets/CourseTeaser.html index 2b7b54ec..280d2783 100644 --- a/community/lms/widgets/CourseTeaser.html +++ b/community/lms/widgets/CourseTeaser.html @@ -12,7 +12,7 @@ {% endif %} diff --git a/community/lms/widgets/InstructorSection.html b/community/lms/widgets/InstructorSection.html index 9c96c294..c478e452 100644 --- a/community/lms/widgets/InstructorSection.html +++ b/community/lms/widgets/InstructorSection.html @@ -1,6 +1,6 @@
{{ widgets.Avatar(member=instructor, avatar_class="avatar-medium") }} - {{ instructor.full_name }} + {{ instructor.full_name }}
Course Creator
diff --git a/community/lms/widgets/MemberCard.html b/community/lms/widgets/MemberCard.html index 77f00556..89879cbb 100644 --- a/community/lms/widgets/MemberCard.html +++ b/community/lms/widgets/MemberCard.html @@ -11,5 +11,5 @@ Created {{ course_count }} {{ suffix }} {% endif %} - + diff --git a/community/lms/widgets/Reviews.html b/community/lms/widgets/Reviews.html index f9ba6524..8fe25df4 100644 --- a/community/lms/widgets/Reviews.html +++ b/community/lms/widgets/Reviews.html @@ -19,7 +19,7 @@