fix(ui): restore brand across anonymous and error pages
giteabot / giteabot (push) Canceled after 0s
giteabot backport / giteabot (push) Canceled after 0s
release-nightly-snapcraft / build-and-publish (push) Canceled after 0s
release-nightly / nightly-binary (push) Canceled after 0s
release-nightly / nightly-container (push) Canceled after 0s
giteabot / giteabot (push) Canceled after 0s
giteabot backport / giteabot (push) Canceled after 0s
release-nightly-snapcraft / build-and-publish (push) Canceled after 0s
release-nightly / nightly-binary (push) Canceled after 0s
release-nightly / nightly-container (push) Canceled after 0s
Assisted-by: Codex:GPT-5
This commit is contained in:
@@ -24,6 +24,11 @@ import (
|
||||
const tplStatus500 templates.TplName = "status/500"
|
||||
|
||||
func renderServerErrorPage(w http.ResponseWriter, req *http.Request, respCode int, tmpl templates.TplName, ctxData map[string]any, plainMsg string) {
|
||||
if ctxData == nil {
|
||||
ctxData = map[string]any{}
|
||||
}
|
||||
ctxData["ShowNavbarBrand"] = true
|
||||
|
||||
acceptsHTML := false
|
||||
for _, part := range req.Header["Accept"] {
|
||||
if strings.Contains(part, "text/html") {
|
||||
|
||||
@@ -27,12 +27,21 @@ func TestRenderPanicErrorPage(t *testing.T) {
|
||||
assert.Contains(t, respContent, `class="page-content status-page-500"`)
|
||||
assert.Contains(t, respContent, `</html>`)
|
||||
assert.Contains(t, respContent, `lang="en-US"`) // make sure the locale work
|
||||
assertNavbarBrand(t, respContent)
|
||||
|
||||
// the 500 page doesn't have normal pages footer, it makes it easier to distinguish a normal page and a failed page.
|
||||
// especially when a sub-template causes page error, the HTTP response code is still 200,
|
||||
// the different "footer" is the only way to know whether a page is fully rendered without error.
|
||||
assert.False(t, test.IsNormalPageCompleted(respContent))
|
||||
})
|
||||
t.Run("ServiceUnavailableHTML", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := &http.Request{URL: &url.URL{}, Header: http.Header{"Accept": []string{"text/html"}}}
|
||||
req = req.WithContext(reqctx.NewRequestContextForTest(t.Context()))
|
||||
renderServiceUnavailable(w, req)
|
||||
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
||||
assertNavbarBrand(t, w.Body.String())
|
||||
})
|
||||
t.Run("Plain", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := &http.Request{URL: &url.URL{}}
|
||||
@@ -42,6 +51,13 @@ func TestRenderPanicErrorPage(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func assertNavbarBrand(t *testing.T, content string) {
|
||||
t.Helper()
|
||||
assert.Contains(t, content, `class="navbar-brand-copy"`)
|
||||
assert.Contains(t, content, "荣刻科技")
|
||||
assert.Contains(t, content, "RONCARVE TECHNOLOGY")
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m)
|
||||
}
|
||||
|
||||
@@ -166,6 +166,7 @@ func (ctx *Context) notFoundInternal(logMsg string, logErr error) {
|
||||
ctx.Data["IsRepo"] = ctx.Repo.Repository != nil
|
||||
ctx.Data["Title"] = "Page Not Found"
|
||||
ctx.Data["ErrorMsg"] = "" // FIXME: the template never renders this message, need to fix in the future (and show safe messages to end users)
|
||||
ctx.Data["ShowNavbarBrand"] = true
|
||||
ctx.HTML(http.StatusNotFound, "status/404")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<nav id="navbar" aria-label="{{ctx.Locale.Tr "aria.navbar"}}">
|
||||
<div class="navbar-left">
|
||||
<!-- the logo -->
|
||||
<a class="item" id="navbar-logo" href="{{AppSubUrl}}/" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home_title"}}{{end}}">
|
||||
<a class="item navbar-brand-link" id="navbar-logo" href="{{AppSubUrl}}/" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home_title"}}{{end}}">
|
||||
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.png" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
|
||||
{{if or .PageIsSignIn (eq .Title (ctx.Locale.Tr "auth.forgot_password_title")) (eq .Title (ctx.Locale.Tr "auth.reset_password")) (eq .Title (ctx.Locale.Tr "auth.prohibit_login"))}}
|
||||
<span class="navbar-brand-copy" aria-hidden="true">
|
||||
<strong>荣刻科技</strong>
|
||||
<small>RONCARVE TECHNOLOGY</small>
|
||||
</span>
|
||||
{{if or (not .IsSigned) .ShowNavbarBrand}}
|
||||
{{template "base/navbar_brand"}}
|
||||
{{end}}
|
||||
</a>
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<span class="navbar-brand-copy" aria-hidden="true">
|
||||
<strong>荣刻科技</strong>
|
||||
<small>RONCARVE TECHNOLOGY</small>
|
||||
</span>
|
||||
@@ -20,8 +20,9 @@
|
||||
<nav class="ui secondary menu">
|
||||
<div class="ui container tw-flex">
|
||||
<div class="item tw-flex-1">
|
||||
<a href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home_title"}}">
|
||||
<a class="navbar-brand-link tw-flex tw-items-center" href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home_title"}}">
|
||||
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.png" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
|
||||
{{template "base/navbar_brand"}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
|
||||
@@ -25,6 +25,7 @@ func TestView(t *testing.T) {
|
||||
t.Run("SecurityHeadersDefaults", testSecurityHeadersDefaults)
|
||||
t.Run("SiteManifest", testSiteManifest)
|
||||
t.Run("CurrentURL", testViewPageCurrentURL)
|
||||
t.Run("NavbarBrandVisibility", testNavbarBrandVisibility)
|
||||
}
|
||||
|
||||
func testRenderFileSVGIsInImgTag(t *testing.T) {
|
||||
@@ -129,3 +130,24 @@ func testViewPageCurrentURL(t *testing.T) {
|
||||
MakeRequest(t, NewRequest(t, "GET", "/any-page?k=v"), http.StatusNotFound)
|
||||
assert.Equal(t, "/subpath/any-page?k=v", currentURL)
|
||||
}
|
||||
|
||||
func testNavbarBrandVisibility(t *testing.T) {
|
||||
assertBrand := func(t *testing.T, session *TestSession, uri string, status int, visible bool) {
|
||||
t.Helper()
|
||||
resp := session.MakeRequest(t, NewRequest(t, "GET", uri), status)
|
||||
doc := NewHTMLParser(t, resp.Body)
|
||||
brand := strings.Join(strings.Fields(doc.doc.Find("#navbar-logo .navbar-brand-copy").Text()), " ")
|
||||
if visible {
|
||||
assert.Equal(t, "荣刻科技 RONCARVE TECHNOLOGY", brand)
|
||||
} else {
|
||||
assert.Empty(t, brand)
|
||||
}
|
||||
}
|
||||
|
||||
assertBrand(t, emptyTestSession(t), "/user/login", http.StatusOK, true)
|
||||
assertBrand(t, emptyTestSession(t), "/brand-page-does-not-exist", http.StatusNotFound, true)
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
assertBrand(t, session, "/user2", http.StatusOK, false)
|
||||
assertBrand(t, session, "/brand-page-does-not-exist", http.StatusNotFound, true)
|
||||
}
|
||||
|
||||
@@ -166,26 +166,26 @@ h4.ui.header .sub.header {
|
||||
padding: 0.5em 0.75em !important;
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
#navbar-logo {
|
||||
.navbar-brand-link {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#navbar-logo .navbar-brand-copy {
|
||||
.navbar-brand-link .navbar-brand-copy {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
#navbar-logo .navbar-brand-copy strong {
|
||||
color: #071a50;
|
||||
.navbar-brand-link .navbar-brand-copy strong {
|
||||
color: var(--color-primary-dark-1);
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
font-weight: var(--font-weight-bold);
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
#navbar-logo .navbar-brand-copy small {
|
||||
.navbar-brand-link .navbar-brand-copy small {
|
||||
margin-top: 5px;
|
||||
color: #75809a;
|
||||
color: var(--color-text-light-2);
|
||||
font-family: var(--fonts-monospace);
|
||||
font-size: 8px;
|
||||
letter-spacing: 0.2em;
|
||||
@@ -193,11 +193,11 @@ h4.ui.header .sub.header {
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
#navbar-logo .navbar-brand-copy strong {
|
||||
.navbar-brand-link .navbar-brand-copy strong {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#navbar-logo .navbar-brand-copy small {
|
||||
.navbar-brand-link .navbar-brand-copy small {
|
||||
font-size: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user