fix(blog): allow public access to blog featured images
Some checks failed
Build and Release / Unit Tests (push) Successful in 3m32s
Build and Release / Create Release (push) Successful in 0s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 8m1s
Build and Release / Lint (push) Failing after 8m13s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Has been skipped
Build and Release / Build Binaries (amd64, darwin, macos) (push) Has been skipped
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Has been skipped
Build and Release / Build Binaries (arm64, darwin, macos) (push) Has been skipped
Build and Release / Build Binary (linux/arm64) (push) Has been skipped
Some checks failed
Build and Release / Unit Tests (push) Successful in 3m32s
Build and Release / Create Release (push) Successful in 0s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 8m1s
Build and Release / Lint (push) Failing after 8m13s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Has been skipped
Build and Release / Build Binaries (amd64, darwin, macos) (push) Has been skipped
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Has been skipped
Build and Release / Build Binaries (arm64, darwin, macos) (push) Has been skipped
Build and Release / Build Binary (linux/arm64) (push) Has been skipped
Allow public access to blog post featured images even when repository is private, if the post is published and blog is enabled. This supports public landing pages with blog sections that display featured images. Adds IsPublishedBlogFeaturedImage query and isBlogFeaturedImage check in attachment serving. Also removes redundant SafeHTML filter from blog content templates (already HTML-safe).
This commit is contained in:
@@ -420,6 +420,14 @@ func HasSubscriptionOnlyBlogPosts(ctx context.Context, repoID int64) (bool, erro
|
|||||||
return count > 0, err
|
return count > 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPublishedBlogFeaturedImage checks if the given attachment ID is used as a
|
||||||
|
// featured image by any published blog post.
|
||||||
|
func IsPublishedBlogFeaturedImage(ctx context.Context, attachmentID int64) (bool, error) {
|
||||||
|
return db.GetEngine(ctx).
|
||||||
|
Where("featured_image_id = ? AND status >= ?", attachmentID, BlogPostPublic).
|
||||||
|
Exist(new(BlogPost))
|
||||||
|
}
|
||||||
|
|
||||||
// CreateBlogPost inserts a new blog post.
|
// CreateBlogPost inserts a new blog post.
|
||||||
func CreateBlogPost(ctx context.Context, p *BlogPost) error {
|
func CreateBlogPost(ctx context.Context, p *BlogPost) error {
|
||||||
_, err := db.GetEngine(ctx).Insert(p)
|
_, err := db.GetEngine(ctx).Insert(p)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
blog_model "code.gitcaddy.com/server/v3/models/blog"
|
||||||
access_model "code.gitcaddy.com/server/v3/models/perm/access"
|
access_model "code.gitcaddy.com/server/v3/models/perm/access"
|
||||||
repo_model "code.gitcaddy.com/server/v3/models/repo"
|
repo_model "code.gitcaddy.com/server/v3/models/repo"
|
||||||
"code.gitcaddy.com/server/v3/models/unit"
|
"code.gitcaddy.com/server/v3/models/unit"
|
||||||
@@ -122,8 +123,12 @@ func ServeAttachment(ctx *context.Context, uuid string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !perm.CanRead(unit.TypeCode) {
|
if !perm.CanRead(unit.TypeCode) {
|
||||||
ctx.HTTPError(http.StatusNotFound)
|
// Allow public access to blog featured images when the repo has blog enabled,
|
||||||
return
|
// even if the repo is private. This supports public landing pages with blog sections.
|
||||||
|
if !isBlogFeaturedImage(ctx, attach) {
|
||||||
|
ctx.HTTPError(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if !(ctx.IsSigned && attach.UploaderID == ctx.Doer.ID) { // We block if not the uploader
|
} else if !(ctx.IsSigned && attach.UploaderID == ctx.Doer.ID) { // We block if not the uploader
|
||||||
ctx.HTTPError(http.StatusNotFound)
|
ctx.HTTPError(http.StatusNotFound)
|
||||||
@@ -171,6 +176,26 @@ func ServeAttachment(ctx *context.Context, uuid string) {
|
|||||||
common.ServeContentByReadSeeker(ctx.Base, attach.Name, util.ToPointer(attach.CreatedUnix.AsTime()), fr)
|
common.ServeContentByReadSeeker(ctx.Base, attach.Name, util.ToPointer(attach.CreatedUnix.AsTime()), fr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isBlogFeaturedImage checks if the attachment is used as a featured image for a
|
||||||
|
// published blog post on a repo that has blog enabled. This allows public access
|
||||||
|
// to these images even when the repo is private, supporting public landing pages.
|
||||||
|
func isBlogFeaturedImage(ctx *context.Context, attach *repo_model.Attachment) bool {
|
||||||
|
if attach.RepoID == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
repo, err := repo_model.GetRepositoryByID(ctx, attach.RepoID)
|
||||||
|
if err != nil || !repo.BlogEnabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Check if any published blog post uses this attachment as its featured image
|
||||||
|
has, err := blog_model.IsPublishedBlogFeaturedImage(ctx, attach.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("isBlogFeaturedImage: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return has
|
||||||
|
}
|
||||||
|
|
||||||
// GetAttachment serve attachments
|
// GetAttachment serve attachments
|
||||||
func GetAttachment(ctx *context.Context) {
|
func GetAttachment(ctx *context.Context) {
|
||||||
ServeAttachment(ctx, ctx.PathParam("uuid"))
|
ServeAttachment(ctx, ctx.PathParam("uuid"))
|
||||||
|
|||||||
@@ -1201,7 +1201,7 @@
|
|||||||
{{if .BlogTags}}<span>·</span>{{range .BlogTags}}<span style="background: var(--nb-surface); border: 1px solid var(--nb-border-hard); padding: 2px 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em;">{{.}}</span> {{end}}{{end}}
|
{{if .BlogTags}}<span>·</span>{{range .BlogTags}}<span style="background: var(--nb-surface); border: 1px solid var(--nb-border-hard); padding: 2px 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em;">{{.}}</span> {{end}}{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="markup nb-blog-content" style="color: var(--nb-text); line-height: 1.8; font-size: 16px; text-align: left;">
|
<div class="markup nb-blog-content" style="color: var(--nb-text); line-height: 1.8; font-size: 16px; text-align: left;">
|
||||||
{{.BlogRenderedContent | SafeHTML}}
|
{{.BlogRenderedContent}}
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 48px; padding-top: 24px; border-top: 2px solid var(--nb-border-hard);">
|
<div style="margin-top: 48px; padding-top: 24px; border-top: 2px solid var(--nb-border-hard);">
|
||||||
<a href="{{.BlogBaseURL}}" class="nb-btn-secondary" data-cta="secondary" style="text-decoration: none;">
|
<a href="{{.BlogBaseURL}}" class="nb-btn-secondary" data-cta="secondary" style="text-decoration: none;">
|
||||||
|
|||||||
@@ -1063,7 +1063,7 @@
|
|||||||
{{if .BlogTags}}<span>·</span>{{range .BlogTags}}<span style="background: var(--ea-surface); padding: 2px 8px; border-radius: 3px; font-size: 12px; color: var(--ea-accent);">{{.}}</span> {{end}}{{end}}
|
{{if .BlogTags}}<span>·</span>{{range .BlogTags}}<span style="background: var(--ea-surface); padding: 2px 8px; border-radius: 3px; font-size: 12px; color: var(--ea-accent);">{{.}}</span> {{end}}{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="markup ea-blog-content" style="color: var(--ea-text); line-height: 1.8; font-size: 16px; font-family: 'Cormorant Garamond', serif;">
|
<div class="markup ea-blog-content" style="color: var(--ea-text); line-height: 1.8; font-size: 16px; font-family: 'Cormorant Garamond', serif;">
|
||||||
{{.BlogRenderedContent | SafeHTML}}
|
{{.BlogRenderedContent}}
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--ea-border);">
|
<div style="margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--ea-border);">
|
||||||
<a href="{{.BlogBaseURL}}" class="ea-btn-text" style="text-decoration: none;">
|
<a href="{{.BlogBaseURL}}" class="ea-btn-text" style="text-decoration: none;">
|
||||||
|
|||||||
@@ -1036,7 +1036,7 @@
|
|||||||
{{if .BlogTags}}<span>·</span>{{range .BlogTags}}<span style="background: var(--osh-glow); padding: 2px 8px; border-radius: 4px; font-size: 12px;">{{.}}</span> {{end}}{{end}}
|
{{if .BlogTags}}<span>·</span>{{range .BlogTags}}<span style="background: var(--osh-glow); padding: 2px 8px; border-radius: 4px; font-size: 12px;">{{.}}</span> {{end}}{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="markup osh-blog-content" style="color: var(--osh-text); line-height: 1.8; font-size: 16px; text-align: left;">
|
<div class="markup osh-blog-content" style="color: var(--osh-text); line-height: 1.8; font-size: 16px; text-align: left;">
|
||||||
{{.BlogRenderedContent | SafeHTML}}
|
{{.BlogRenderedContent}}
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 48px; padding-top: 24px; border-top: 1px solid rgba(255,255,255,0.06);">
|
<div style="margin-top: 48px; padding-top: 24px; border-top: 1px solid rgba(255,255,255,0.06);">
|
||||||
<a href="{{.BlogBaseURL}}" class="osh-btn-secondary" data-cta="secondary" style="text-decoration: none;">
|
<a href="{{.BlogBaseURL}}" class="osh-btn-secondary" data-cta="secondary" style="text-decoration: none;">
|
||||||
|
|||||||
@@ -1174,7 +1174,7 @@
|
|||||||
{{if .BlogTags}}<span>·</span>{{range .BlogTags}}<span style="background: var(--gm-glass); border: 1px solid var(--gm-glass-border); padding: 2px 8px; border-radius: 20px; font-size: 12px;">{{.}}</span> {{end}}{{end}}
|
{{if .BlogTags}}<span>·</span>{{range .BlogTags}}<span style="background: var(--gm-glass); border: 1px solid var(--gm-glass-border); padding: 2px 8px; border-radius: 20px; font-size: 12px;">{{.}}</span> {{end}}{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="markup gm-blog-content" style="color: var(--gm-text); line-height: 1.8; font-size: 16px; text-align: left;">
|
<div class="markup gm-blog-content" style="color: var(--gm-text); line-height: 1.8; font-size: 16px; text-align: left;">
|
||||||
{{.BlogRenderedContent | SafeHTML}}
|
{{.BlogRenderedContent}}
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--gm-glass-border);">
|
<div style="margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--gm-glass-border);">
|
||||||
<a href="{{.BlogBaseURL}}" class="gm-btn-secondary" data-cta="secondary" style="text-decoration: none;">
|
<a href="{{.BlogBaseURL}}" class="gm-btn-secondary" data-cta="secondary" style="text-decoration: none;">
|
||||||
|
|||||||
Reference in New Issue
Block a user