diff --git a/modules/pages/config.go b/modules/pages/config.go index 1c3e0be0ad..16c208324b 100644 --- a/modules/pages/config.go +++ b/modules/pages/config.go @@ -47,6 +47,9 @@ type LandingConfig struct { // Blog section Blog BlogSectionConfig `yaml:"blog,omitempty"` + // Gallery section + Gallery GallerySectionConfig `yaml:"gallery,omitempty"` + // Navigation visibility Navigation NavigationConfig `yaml:"navigation,omitempty"` @@ -196,6 +199,15 @@ type BlogSectionConfig struct { CTAButton CTAButton `yaml:"cta_button,omitempty"` // "View All Posts" link } +// GallerySectionConfig represents gallery section settings on the landing page +type GallerySectionConfig struct { + Enabled bool `yaml:"enabled,omitempty"` + Headline string `yaml:"headline,omitempty"` + Subheadline string `yaml:"subheadline,omitempty"` + MaxImages int `yaml:"max_images,omitempty"` // default 6 + Columns int `yaml:"columns,omitempty"` // grid columns, default 3 +} + // NavigationConfig controls which built-in navigation links appear in the header and footer type NavigationConfig struct { ShowDocs bool `yaml:"show_docs,omitempty"` diff --git a/options/locale/custom_keys.json b/options/locale/custom_keys.json index a06306e17a..39e36900b7 100644 --- a/options/locale/custom_keys.json +++ b/options/locale/custom_keys.json @@ -737,6 +737,13 @@ "repo.settings.pages.stats": "Stats", "repo.settings.pages.value_props": "Value Propositions", "repo.settings.pages.features": "Features", + "repo.settings.pages.gallery_section": "Gallery Section", + "repo.settings.pages.gallery_enabled_desc": "Show a gallery of images from your repository's .gallery folder on the landing page", + "repo.settings.pages.gallery_headline": "Gallery Headline", + "repo.settings.pages.gallery_subheadline": "Gallery Subheadline", + "repo.settings.pages.gallery_max_images": "Maximum Images to Show", + "repo.settings.pages.gallery_columns": "Grid Columns", + "repo.settings.pages.gallery_help_link": "Upload and manage gallery images in Settings > Gallery.", "repo.settings.pages.company_logos": "Company Logos", "repo.settings.pages.testimonials": "Testimonials", "repo.settings.pages.pricing_headline": "Pricing Headline", diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index ee331c2da1..0d9a18d5e8 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -4544,6 +4544,13 @@ "repo.settings.pages.blog_headline": "Blog Headline", "repo.settings.pages.blog_subheadline": "Blog Subheadline", "repo.settings.pages.blog_max_posts": "Maximum Posts to Show", + "repo.settings.pages.gallery_section": "Gallery Section", + "repo.settings.pages.gallery_enabled_desc": "Show a gallery of images from your repository's .gallery folder on the landing page", + "repo.settings.pages.gallery_headline": "Gallery Headline", + "repo.settings.pages.gallery_subheadline": "Gallery Subheadline", + "repo.settings.pages.gallery_max_images": "Maximum Images to Show", + "repo.settings.pages.gallery_columns": "Grid Columns", + "repo.settings.pages.gallery_help_link": "Upload and manage gallery images in Settings > Gallery.", "repo.settings.pages.ai_generate": "AI Content Generator", "repo.settings.pages.ai_generate_desc": "Automatically generate landing page content (headline, features, stats, CTAs) from your repository's README and metadata using AI.", "repo.settings.pages.ai_generate_button": "Generate Content with AI", diff --git a/routers/web/pages/pages.go b/routers/web/pages/pages.go index dd64068d9c..3e352db963 100644 --- a/routers/web/pages/pages.go +++ b/routers/web/pages/pages.go @@ -23,6 +23,7 @@ import ( "code.gitcaddy.com/server/v3/models/renderhelper" repo_model "code.gitcaddy.com/server/v3/models/repo" "code.gitcaddy.com/server/v3/modules/git" + "code.gitcaddy.com/server/v3/modules/gitrepo" "code.gitcaddy.com/server/v3/modules/json" "code.gitcaddy.com/server/v3/modules/log" "code.gitcaddy.com/server/v3/modules/markup/markdown" @@ -196,6 +197,14 @@ func renderLandingPage(ctx *context.Context, repo *repo_model.Repository, config } } + // Load gallery images if gallery section is enabled + if config.Gallery.Enabled { + images := loadGalleryImagesForLanding(ctx, repo, config) + if len(images) > 0 { + ctx.Data["GalleryImages"] = images + } + } + tpl := selectTemplate(config.Template) ctx.HTML(http.StatusOK, tpl) } @@ -315,6 +324,94 @@ func getPageTitle(repo *repo_model.Repository, config *pages_module.LandingConfi return repo.Name } +// GalleryImageInfo holds gallery image data for landing page templates +type GalleryImageInfo struct { + Name string + Caption string + URL string +} + +// loadGalleryImagesForLanding reads gallery images from the .gallery folder +func loadGalleryImagesForLanding(ctx *context.Context, repo *repo_model.Repository, config *pages_module.LandingConfig) []GalleryImageInfo { + if repo.IsEmpty { + return nil + } + + gitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo) + if err != nil { + return nil + } + + commit, err := gitRepo.GetBranchCommit(repo.DefaultBranch) + if err != nil { + return nil + } + + galleryEntry, err := commit.GetTreeEntryByPath(".gallery") + if err != nil || !galleryEntry.IsDir() { + return nil + } + + // Load metadata for captions + var metadata struct { + Images []struct { + Name string `json:"name"` + Caption string `json:"caption"` + } `json:"images"` + } + if entry, err := commit.GetTreeEntryByPath(".gallery/gallery.json"); err == nil { + if content, err := entry.Blob().GetBlobContent(100000); err == nil { + _ = json.Unmarshal([]byte(content), &metadata) + } + } + captionMap := make(map[string]string) + for _, img := range metadata.Images { + captionMap[img.Name] = img.Caption + } + + tree, err := commit.SubTree(".gallery") + if err != nil { + return nil + } + + entries, err := tree.ListEntries() + if err != nil { + return nil + } + + maxImages := config.Gallery.MaxImages + if maxImages <= 0 { + maxImages = 6 + } + + var images []GalleryImageInfo + for _, entry := range entries { + if entry.IsDir() { + continue + } + name := entry.Name() + if name == "gallery.json" { + continue + } + ext := strings.ToLower(path.Ext(name)) + switch ext { + case ".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".bmp": + default: + continue + } + images = append(images, GalleryImageInfo{ + Name: name, + Caption: captionMap[name], + URL: repo.Link() + "/raw/" + repo.DefaultBranch + "/.gallery/" + name, + }) + if len(images) >= maxImages { + break + } + } + + return images +} + // loadReadmeContent loads and renders the README content func loadReadmeContent(ctx *context.Context, repo *repo_model.Repository) (template.HTML, error) { gitRepo, err := git.OpenRepository(ctx, repo.RepoPath()) diff --git a/routers/web/repo/setting/pages.go b/routers/web/repo/setting/pages.go index 14dfe6dc68..bd2479442d 100644 --- a/routers/web/repo/setting/pages.go +++ b/routers/web/repo/setting/pages.go @@ -545,6 +545,15 @@ func PagesContentPost(ctx *context.Context) { if maxPosts := ctx.FormInt("blog_max_posts"); maxPosts > 0 { config.Blog.MaxPosts = maxPosts } + config.Gallery.Enabled = ctx.FormBool("gallery_enabled") + config.Gallery.Headline = ctx.FormString("gallery_headline") + config.Gallery.Subheadline = ctx.FormString("gallery_subheadline") + if maxImages := ctx.FormInt("gallery_max_images"); maxImages > 0 { + config.Gallery.MaxImages = maxImages + } + if cols := ctx.FormInt("gallery_columns"); cols >= 2 && cols <= 4 { + config.Gallery.Columns = cols + } config.Stats = nil for i := range 10 { value := ctx.FormString(fmt.Sprintf("stat_value_%d", i)) diff --git a/templates/pages/bold-marketing.tmpl b/templates/pages/bold-marketing.tmpl index cb6e7d1552..b8a9a2ad9f 100644 --- a/templates/pages/bold-marketing.tmpl +++ b/templates/pages/bold-marketing.tmpl @@ -1136,6 +1136,7 @@ {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} {{if .Config.Blog.Enabled}}Blog{{end}} + {{if .Config.Gallery.Enabled}}Gallery{{end}} {{if .Config.Navigation.ShowRepository}} GitCaddy @@ -1174,6 +1175,7 @@ {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} {{if .Config.Blog.Enabled}}Blog{{end}} + {{if .Config.Gallery.Enabled}}Gallery{{end}} {{if .Config.Navigation.ShowRepository}} GitCaddy @@ -1481,6 +1483,29 @@ {{end}} + + {{if and .Config.Gallery.Enabled .GalleryImages}} + + {{end}} + {{end}}{{/* end PageIsBlogDetail / PageIsBlogList / else */}} diff --git a/templates/pages/minimalist-docs.tmpl b/templates/pages/minimalist-docs.tmpl index 42f2d9cdb9..4a3bd83096 100644 --- a/templates/pages/minimalist-docs.tmpl +++ b/templates/pages/minimalist-docs.tmpl @@ -1003,6 +1003,7 @@ {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} {{if .Config.Blog.Enabled}}Blog{{end}} + {{if .Config.Gallery.Enabled}}Gallery{{end}} {{if .Config.Navigation.ShowRepository}} GitCaddy @@ -1036,6 +1037,7 @@ {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} {{if .Config.Blog.Enabled}}Blog{{end}} + {{if .Config.Gallery.Enabled}}Gallery{{end}} {{if .Config.Navigation.ShowRepository}} GitCaddy @@ -1355,6 +1357,27 @@ {{end}} + + {{if and .Config.Gallery.Enabled .GalleryImages}} + + {{end}} + {{end}}{{/* end PageIsBlogDetail / PageIsBlogList / else */}} diff --git a/templates/pages/open-source-hero.tmpl b/templates/pages/open-source-hero.tmpl index b12674d362..e7b523534b 100644 --- a/templates/pages/open-source-hero.tmpl +++ b/templates/pages/open-source-hero.tmpl @@ -994,6 +994,7 @@ {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} {{if .Config.Blog.Enabled}}Blog{{end}} + {{if .Config.Gallery.Enabled}}Gallery{{end}} {{if .Config.Navigation.ShowRepository}} GitCaddy @@ -1352,6 +1353,31 @@ {{end}} + + {{if and .Config.Gallery.Enabled .GalleryImages}} + + {{end}} + {{end}}{{/* end PageIsBlogDetail / PageIsBlogList / else */}} diff --git a/templates/pages/saas-conversion.tmpl b/templates/pages/saas-conversion.tmpl index c0f710e08b..bf7b242882 100644 --- a/templates/pages/saas-conversion.tmpl +++ b/templates/pages/saas-conversion.tmpl @@ -1111,6 +1111,7 @@ {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} {{if .Config.Blog.Enabled}}Blog{{end}} + {{if .Config.Gallery.Enabled}}Gallery{{end}} {{if .Config.Navigation.ShowRepository}} GitCaddy @@ -1147,6 +1148,7 @@ {{if .Config.Features}}Features{{end}} {{if .Config.Pricing.Plans}}Pricing{{end}} {{if .Config.Blog.Enabled}}Blog{{end}} + {{if .Config.Gallery.Enabled}}Gallery{{end}} {{if .Config.Navigation.ShowRepository}} GitCaddy @@ -1491,6 +1493,30 @@ {{end}} + + {{if and .Config.Gallery.Enabled .GalleryImages}} + + {{end}} + {{end}}{{/* end PageIsBlogDetail / PageIsBlogList / else */}} diff --git a/templates/repo/settings/pages_content.tmpl b/templates/repo/settings/pages_content.tmpl index eecdfef27a..c32eb713f2 100644 --- a/templates/repo/settings/pages_content.tmpl +++ b/templates/repo/settings/pages_content.tmpl @@ -65,6 +65,37 @@ +
{{ctx.Locale.Tr "repo.settings.pages.gallery_section"}}
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+

{{ctx.Locale.Tr "repo.settings.pages.gallery_help_link"}}

+
{{ctx.Locale.Tr "repo.settings.pages.stats"}}
{{range $i, $stat := .Config.Stats}}