chore(sync): cascade main -> dev #776
@@ -64,6 +64,7 @@
|
||||
|
||||
### Fixed
|
||||
- Repo metadata `org` is now **derived from the org profile** (the repository owner) instead of being stored/editable, so it can never drift when an organization is renamed. The `org` column is dropped from `repo_manifest` (migration #368) and the field is derived on read via `Repository.DerivedOrgName` (owner display name, falling back to the handle) across the metadata API, the Settings → Metadata page (now read-only), and the Joomla update-server feed. The API `PUT` and MCP `metadata_update` now ignore `org` (read-only, like the already-derived `display_name`) (#771)
|
||||
- Admin Branding: uploading a custom **Nav Icon** now also sets the **app icon** (`logo.png`, the PWA / web-manifest icon and navbar fallback), so a branded instance shows its own installable app icon instead of the shipped default; resetting the Nav Icon reverts both (#773)
|
||||
- Fork server binary now compiles: `routers/api/v1/api.go` called `organization.HasOrgOrUserVisible`, which had been renamed to `IsOwnerVisibleToDoer`; the one missed call site broke `go build` of the entire `routers/api/v1` package (CI's Lint & Validate does not run a full build, so it went unnoticed) (#735)
|
||||
- Dev deploy workflow: the build/deploy step referenced runner-side values as `\$TAG` / `\$REGISTRY_TOKEN` inside an unquoted SSH heredoc, deferring expansion to the remote shell where those names are unset — the Docker tag collapsed to an empty `mokogitea:` and every dev deploy failed with `invalid reference format`. Runner values are now injected via an ssh env-prefix and the heredoc is quoted so each `$var` expands in exactly one place (#737)
|
||||
- Repaired unit-test compile and `go vet` failures: `CryptoRandomInt/String/Bytes` now return two values (updated `modules/util/util_test.go`), removed a redundant `&&` condition in `issue_comment.go`, and cleaned up isolated integration-test compile errors (#736)
|
||||
|
||||
@@ -137,27 +137,49 @@ func BrandingUpload(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
destPath := filepath.Join(imgDir, filename)
|
||||
dest, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
ctx.Flash.Error("Failed to save image")
|
||||
log.Error("Create %s: %v", destPath, err)
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
|
||||
return
|
||||
}
|
||||
defer dest.Close()
|
||||
|
||||
if _, err := io.Copy(dest, file); err != nil {
|
||||
ctx.Flash.Error("Failed to write image")
|
||||
log.Error("Copy to %s: %v", destPath, err)
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
|
||||
return
|
||||
// The nav icon doubles as the app icon (logo.png) — used as the PWA / web
|
||||
// manifest icon and the navbar fallback — so branding the nav icon brands
|
||||
// the app icon too. Write the uploaded image to both in a single pass. See #773.
|
||||
targets := []string{filename}
|
||||
if imageType == "nav-icon" {
|
||||
targets = append(targets, "logo.png")
|
||||
}
|
||||
|
||||
// Remove SVG override if present
|
||||
svgPath := filepath.Join(imgDir, filename[:len(filename)-4]+".svg")
|
||||
if fileExists(svgPath) {
|
||||
os.Remove(svgPath)
|
||||
// Write the uploaded image to each target independently (rewinding the
|
||||
// stream between targets) so a failure on a later target never leaves an
|
||||
// earlier one truncated/empty.
|
||||
for i, name := range targets {
|
||||
if i > 0 {
|
||||
if _, err := file.Seek(0, io.SeekStart); err != nil {
|
||||
ctx.Flash.Error("Failed to write image")
|
||||
log.Error("Seek branding image %s: %v", name, err)
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
destPath := filepath.Join(imgDir, name)
|
||||
dest, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
ctx.Flash.Error("Failed to save image")
|
||||
log.Error("Create %s: %v", destPath, err)
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
|
||||
return
|
||||
}
|
||||
if _, err := io.Copy(dest, file); err != nil {
|
||||
dest.Close()
|
||||
ctx.Flash.Error("Failed to write image")
|
||||
log.Error("Copy to %s: %v", destPath, err)
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
|
||||
return
|
||||
}
|
||||
dest.Close()
|
||||
|
||||
// Remove any SVG override so the uploaded PNG takes effect.
|
||||
svgPath := filepath.Join(imgDir, name[:len(name)-4]+".svg")
|
||||
if fileExists(svgPath) {
|
||||
os.Remove(svgPath)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Flash.Success("Branding image updated: " + imageType)
|
||||
@@ -183,16 +205,36 @@ func BrandingReset(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
path := filepath.Join(brandingImageDir(), filename)
|
||||
if fileExists(path) {
|
||||
// The nav icon also sets the app icon (logo.png), so resetting it reverts
|
||||
// both back to the built-in default. See #773.
|
||||
targets := []string{filename}
|
||||
if imageType == "nav-icon" {
|
||||
targets = append(targets, "logo.png")
|
||||
}
|
||||
|
||||
removedAny := false
|
||||
failed := false
|
||||
for _, name := range targets {
|
||||
path := filepath.Join(brandingImageDir(), name)
|
||||
if !fileExists(path) {
|
||||
continue
|
||||
}
|
||||
if err := os.Remove(path); err != nil {
|
||||
ctx.Flash.Error("Failed to remove custom image")
|
||||
log.Error("Remove %s: %v", path, err)
|
||||
failed = true
|
||||
} else {
|
||||
ctx.Flash.Success("Reset to default: " + imageType)
|
||||
log.Info("Branding reset to default: %s", filename)
|
||||
removedAny = true
|
||||
log.Info("Branding reset to default: %s", name)
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
switch {
|
||||
case failed:
|
||||
// error flash already set
|
||||
case removedAny:
|
||||
ctx.Flash.Success("Reset to default: " + imageType)
|
||||
default:
|
||||
ctx.Flash.Info("Already using default: " + imageType)
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Nav Icon</strong> {{if .HasNavIcon}}<span class="ui mini green label">Custom</span>{{else}}<span class="ui mini grey label">Default</span>{{end}}
|
||||
<div class="tw-text-text-light tw-text-sm tw-mt-1">Top-left corner across all pages. Square, 30x30px.</div>
|
||||
<div class="tw-text-text-light tw-text-sm tw-mt-1">Top-left corner across all pages. Square, 30x30px. Also sets the app icon (PWA / web manifest); use at least 512x512px for a crisp installed icon.</div>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="{{AppSubUrl}}/-/admin/branding/upload" enctype="multipart/form-data">
|
||||
|
||||
Reference in New Issue
Block a user