Merge pull request #202 from fossunited/profile-urls
Added support for making profile urls to be top-level
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{% set color = member.get_palette() %}
|
||||
<a class="button-links" href="/user/{{member.username}}">
|
||||
<a class="button-links" href="{{ get_profile_url(member.username) }}">
|
||||
<span class="avatar {{ avatar_class }}" title="{{ member.full_name }}">
|
||||
{% if member.user_image %}
|
||||
<img class="avatar-frame standard-image" style="object-fit: cover;" src="{{ member.user_image }}" title="{{ member.full_name }}">
|
||||
|
||||
@@ -141,15 +141,21 @@ website_route_rules = [
|
||||
{"from_route": "/courses/<course>/learn/<int:chapter>.<int:lesson>", "to_route": "batch/learn"},
|
||||
{"from_route": "/courses/<course>/progress", "to_route": "batch/progress"},
|
||||
{"from_route": "/courses/<course>/join", "to_route": "batch/join"},
|
||||
{"from_route": "/user/<string(minlength=4):username>", "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',
|
||||
]
|
||||
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 = []
|
||||
@@ -170,3 +176,12 @@ 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.ProfileRedirectPage",
|
||||
"community.page_renderers.ProfilePage"
|
||||
]
|
||||
|
||||
# set this to "/" to have profiles on the top-level
|
||||
profile_url_prefix = "/users/"
|
||||
|
||||
@@ -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"])}`;
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{% endif %}
|
||||
<div class="course-author">
|
||||
{% with author = course.get_instructor() %}
|
||||
{{ widgets.Avatar(member=author, avatar_class="avatar-medium") }} <a href="/user/{{author.username}}">{{ author.full_name }}</a>
|
||||
{{ widgets.Avatar(member=author, avatar_class="avatar-medium") }} <a href="{{get_profile_url(author.username)}}">{{ author.full_name }}</a>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="instructor">
|
||||
{{ widgets.Avatar(member=instructor, avatar_class="avatar-medium") }}
|
||||
<a class="ml-1 instructor-title" href="/user/{{instructor.username}}">{{ instructor.full_name }}</a>
|
||||
<a class="ml-1 instructor-title" href="{{get_profile_url(instructor.username)}}">{{ instructor.full_name }}</a>
|
||||
<div class="instructor-subtitle">Course Creator</div>
|
||||
<!-- <div class="instructor-subtitle">Created {{instructor.get_course_count()}} courses</div> -->
|
||||
</div>
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
Created {{ course_count }} {{ suffix }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<a class="stretched-link" href="/user/{{ member.username }}"></a>
|
||||
<a class="stretched-link" href="{{ get_profile_url(member.username) }}"></a>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="review-card-footer">
|
||||
<div>
|
||||
{{ widgets.Avatar(member=review.owner_details, avatar_class="avatar-medium") }}
|
||||
<a class="button-links" href="/user/{{review.owner_details.username}}">
|
||||
<a class="button-links" href="{{get_profile_url(review.owner_details.username) }}">
|
||||
<span class="course-instructor">
|
||||
{{ review.owner_details.full_name }}
|
||||
</span>
|
||||
|
||||
105
community/page_renderers.py
Normal file
105
community/page_renderers.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""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 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 ProfileRedirectPage(BaseRenderer):
|
||||
"""Renderer to redirect /profile_/foo to <profile_prefix>/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 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()
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="d-flex align-items-center muted-text">
|
||||
{% set member = frappe.get_doc("User", reply.owner) %}
|
||||
{{ widgets.Avatar(member=member, avatar_class="avatar-small")}}
|
||||
<a class="button-links ml-2" href="/user/{{ member.username }}">
|
||||
<a class="button-links ml-2" href="{{ get_profile_url(member.username) }}">
|
||||
{{ member.full_name }}
|
||||
</a>
|
||||
<div class="ml-2 frappe-timestamp" data-timestamp="{{ reply.creation }}"> just now </div>
|
||||
|
||||
Reference in New Issue
Block a user