diff --git a/modules/pages/config.go b/modules/pages/config.go index c6056be35f..dda118768a 100644 --- a/modules/pages/config.go +++ b/modules/pages/config.go @@ -44,6 +44,9 @@ type LandingConfig struct { // CTA section CTASection CTASectionConfig `yaml:"cta_section,omitempty"` + // Blog section + Blog BlogSectionConfig `yaml:"blog,omitempty"` + // Footer Footer FooterConfig `yaml:"footer,omitempty"` @@ -62,9 +65,10 @@ type LandingConfig struct { // BrandConfig represents brand/identity settings type BrandConfig struct { - Name string `yaml:"name,omitempty"` - LogoURL string `yaml:"logo_url,omitempty"` - Tagline string `yaml:"tagline,omitempty"` + Name string `yaml:"name,omitempty"` + LogoURL string `yaml:"logo_url,omitempty"` + LogoSource string `yaml:"logo_source,omitempty"` // "url" (default), "repo", or "org" — selects avatar source + Tagline string `yaml:"tagline,omitempty"` } // HeroConfig represents hero section settings @@ -145,6 +149,16 @@ type CTASectionConfig struct { Button CTAButton `yaml:"button,omitempty"` } +// BlogSectionConfig represents blog section settings on the landing page +type BlogSectionConfig struct { + Enabled bool `yaml:"enabled,omitempty"` + Headline string `yaml:"headline,omitempty"` + Subheadline string `yaml:"subheadline,omitempty"` + MaxPosts int `yaml:"max_posts,omitempty"` // default 3 + ShowExcerpt bool `yaml:"show_excerpt,omitempty"` // show subtitle as excerpt + CTAButton CTAButton `yaml:"cta_button,omitempty"` // "View All Posts" link +} + // FooterConfig represents footer settings type FooterConfig struct { Links []FooterLink `yaml:"links,omitempty"` diff --git a/routers/web/pages/pages.go b/routers/web/pages/pages.go index f5d3cfdaa8..828fe37608 100644 --- a/routers/web/pages/pages.go +++ b/routers/web/pages/pages.go @@ -11,6 +11,7 @@ import ( "strings" "time" + blog_model "code.gitcaddy.com/server/v3/models/blog" "code.gitcaddy.com/server/v3/models/renderhelper" repo_model "code.gitcaddy.com/server/v3/models/repo" "code.gitcaddy.com/server/v3/modules/git" @@ -106,6 +107,12 @@ func renderLandingPage(ctx *context.Context, repo *repo_model.Repository, config ctx.Data["Title"] = getPageTitle(repo, config) ctx.Data["PageIsPagesLanding"] = true + // Provide absolute repo URL for links on custom domains + ctx.Data["RepoURL"] = repo.HTMLURL() + + // Resolve logo URL based on logo_source config + ctx.Data["LogoURL"] = resolveLogoURL(ctx, repo, config) + // Load README content readme, err := loadReadmeContent(ctx, repo) if err != nil { @@ -123,15 +130,54 @@ func renderLandingPage(ctx *context.Context, repo *repo_model.Repository, config if err == nil && release != nil { _ = repo_model.GetReleaseAttachments(ctx, release) ctx.Data["LatestRelease"] = release + // Provide tag name with leading "v" stripped to avoid double "v" in templates + ctx.Data["LatestReleaseTag"] = strings.TrimPrefix(release.TagName, "v") } ctx.Data["PublicReleases"] = config.Advanced.PublicReleases + // Load recent blog posts if blog section is enabled + if config.Blog.Enabled && repo.BlogEnabled { + maxPosts := config.Blog.MaxPosts + if maxPosts <= 0 { + maxPosts = 3 + } + posts, _, err := blog_model.GetBlogPostsByRepoID(ctx, &blog_model.BlogPostSearchOptions{ + RepoID: repo.ID, + AnyPublicStatus: true, + Page: 1, + PageSize: maxPosts, + }) + if err == nil && len(posts) > 0 { + for _, post := range posts { + _ = post.LoadAuthor(ctx) + _ = post.LoadFeaturedImage(ctx) + } + ctx.Data["BlogPosts"] = posts + } + } + // Select template based on config tpl := selectTemplate(config.Template) ctx.HTML(http.StatusOK, tpl) } +// resolveLogoURL determines the logo URL based on the brand.logo_source config +func resolveLogoURL(ctx *context.Context, repo *repo_model.Repository, config *pages_module.LandingConfig) string { + switch config.Brand.LogoSource { + case "repo": + return repo.AvatarLink(ctx) + case "org": + if err := repo.LoadOwner(ctx); err != nil { + log.Warn("Failed to load repo owner for logo: %v", err) + return "" + } + return repo.Owner.AvatarLink(ctx) + default: // "url" or empty + return config.Brand.LogoURL + } +} + // getPageTitle returns the page title func getPageTitle(repo *repo_model.Repository, config *pages_module.LandingConfig) string { if config.SEO.Title != "" { diff --git a/templates/pages/bold-marketing.tmpl b/templates/pages/bold-marketing.tmpl index ea9b892faa..1896fe4ca2 100644 --- a/templates/pages/bold-marketing.tmpl +++ b/templates/pages/bold-marketing.tmpl @@ -1113,7 +1113,8 @@ {{if .Config.ValueProps}}Why{{end}} {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} - + {{if and .Config.Blog.Enabled .BlogPosts}}Blog{{end}} + GitCaddy Repo @@ -1134,7 +1135,8 @@ {{if .Config.ValueProps}}Why{{end}} {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} - + {{if and .Config.Blog.Enabled .BlogPosts}}Blog{{end}} + GitCaddy Repository @@ -1188,12 +1190,12 @@
-

v{{.LatestRelease.TagName}}

+

v{{.LatestReleaseTag}}

Get the latest release

{{range .LatestRelease.Attachments}} - + {{svg "octicon-download" 16}} {{.Name}} {{FileSize .Size}} @@ -1340,6 +1342,44 @@ {{end}} + + {{if and .Config.Blog.Enabled .BlogPosts}} +
+
+ +

{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}

+ {{if .Config.Blog.Subheadline}}

{{.Config.Blog.Subheadline}}

{{end}} +
+
+ {{if .Config.Blog.CTAButton.Label}} + + {{end}} +
+ {{end}} + diff --git a/templates/pages/minimalist-docs.tmpl b/templates/pages/minimalist-docs.tmpl index 068ada407a..0a5bf2dc45 100644 --- a/templates/pages/minimalist-docs.tmpl +++ b/templates/pages/minimalist-docs.tmpl @@ -977,13 +977,14 @@ {{.Label}} {{end}} {{else}} - Docs - API + Docs + API {{end}} {{if .Config.ValueProps}}Why{{end}} {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} - + {{if and .Config.Blog.Enabled .BlogPosts}}Blog{{end}} + GitCaddy Repository @@ -996,13 +997,14 @@ {{.Label}} {{end}} {{else}} - Docs - API + Docs + API {{end}} {{if .Config.ValueProps}}Why{{end}} {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} - + {{if and .Config.Blog.Enabled .BlogPosts}}Blog{{end}} + GitCaddy Repository @@ -1023,7 +1025,7 @@ {{svg "octicon-arrow-right" 14}} {{else}} - + Get Started {{svg "octicon-arrow-right" 14}} @@ -1063,7 +1065,7 @@
{{if .LatestRelease}}
-
v{{.LatestRelease.TagName}}
+
v{{.LatestReleaseTag}}
Latest
{{end}} @@ -1076,11 +1078,11 @@ {{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
-

v{{.LatestRelease.TagName}}

+

v{{.LatestReleaseTag}}

Get the latest release

{{end}} + + {{if and .Config.Blog.Enabled .BlogPosts}} +
+ +

{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}

+ {{if .Config.Blog.Subheadline}}

{{.Config.Blog.Subheadline}}

{{end}} + {{range .BlogPosts}} +
+ {{if .FeaturedImage}} +
+ {{.Title}} +
+ {{end}} +
+
{{.Title}}
+ {{if and $.Config.Blog.ShowExcerpt .Subtitle}} +
{{.Subtitle}}
+ {{end}} +
+ {{if .Author}}{{.Author.Name}}{{end}} · {{DateUtils.AbsoluteShort .CreatedUnix}} +
+
+
+ {{end}} + {{if .Config.Blog.CTAButton.Label}} + + {{end}} +
+ {{end}} +
diff --git a/templates/pages/open-source-hero.tmpl b/templates/pages/open-source-hero.tmpl index be5a99d465..cf24f0c4b8 100644 --- a/templates/pages/open-source-hero.tmpl +++ b/templates/pages/open-source-hero.tmpl @@ -953,8 +953,8 @@