fix: resolve translated post slugs (D-58)
GET /_journal/api/v1/posts/{slug} now looks the slug up in translate's
slug index for the request locale after the default-locale slug and
before the numeric id. TestTranslatedSlug covers the translated slug,
the default slug, the id and an unknown slug.
This commit is contained in:
@@ -128,7 +128,7 @@ func Show(app *backpack.App) http.HandlerFunc {
|
||||
}
|
||||
db = db.WithContext(r.Context())
|
||||
slug := strings.TrimSpace(r.PathValue("slug"))
|
||||
post, err := findPostBySlugOrID(db, slug)
|
||||
post, err := findPostBySlugOrID(db, slug, requestLocale(r))
|
||||
if err != nil || post == nil {
|
||||
writeAPIError(w, http.StatusNotFound, errPostNotFound)
|
||||
return
|
||||
|
||||
@@ -16,8 +16,10 @@ import (
|
||||
"git.golem15.com/golem15/sm-journal-plugin/classes"
|
||||
"git.golem15.com/golem15/sm-journal-plugin/models"
|
||||
translate "git.golem15.com/golem15/sm-translate-plugin/classes"
|
||||
translatemodels "git.golem15.com/golem15/sm-translate-plugin/models"
|
||||
"git.golem15.com/golem15/summercms/modules/bouncer"
|
||||
"git.golem15.com/golem15/summercms/modules/cabana"
|
||||
"git.golem15.com/golem15/summercms/modules/towel"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -52,12 +54,29 @@ func ctypeDigit(s string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func findPostBySlugOrID(db *gorm.DB, slug string) (*models.Post, error) {
|
||||
// findPostBySlugOrID finds a post by its default-locale slug, then by its
|
||||
// slug in locale (the request locale, D-58: translate's slug index), then
|
||||
// by numeric id. Drafts are found too; the caller decides who may see them.
|
||||
func findPostBySlugOrID(db *gorm.DB, slug, locale string) (*models.Post, error) {
|
||||
q := postQuery(db)
|
||||
var post models.Post
|
||||
if err := q.Where("slug = ?", slug).Take(&post).Error; err == nil {
|
||||
return &post, nil
|
||||
}
|
||||
if locale != "" && slug != "" {
|
||||
var ids []string
|
||||
err := db.Session(&gorm.Session{NewDB: true, Context: db.Statement.Context}).
|
||||
Table(translatemodels.Index{}.TableName()).
|
||||
Where("model_type = ? AND locale = ? AND item = ? AND value = ?", models.Post{}.MorphName(), locale, "slug", slug).
|
||||
Order("id ASC").
|
||||
Pluck("model_id", &ids).Error
|
||||
if err == nil && len(ids) > 0 {
|
||||
var translated models.Post
|
||||
if err := postQuery(db).Where("CAST(id AS TEXT) IN ?", ids).Order("id ASC").Take(&translated).Error; err == nil {
|
||||
return &translated, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if ctypeDigit(slug) {
|
||||
id, err := strconv.ParseUint(slug, 10, 64)
|
||||
if err == nil && id > 0 {
|
||||
@@ -780,3 +799,21 @@ func syncTranslations(ctx context.Context, db *gorm.DB, post *models.Post, raw a
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// requestLocale is the request's locale (towel, set by the router from the
|
||||
// locale resolver or Accept-Language) as a lower-case language code: the
|
||||
// first language of an Accept-Language list, without its region.
|
||||
func requestLocale(r *http.Request) string {
|
||||
if r == nil {
|
||||
return ""
|
||||
}
|
||||
raw, _ := towel.Locale(r.Context())
|
||||
raw = strings.TrimSpace(raw)
|
||||
if i := strings.IndexAny(raw, ",;"); i >= 0 {
|
||||
raw = raw[:i]
|
||||
}
|
||||
if i := strings.IndexAny(raw, "-_"); i >= 0 {
|
||||
raw = raw[:i]
|
||||
}
|
||||
return strings.ToLower(strings.TrimSpace(raw))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user