diff --git a/frappe/boot.py b/frappe/boot.py index d20aa5b12e..3ad9ec800d 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -385,25 +385,6 @@ def load_print_css(bootinfo, print_settings): ) -def get_unseen_notes(): - note = DocType("Note") - nsb = DocType("Note Seen By").as_("nsb") - - return ( - frappe.qb.from_(note) - .select(note.name, note.title, note.content, note.notify_on_every_login) - .where( - (note.notify_on_login == 1) - & (note.expire_notification_on > frappe.utils.now()) - & ( - ParameterizedValueWrapper(frappe.session.user).notin( - SubQuery(frappe.qb.from_(nsb).select(nsb.user).where(nsb.parent == note.name)) - ) - ) - ) - ).run(as_dict=1) - - def get_success_action(): return frappe.get_all("Success Action", fields=["*"]) diff --git a/frappe/desk/doctype/note/note.py b/frappe/desk/doctype/note/note.py index e303f9b80f..e650b48b12 100644 --- a/frappe/desk/doctype/note/note.py +++ b/frappe/desk/doctype/note/note.py @@ -4,6 +4,8 @@ import frappe from frappe.model.document import Document +UNSEEN_NOTES_KEY = "unseen_notes::" + class Note(Document): # begin: auto-generated types @@ -39,6 +41,10 @@ class Note(Document): self.print_heading = self.name self.sub_heading = "" + def clear_cache(self): + frappe.cache.delete_keys(UNSEEN_NOTES_KEY) + return super().clear_cache() + def mark_seen_by(self, user: str) -> None: if user in [d.user for d in self.seen_by]: return @@ -62,3 +68,33 @@ def get_permission_query_conditions(user): def has_permission(doc, user): return bool(doc.public or doc.owner == user) + + +def get_unseen_notes(): + from frappe.query_builder.terms import ParameterizedValueWrapper, SubQuery + + def _get_unseen_notes(): + note = frappe.qb.DocType("Note") + nsb = frappe.qb.DocType("Note Seen By").as_("nsb") + + return ( + frappe.qb.from_(note) + .select(note.name, note.title, note.content, note.notify_on_every_login) + .where( + (note.notify_on_login == 1) + & (note.expire_notification_on > frappe.utils.now()) + & ( + ParameterizedValueWrapper(frappe.session.user).notin( + SubQuery(frappe.qb.from_(nsb).select(nsb.user).where(nsb.parent == note.name)) + ) + ) + ) + ).run(as_dict=1) + + return ( + frappe.cache.get_value( + f"{UNSEEN_NOTES_KEY}{frappe.session.user}", + generator=_get_unseen_notes, + ) + or [] + ) diff --git a/frappe/sessions.py b/frappe/sessions.py index d903ba7896..56595e4502 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -126,7 +126,8 @@ def clear_expired_sessions(): def get(): """get session boot info""" - from frappe.boot import get_bootinfo, get_unseen_notes + from frappe.boot import get_bootinfo + from frappe.desk.doctype.note.note import get_unseen_notes from frappe.utils.change_log import get_change_log bootinfo = None @@ -175,9 +176,9 @@ def get(): "default_path": get_default_path() or "", } - bootinfo["desk_theme"] = frappe.db.get_value("User", frappe.session.user, "desk_theme") or "Light" + bootinfo["desk_theme"] = frappe.get_cached_value("User", frappe.session.user, "desk_theme") or "Light" bootinfo["user"]["impersonated_by"] = frappe.session.data.get("impersonated_by") - bootinfo["navbar_settings"] = frappe.get_cached_doc("Navbar Settings") + bootinfo["navbar_settings"] = frappe.client_cache.get_doc("Navbar Settings") bootinfo.has_app_updates = has_app_update_notifications() return bootinfo diff --git a/frappe/tests/test_boot.py b/frappe/tests/test_boot.py index e7b04a4033..b28cfa6ff1 100644 --- a/frappe/tests/test_boot.py +++ b/frappe/tests/test_boot.py @@ -1,6 +1,6 @@ import frappe -from frappe.boot import get_unseen_notes, get_user_pages_or_reports -from frappe.desk.doctype.note.note import mark_as_seen +from frappe.boot import get_user_pages_or_reports +from frappe.desk.doctype.note.note import get_unseen_notes, mark_as_seen from frappe.tests import IntegrationTestCase diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index 6be55d7fcb..4e5a2b6a1a 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -1,5 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE +import frappe +from frappe.utils.caching import site_cache + + def get_jenv(): import frappe @@ -88,22 +92,23 @@ def render_template(template, context=None, is_path=None, safe_render=True): if context is None: context = {} - jenv: SandboxedEnvironment = get_jenv() - if is_path or guess_is_path(template): - is_path = True - compiled_template = jenv.get_template(template) - else: - if safe_render and ".__" in template: - throw(_("Illegal template")) - try: - compiled_template = jenv.from_string(template) - except TemplateError: - import html + try: + if is_path or guess_is_path(template): + is_path = True + compiled_template = compile_template(template) + else: + jenv: SandboxedEnvironment = get_jenv() + if safe_render and ".__" in template: + throw(_("Illegal template")) - throw( - title="Jinja Template Error", - msg=f"
{template}
{html.escape(get_traceback())}
", - ) + compiled_template = jenv.from_string(template) + except TemplateError: + import html + + throw( + title="Jinja Template Error", + msg=f"
{template}
{html.escape(get_traceback())}
", + ) import time @@ -136,30 +141,38 @@ def guess_is_path(template): def get_jloader(): + jloader = _get_jloader() + frappe.local.jloader = jloader # backward compat + return jloader + + +@site_cache(ttl=10 * 60, maxsize=16) +def compile_template(path): + jenv = get_jenv() + return jenv.get_template(path) + + +@site_cache(ttl=10 * 60, maxsize=8) +def _get_jloader(): + from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader + import frappe - if not getattr(frappe.local, "jloader", None): - from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader + apps = frappe.get_hooks("template_apps") + if not apps: + apps = list(reversed(frappe.get_installed_apps(_ensure_on_bench=True))) - apps = frappe.get_hooks("template_apps") - if not apps: - apps = list( - reversed( - frappe.local.flags.web_pages_apps or frappe.get_installed_apps(_ensure_on_bench=True) - ) - ) + if "frappe" not in apps: + apps.append("frappe") - if "frappe" not in apps: - apps.append("frappe") + jloader = ChoiceLoader( + # search for something like app/templates/... + [PrefixLoader({app: PackageLoader(app, ".") for app in apps})] + # search for something like templates/... + + [PackageLoader(app, ".") for app in apps] + ) - frappe.local.jloader = ChoiceLoader( - # search for something like app/templates/... - [PrefixLoader({app: PackageLoader(app, ".") for app in apps})] - # search for something like templates/... - + [PackageLoader(app, ".") for app in apps] - ) - - return frappe.local.jloader + return jloader def set_filters(jenv): diff --git a/frappe/utils/user.py b/frappe/utils/user.py index 872fa8dca8..355d968eb7 100644 --- a/frappe/utils/user.py +++ b/frappe/utils/user.py @@ -287,8 +287,8 @@ def get_user_fullname(user: str) -> str: def get_fullname_and_avatar(user: str) -> _dict: - first_name, last_name, avatar, name = frappe.db.get_value( - "User", user, ["first_name", "last_name", "user_image", "name"], order_by=None + first_name, last_name, avatar, name = frappe.get_cached_value( + "User", user, ["first_name", "last_name", "user_image", "name"] ) return _dict( { diff --git a/frappe/website/doctype/website_route_meta/website_route_meta.py b/frappe/website/doctype/website_route_meta/website_route_meta.py index 9164196b13..0846ec130f 100644 --- a/frappe/website/doctype/website_route_meta/website_route_meta.py +++ b/frappe/website/doctype/website_route_meta/website_route_meta.py @@ -20,3 +20,9 @@ class WebsiteRouteMeta(Document): def autoname(self): if self.name and self.name.startswith("/"): self.name = self.name[1:] + + def clear_cache(self): + from frappe.website.website_components.metatags import has_meta_tags + + has_meta_tags.clear_cache() + return super().clear_cache() diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index d3b2b07506..55243ab5db 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -148,7 +148,7 @@ class TemplatePage(BaseTemplatePage): def setup_template_source(self): """Setup template source, frontmatter and markdown conversion""" - self.source = self.get_raw_template() + self.original_source = self.source = self.get_raw_template() self.extract_frontmatter() self.convert_from_markdown() @@ -233,7 +233,10 @@ class TemplatePage(BaseTemplatePage): else: safe_render = True - html = frappe.render_template(self.source, self.context, safe_render=safe_render) + src_modified = self.source is not self.original_source + html = frappe.render_template( + self.source if src_modified else self.context.template, self.context, safe_render=safe_render + ) return html diff --git a/frappe/website/router.py b/frappe/website/router.py index ac0ecc951b..766d13fb6d 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -89,7 +89,7 @@ def get_pages(app=None): if app: apps = [app] else: - apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps() + apps = frappe.get_installed_apps() for app in apps: app_path = frappe.get_app_path(app) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index e8587f551f..94a923729e 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -423,7 +423,7 @@ def get_frontmatter(string): body = result.group(2) return { - "attributes": yaml.safe_load(frontmatter), + "attributes": yaml.safe_load(frontmatter) if frontmatter else "", "body": body, } diff --git a/frappe/website/website_components/metatags.py b/frappe/website/website_components/metatags.py index 3d35be5514..7708a5d42e 100644 --- a/frappe/website/website_components/metatags.py +++ b/frappe/website/website_components/metatags.py @@ -1,4 +1,5 @@ import frappe +from frappe.utils.caching import site_cache METATAGS = ("title", "description", "image", "author", "published_on") @@ -58,14 +59,17 @@ class MetaTags: route = self.path if route == "": # homepage - route = frappe.db.get_single_value("Website Settings", "home_page") + route = frappe.get_website_settings("home_page") - route_exists = ( - route and not route.endswith((".js", ".css")) and frappe.db.exists("Website Route Meta", route) - ) + route_exists = route and not route.endswith((".js", ".css")) and has_meta_tags(route) if route_exists: website_route_meta = frappe.get_doc("Website Route Meta", route) for meta_tag in website_route_meta.meta_tags: d = meta_tag.get_meta_dict() self.tags.update(d) + + +@site_cache(ttl=10 * 60, maxsize=16) +def has_meta_tags(route): + return bool(frappe.db.exists("Website Route Meta", route)) diff --git a/frappe/www/app.py b/frappe/www/app.py index 4414b5a849..114bd62292 100644 --- a/frappe/www/app.py +++ b/frappe/www/app.py @@ -23,7 +23,7 @@ def get_context(context): frappe.msgprint(_("Log in to access this page.")) frappe.redirect(f"/login?{urlencode({'redirect-to': frappe.request.path})}") - elif frappe.db.get_value("User", frappe.session.user, "user_type", order_by=None) == "Website User": + elif frappe.session.data.user_type == "Website User": frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError) try: