From c3fbe00eb0a30c0e5786665e937ce46703855d3e Mon Sep 17 00:00:00 2001 From: Shubham Padia Date: Thu, 11 Sep 2025 15:19:23 +0000 Subject: [PATCH] help: Add default page to display on /help when help server is off. We've copied the button and error colors from portico_signin.css. We did not want the new HTML file to depend on portico_signin.css since they are unrelated. In addition, having those colors diverge over time might not be an issue. We make the raw mode work with /help and /help/ both. See https://chat.zulip.org/#narrow/channel/19-documentation/topic/edits.20not.20appearing.20with.20vagrant/near/2257442 Co-authored-by: Alya Abbott --- templates/zerver/development/dev_help.html | 60 +++++++++++++++++++ templates/zerver/development/dev_tools.html | 33 +--------- .../help_center_instructions_macro.html | 19 ++++++ web/styles/portico/dev_help.css | 44 ++++++++++++++ web/webpack.dev-assets.json | 5 ++ zerver/tests/test_docs.py | 49 +++++++++++++++ zerver/views/development/help.py | 57 ++++++++++++++++++ zproject/dev_urls.py | 6 ++ 8 files changed, 241 insertions(+), 32 deletions(-) create mode 100644 templates/zerver/development/dev_help.html create mode 100644 templates/zerver/development/help_center_instructions_macro.html create mode 100644 web/styles/portico/dev_help.css create mode 100644 zerver/views/development/help.py diff --git a/templates/zerver/development/dev_help.html b/templates/zerver/development/dev_help.html new file mode 100644 index 0000000000..b2befbc8e4 --- /dev/null +++ b/templates/zerver/development/dev_help.html @@ -0,0 +1,60 @@ +{% extends "zerver/portico.html" %} +{% set entrypoint = "dev-help" %} + +{% block title %} +Help center server not running | Zulip Dev +{% endblock %} + +{% block portico_content %} + +
+
+ {% if mdx_file_exists %} +

Help center server not running

+

+ To minimize resource requirements for the development + environment, the help center's development server does + not run by default. Below are our recommendations for + accessing this help center page: +

+
    +
  • + Quick reference: If you aren't planning to modify the help center, you can + view it on + zulip.com. Pages may be slightly behind the version + currently in main. +
  • +
  • + Up-to-date reference: You can + view the current raw MDX file, which is recommended for documentation + that touches brand new or recently modified features. +
  • +
  • + Modifying the help center: If you're making changes to + the help center files, be sure to test and provide + screenshots in your pull request description. As + with other code, untested documentation is often buggy. + {% include "zerver/development/help_center_instructions_macro.html" %} +
  • +
+ {% else %} +

+ This is not a valid help path and not a valid MDX file. Please re-check the URL subpath you have provided. +

+ {% endif %} + +
+ {% if mdx_file_exists %} + + View page on zulip.com + + + View page source + + {% endif %} +
+
+
+{% endblock %} diff --git a/templates/zerver/development/dev_tools.html b/templates/zerver/development/dev_tools.html index 4d6dd01d8b..8ec8c68ec5 100644 --- a/templates/zerver/development/dev_tools.html +++ b/templates/zerver/development/dev_tools.html @@ -138,38 +138,7 @@

Development instructions for help center

-

- Running ./tools/run-dev without any flags will not run the help center at all. - The development server for the help center takes significant resources to run and we don't - want to increase the minimum requirements to run Zulip for development. -

-

Dev server (supports hot reload but not search)

-

- This mode is useful when you are editing a help center file, and want to visualize the changes - quickly in the help center documentation. -

-

- ./tools/run-dev --only-help-center will run a dev server at - /help that supports hot reload. Note that, with this flag, search will not work - in the help center docs. In this mode, the Zulip web app and other related services will not run. - Since the dev server consumes a significant amount of memory, this is the recommended way to run - the dev server for the help center. -

-

- If you have a machine with resources significantly more than minimum requirements to run Zulip in - development, you can choose to run the dev server alongside Zulip using - ./tools/run-dev --help-center-dev-server. - The dev server makes a bunch of request to base Zulip URL instead of scoping it to the astro/starlight - base url. For this reason, in this mode, we run the dev server on it's own port and redirect help center - requests to the appropriate port (9995 by default). -

-

Serve static build (supports search but not hot reload)

-

- Please run ./tools/build-help-center to generate a static build of the help center. - ./tools/run-dev --help-center-static-build will host the generated build on - /help. Note that you need to generate a build and pass the flag mentioned for the search - to work. -

+ {% include "zerver/development/help_center_instructions_macro.html" %} diff --git a/templates/zerver/development/help_center_instructions_macro.html b/templates/zerver/development/help_center_instructions_macro.html new file mode 100644 index 0000000000..1385774637 --- /dev/null +++ b/templates/zerver/development/help_center_instructions_macro.html @@ -0,0 +1,19 @@ + diff --git a/web/styles/portico/dev_help.css b/web/styles/portico/dev_help.css new file mode 100644 index 0000000000..6afabab4b1 --- /dev/null +++ b/web/styles/portico/dev_help.css @@ -0,0 +1,44 @@ +.dev-help-page { + .white-box { + width: 40%; + margin: 0 auto; + } + + .dev-help-header { + text-align: center; + margin-bottom: 1em; + } + + .invalid-path-error { + color: hsl(1.1deg 44.7% 50.4%); + font-weight: 400; + display: block; + text-align: center; + margin-top: 1em; + } + + .dev-help-actions { + margin-top: 1.5em; + display: flex; + justify-content: center; + gap: 5px; + + .dev-help-action-button { + display: inline-block; + font-weight: 400; + color: hsl(170deg 41% 52%); + border: 1px solid hsl(170deg 41% 52%); + border-radius: 4px; + padding: 6px 12px; + + transition: + color 0.3s ease, + border-color 0.3s ease; + + &:hover { + color: hsl(156deg 62% 61%); + border-color: hsl(156deg 62% 61%); + } + } + } +} diff --git a/web/webpack.dev-assets.json b/web/webpack.dev-assets.json index ecb0c34a18..cf3ba9b116 100644 --- a/web/webpack.dev-assets.json +++ b/web/webpack.dev-assets.json @@ -15,5 +15,10 @@ "./src/reload_state.ts", "./src/channel.ts" ], + "dev-help": [ + "./src/bundles/portico.ts", + "./styles/portico/dev_help.css", + "./styles/portico/markdown.css" + ], "showroom": ["./src/bundles/showroom.ts"] } diff --git a/zerver/tests/test_docs.py b/zerver/tests/test_docs.py index b06416810e..8b39cb186d 100644 --- a/zerver/tests/test_docs.py +++ b/zerver/tests/test_docs.py @@ -48,6 +48,7 @@ class DocPageTest(ZulipTestCase): "/devtools/", "/emails/", "/errors/", + "/help", "/integrations/", ]: if url.startswith(prefix): @@ -70,6 +71,11 @@ class DocPageTest(ZulipTestCase): if url.startswith("/attribution/"): allow_robots = False + # When a raw MDX file is being fetched, the meta tag to + # disallow robots will be absent. + if url in ["/help/status-and-availability?raw", "/help/?raw", "/help?raw"]: + allow_robots = True + result = self.get_doc(url, subdomain=subdomain) self.print_msg_if_error(url, result) self.assertEqual(result.status_code, 200) @@ -243,6 +249,49 @@ class DocPageTest(ZulipTestCase): self._test("/devtools/", ["Useful development URLs"]) self._test("/emails/", ["Manually generate most emails"]) + def test_dev_help_default_page_endpoints(self) -> None: + # View on Zulip.com and View source URLs should be visible. + self._test( + "/help/status-and-availability", + [ + 'href="https://zulip.com/help/status-and-availability"', + 'href="/help/status-and-availability?raw"', + ], + ) + + # Raw MDX file should be shown when `?raw` is present. + self._test( + "/help/status-and-availability?raw", + ["---", "title: Status and availability", "### Set a status"], + ) + + self._test( + "/help/nonexistent-page-that-does-not-exist", + ["This is not a valid help path and not a valid MDX file"], + ) + + # `?raw` should have no effect when the page does not exist + self._test( + "/help/nonexistent-page-that-does-not-exist?raw", + ["This is not a valid help path and not a valid MDX file"], + ) + + # Root /help and /help/ is a special case without subpath. + self._test("/help/?raw", ["---", "title: Zulip help center"]) + self._test("/help?raw", ["---", "title: Zulip help center"]) + + with ( + mock.patch("builtins.open", side_effect=OSError("File read error")), + self.assertLogs("django.request", level="ERROR") as m, + ): + result = self.client_get("/help/status-and-availability?raw") + self.assertEqual(result.status_code, 500) + self.assertIn("Error reading MDX file", result.content.decode()) + self.assertEqual( + m.output, + ["ERROR:django.request:Internal Server Error: /help/status-and-availability"], + ) + def test_error_endpoints(self) -> None: self._test("/errors/404/", ["Page not found"]) self._test("/errors/5xx/", ["Internal server error"]) diff --git a/zerver/views/development/help.py b/zerver/views/development/help.py new file mode 100644 index 0000000000..845d438217 --- /dev/null +++ b/zerver/views/development/help.py @@ -0,0 +1,57 @@ +import os + +import werkzeug +from django.conf import settings +from django.http import HttpRequest, HttpResponse +from django.shortcuts import render + + +def help_dev_mode_view(request: HttpRequest, subpath: str = "") -> HttpResponse: + """ + Dev only view that displays help information for setting up the + help center dev server in the default `run-dev` mode where the + help center server is not running. Also serves raw MDX content when + `raw` query param is passed is passed. + """ + + def read_mdx_file(filename: str) -> HttpResponse: + file_path = os.path.join( + settings.DEPLOY_ROOT, "starlight_help", "src", "content", "docs", f"{filename}.mdx" + ) + try: + with open(file_path, encoding="utf-8") as f: + content = f.read() + return HttpResponse(content, content_type="text/plain") + except OSError: + return HttpResponse("Error reading MDX file", status=500) + + mdx_file_exists = False + is_requesting_raw_file = request.GET.get("raw") == "" + + if subpath: + subpath = werkzeug.utils.secure_filename(subpath) + raw_url = f"/help/{subpath}?raw" + mdx_path = os.path.join( + settings.DEPLOY_ROOT, "starlight_help", "src", "content", "docs", f"{subpath}.mdx" + ) + mdx_file_exists = os.path.exists(mdx_path) and "/include/" not in mdx_path + if mdx_file_exists and is_requesting_raw_file: + return read_mdx_file(subpath) + else: + if request.path.endswith("/"): + raw_url = "/help/?raw" + else: + raw_url = "/help?raw" + mdx_file_exists = True + if is_requesting_raw_file: + return read_mdx_file("index") + + return render( + request, + "zerver/development/dev_help.html", + { + "subpath": subpath, + "mdx_file_exists": mdx_file_exists, + "raw_url": raw_url, + }, + ) diff --git a/zproject/dev_urls.py b/zproject/dev_urls.py index 9791628427..b9ae272522 100644 --- a/zproject/dev_urls.py +++ b/zproject/dev_urls.py @@ -19,6 +19,7 @@ from zerver.views.development.dev_login import ( dev_direct_login, ) from zerver.views.development.email_log import clear_emails, email_page, generate_all_emails +from zerver.views.development.help import help_dev_mode_view from zerver.views.development.integrations import ( check_send_webhook_fixture_message, dev_panel, @@ -106,6 +107,11 @@ urls = [ path("devtools/buttons/", showroom_component_buttons), path("devtools/banners/", showroom_component_banners), path("devtools/inputs/", showroom_component_inputs), + # Development server for the help center in not run by default, we + # show this page with zulip.com and view source links instead. + path("help", help_dev_mode_view), + path("help/", help_dev_mode_view), + path("help/", help_dev_mode_view), ] v1_api_mobile_patterns = [