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 %}
+
+
+ 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 %}
+
+
+
+
+{% 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 @@
+
+ -
+ To run just the help center dev server without
+ running the rest of the Zulip app, use
+
./tools/run-dev --only-help-center
.
+
+ -
+ To run the help center dev server alongside the
+ Zulip app, use
+
./tools/run-dev --help-center-dev-server
(requires more resources).
+
+ -
+ To test help center search, run
./tools/build-help-center
followed by
+ ./tools/run-dev --help-center-static-build
. Hot reloads won't
+ work: rerun ./tools/build-help-center
+ and reload your browser to see updates. Search will not
+ work with other methods of running the help center.
+
+
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 = [