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 <alya@zulip.com>
This commit is contained in:
Shubham Padia
2025-09-11 15:19:23 +00:00
committed by Tim Abbott
parent 3b6d8de815
commit c3fbe00eb0
8 changed files with 241 additions and 32 deletions

View File

@@ -0,0 +1,60 @@
{% extends "zerver/portico.html" %}
{% set entrypoint = "dev-help" %}
{% block title %}
<title>Help center server not running | Zulip Dev</title>
{% endblock %}
{% block portico_content %}
<div class="dev-help-page flex">
<div class="white-box markdown">
{% if mdx_file_exists %}
<h2 class="dev-help-header">Help center server not running</h2>
<p>
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:
</p>
<ul>
<li>
<b>Quick reference</b>: If you aren't planning to modify the help center, you can
<a target="_blank" rel="noopener noreferrer"
href="https://zulip.com/help/{{ subpath }}">view it on
zulip.com</a>. Pages may be slightly behind the version
currently in <code>main</code>.
</li>
<li>
<b>Up-to-date reference</b>: You can
<a target="_blank" rel="noopener noreferrer" href="{{
raw_url }}">view the current raw MDX file</a>, which is recommended for documentation
that touches brand new or recently modified features.
</li>
<li>
<b>Modifying the help center</b>: If you're making changes to
the help center files, be sure to <b>test</b> 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" %}
</li>
</ul>
{% else %}
<p class="invalid-path-error">
This is not a valid help path and not a valid MDX file. Please re-check the URL subpath you have provided.
</p>
{% endif %}
<div class="dev-help-actions">
{% if mdx_file_exists %}
<a target="_blank" rel="noopener noreferrer" href="https://zulip.com/help/{{ subpath }}" class="dev-help-action-button">
View page on zulip.com
</a>
<a target="_blank" rel="noopener noreferrer" href="{{ raw_url }}" class="dev-help-action-button">
View page source
</a>
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -138,38 +138,7 @@
</li>
</ul>
<h2>Development instructions for help center</h2>
<p>
Running <code>./tools/run-dev</code> 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.
</p>
<h3> Dev server (supports hot reload but not search)</h3>
<p>
This mode is useful when you are editing a help center file, and want to visualize the changes
quickly in the help center documentation.
</p>
<p>
<code>./tools/run-dev --only-help-center</code> will run a dev server at
<code>/help</code> 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.
</p>
<p>
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
<code>./tools/run-dev --help-center-dev-server</code>.
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).
</p>
<h3> Serve static build (supports search but not hot reload)</h3>
<p>
Please run <code>./tools/build-help-center</code> to generate a static build of the help center.
<code>./tools/run-dev --help-center-static-build</code> will host the generated build on
<code>/help</code>. Note that you need to generate a build and pass the flag mentioned for the search
to work.
</p>
{% include "zerver/development/help_center_instructions_macro.html" %}
</div>
</div>

View File

@@ -0,0 +1,19 @@
<ul>
<li>
To run just the help center dev server without
running the rest of the Zulip app, use
<code>./tools/run-dev --only-help-center</code>.
</li>
<li>
To run the help center dev server alongside the
Zulip app, use
<code>./tools/run-dev --help-center-dev-server</code> (requires more resources).
</li>
<li>
To test help center search, run <code>./tools/build-help-center</code> followed by
<code>./tools/run-dev --help-center-static-build</code>. Hot reloads won't
work: rerun <code>./tools/build-help-center</code>
and reload your browser to see updates. Search will not
work with other methods of running the help center.
</li>
</ul>

View File

@@ -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%);
}
}
}
}

View File

@@ -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"]
}

View File

@@ -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"])

View File

@@ -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,
},
)

View File

@@ -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/<path:subpath>", help_dev_mode_view),
]
v1_api_mobile_patterns = [