feat: move /app to /desk

This commit is contained in:
sokumon 2025-11-07 16:43:19 +05:30
parent 3b9656d7fb
commit 86db71f8fa
8 changed files with 163 additions and 15 deletions

View file

@ -364,7 +364,7 @@ def add_home_page(bootinfo, docs):
bootinfo["home_page"] = page.name
except (frappe.DoesNotExistError, frappe.PermissionError):
frappe.clear_last_message()
bootinfo["home_page"] = "Workspaces"
bootinfo["home_page"] = "desktop"
def add_timezone_info(bootinfo):
@ -544,6 +544,7 @@ def get_sidebar_items():
"keep_closed": si.keep_closed,
"display_depends_on": si.display_depends_on,
"url": si.url,
"show_arrow": si.show_arrow,
}
if si.link_type == "Report" and si.link_to:
report_type, ref_doctype = frappe.db.get_value(

View file

@ -57,11 +57,11 @@ email_css = ["email.bundle.css"]
website_route_rules = [
{"from_route": "/kb/<category>", "to_route": "Help Article"},
{"from_route": "/profile", "to_route": "me"},
{"from_route": "/app/<path:app_path>", "to_route": "app"},
{"from_route": "/desk/<path:app_path>", "to_route": "desk"},
]
website_redirects = [
{"source": r"/desk(.*)", "target": r"/app\1"},
{"source": r"/app(.*)", "target": r"/desk\1"},
]
base_template = "templates/base.html"

View file

@ -107,7 +107,7 @@ frappe.router = {
if (path.substr(0, 1) === "/") path = path.substr(1);
path = path.split("/");
if (path[0]) {
return path[0] === "app";
return path[0] === "desk";
}
},
@ -461,7 +461,7 @@ frappe.router = {
}).join("/");
if (path_string) {
return "/app/" + path_string;
return "/desk/" + path_string;
}
// Resolution order
@ -482,11 +482,13 @@ frappe.router = {
if (workspace) {
return (
"/app/" + (workspace.public ? "" : "private/") + frappe.router.slug(workspace.name)
"/desk/" +
(workspace.public ? "" : "private/") +
frappe.router.slug(workspace.name)
);
}
return "/app";
return "/desk";
},
/**
@ -519,8 +521,8 @@ frappe.router = {
strip_prefix(route) {
if (route.substr(0, 1) == "/") route = route.substr(1); // for /app/sub
if (route == "app") route = route.substr(4); // for app
if (route.startsWith("app/")) route = route.substr(4); // for desk/sub
if (route == "desk") route = route.substr(4); // for app
if (route.startsWith("desk/")) route = route.substr(4); // for desk/sub
if (route.substr(0, 1) == "/") route = route.substr(1);
if (route.substr(0, 1) == "#") route = route.substr(1);
if (route.substr(0, 1) == "!") route = route.substr(1);

View file

@ -1389,7 +1389,7 @@ Object.assign(frappe.utils, {
// (item.doctype && frappe.model.can_read(item.doctype))) {
// item.shown = true;
// }
return `/app/${route}`;
return `/desk/${route}`;
},
shorten_number: function (number, country, min_length = 4, max_no_of_decimals = 2) {

View file

@ -141,7 +141,7 @@ class TestWebsite(IntegrationTestCase):
def test_app(self):
frappe.set_user("Administrator")
set_request(method="GET", path="/app")
set_request(method="GET", path="/desk")
response = get_response()
self.assertEqual(response.status_code, 200)
@ -405,8 +405,8 @@ class TestWebsite(IntegrationTestCase):
frappe.conf.update({"app_include_js": ["test_app_include_via_site_config.js"]})
frappe.conf.update({"app_include_css": ["test_app_include_via_site_config.css"]})
set_request(method="GET", path="/app")
content = get_response_content("/app")
set_request(method="GET", path="/desk")
content = get_response_content("/desk")
self.assertIn('<script type="text/javascript" src="/test_app_include.js"></script>', content)
self.assertIn(
'<script type="text/javascript" src="/test_app_include_via_site_config.js"></script>', content

View file

@ -31,8 +31,8 @@ class PathResolver:
request = frappe.local.request or request
# WARN: Hardcoded for better performance
if self.path == "app" or self.path.startswith("app/"):
return "app", TemplatePage("app", self.http_status_code)
if self.path == "desk" or self.path.startswith("desk/"):
return "desk", TemplatePage("desk", self.http_status_code)
# check if the request url is in 404 list
if request.url and can_cache() and frappe.cache.hget("website_404", request.url):

77
frappe/www/desk.html Normal file
View file

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html data-theme-mode="{{ desk_theme.lower() }}" data-theme="{{ desk_theme.lower() }}" dir={{ layout_direction }} lang="{{ lang }}">
<head>
<meta charset="UTF-8">
<!-- Chrome, Firefox OS and Opera -->
<meta name="theme-color" content="#0089FF">
<!-- Windows Phone -->
<meta name="msapplication-navbutton-color" content="#0089FF">
<!-- iOS Safari -->
<meta name="apple-mobile-web-app-status-bar-style" content="#0089FF">
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1.0,
maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, minimal-ui">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="white">
<meta name="mobile-web-app-capable" content="yes">
<title>{{ app_name }}</title>
<link rel="shortcut icon"
href="{{ favicon or "/assets/frappe/images/frappe-favicon.svg" }}" type="image/x-icon">
<link rel="icon"
href="{{ favicon or "/assets/frappe/images/frappe-favicon.svg" }}" type="image/x-icon">
{% for include in app_include_css -%}
{{ include_style(include) }}
{%- endfor -%}
{% if lang == "eo" %}
<script type="text/javascript">
var _jipt = [];
_jipt.push(['project', 'frappe']);
</script>
<script type="text/javascript" src="https://cdn.crowdin.com/jipt/jipt.js"></script>
{% endif %}
</head>
<body>
{% include "templates/includes/splash_screen.html" %}
<div class="main-section">
<header></header>
<div id="body"></div>
<footer></footer>
</div>
<div id="all-symbols" style="display:none"></div>
<div id="build-events-overlay"></div>
<script type="text/javascript">
window._version_number = "{{ build_version }}";
// browser support
window.app = true;
window.dev_server = {{ dev_server }};
if (!window.frappe) window.frappe = {};
frappe.boot = {{ frappe.utils.orjson_dumps(boot, default=frappe.json_handler) }};
frappe._messages = frappe.boot["__messages"];
frappe.csrf_token = "{{ csrf_token }}";
</script>
{%- for path in app_include_icons -%}
{{ include_icons(path) }}
{%- endfor -%}
{% for include in app_include_js %}
{{ include_script(include) }}
{% endfor %}
{% include "templates/includes/app_analytics/google_analytics.html" %}
{% for sound in (sounds or []) %}
<audio preload="auto" id="sound-{{ sound.name }}" volume={{ sound.volume or 1 }}>
<source src="{{ sound.src }}"></source>
</audio>
{% endfor %}
</body>
</html>

68
frappe/www/desk.py Normal file
View file

@ -0,0 +1,68 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
import os
no_cache = 1
import json
import re
from urllib.parse import urlencode
import frappe
import frappe.sessions
from frappe import _
from frappe.utils.jinja_globals import is_rtl
SCRIPT_TAG_PATTERN = re.compile(r"\<script[^<]*\</script\>")
CLOSING_SCRIPT_TAG_PATTERN = re.compile(r"</script\>")
def get_context(context):
if frappe.session.user == "Guest":
frappe.response["status_code"] = 403
frappe.msgprint(_("Log in to access this page."))
frappe.redirect(f"/login?{urlencode({'redirect-to': frappe.request.path})}")
elif frappe.session.data.user_type == "Website User":
frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError)
try:
boot = frappe.sessions.get()
except Exception as e:
raise frappe.SessionBootFailed from e
# this needs commit
csrf_token = frappe.sessions.get_csrf_token()
frappe.db.commit()
hooks = frappe.get_hooks()
app_include_js = hooks.get("app_include_js", []) + frappe.conf.get("app_include_js", [])
app_include_css = hooks.get("app_include_css", []) + frappe.conf.get("app_include_css", [])
app_include_icons = hooks.get("app_include_icons", [])
if frappe.get_system_settings("enable_telemetry") and os.getenv("FRAPPE_SENTRY_DSN"):
app_include_js.append("sentry.bundle.js")
context.update(
{
"no_cache": 1,
"build_version": frappe.utils.get_build_version(),
"app_include_js": app_include_js,
"app_include_css": app_include_css,
"app_include_icons": app_include_icons,
"layout_direction": "rtl" if is_rtl() else "ltr",
"lang": frappe.local.lang,
"sounds": hooks["sounds"],
"boot": boot,
"desk_theme": boot.get("desk_theme") or "Light",
"csrf_token": csrf_token,
"google_analytics_id": frappe.conf.get("google_analytics_id"),
"google_analytics_anonymize_ip": frappe.conf.get("google_analytics_anonymize_ip"),
"app_name": (
frappe.get_website_settings("app_name") or frappe.get_system_settings("app_name") or "Frappe"
),
}
)
return context