Make some project fields not null
This commit is contained in:
parent
e8d1859d0a
commit
c8231750aa
|
@ -4,6 +4,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"git.handmade.network/hmn/hmn/src/config"
|
"git.handmade.network/hmn/hmn/src/config"
|
||||||
|
"git.handmade.network/hmn/hmn/src/models"
|
||||||
"git.handmade.network/hmn/hmn/src/oops"
|
"git.handmade.network/hmn/hmn/src/oops"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,10 +38,15 @@ func Url(path string, query []Q) string {
|
||||||
return ProjectUrl(path, query, "")
|
return ProjectUrl(path, query, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProjectUrl(path string, query []Q, subdomain string) string {
|
func ProjectUrl(path string, query []Q, slug string) string {
|
||||||
|
subdomain := slug
|
||||||
|
if slug == models.HMNProjectSlug {
|
||||||
|
subdomain = ""
|
||||||
|
}
|
||||||
|
|
||||||
host := baseUrlParsed.Host
|
host := baseUrlParsed.Host
|
||||||
if len(subdomain) > 0 {
|
if len(subdomain) > 0 {
|
||||||
host = subdomain + "." + host
|
host = slug + "." + host
|
||||||
}
|
}
|
||||||
|
|
||||||
url := url.URL{
|
url := url.URL{
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package hmnurl
|
package hmnurl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.handmade.network/hmn/hmn/src/oops"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.handmade.network/hmn/hmn/src/oops"
|
||||||
)
|
)
|
||||||
|
|
||||||
var RegexHomepage *regexp.Regexp = regexp.MustCompile("^/$")
|
var RegexHomepage *regexp.Regexp = regexp.MustCompile("^/$")
|
||||||
|
@ -86,10 +87,6 @@ func BuildFeedWithPage(page int) string {
|
||||||
var RegexForumThread *regexp.Regexp = regexp.MustCompile(`^/(?P<cats>forums(/[^\d]+?)*)/t/(?P<threadid>\d+)(/(?P<page>\d+))?$`)
|
var RegexForumThread *regexp.Regexp = regexp.MustCompile(`^/(?P<cats>forums(/[^\d]+?)*)/t/(?P<threadid>\d+)(/(?P<page>\d+))?$`)
|
||||||
|
|
||||||
func BuildForumThread(projectSlug string, subforums []string, threadId int, page int) string {
|
func BuildForumThread(projectSlug string, subforums []string, threadId int, page int) string {
|
||||||
if projectSlug == "hmn" {
|
|
||||||
projectSlug = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
panic(oops.New(nil, "Invalid forum thread page (%d), must be >= 1", page))
|
panic(oops.New(nil, "Invalid forum thread page (%d), must be >= 1", page))
|
||||||
}
|
}
|
||||||
|
@ -120,10 +117,6 @@ func BuildForumThread(projectSlug string, subforums []string, threadId int, page
|
||||||
var RegexForumCategory *regexp.Regexp = regexp.MustCompile(`^/(?P<cats>forums(/[^\d]+?)*)(/(?P<page>\d+))?$`)
|
var RegexForumCategory *regexp.Regexp = regexp.MustCompile(`^/(?P<cats>forums(/[^\d]+?)*)(/(?P<page>\d+))?$`)
|
||||||
|
|
||||||
func BuildForumCategory(projectSlug string, subforums []string, page int) string {
|
func BuildForumCategory(projectSlug string, subforums []string, page int) string {
|
||||||
if projectSlug == "hmn" {
|
|
||||||
projectSlug = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
panic(oops.New(nil, "Invalid forum thread page (%d), must be >= 1", page))
|
panic(oops.New(nil, "Invalid forum thread page (%d), must be >= 1", page))
|
||||||
}
|
}
|
||||||
|
@ -152,10 +145,6 @@ func BuildForumCategory(projectSlug string, subforums []string, page int) string
|
||||||
var RegexForumPost *regexp.Regexp = regexp.MustCompile(``) // TODO(asaf): Complete this and test it
|
var RegexForumPost *regexp.Regexp = regexp.MustCompile(``) // TODO(asaf): Complete this and test it
|
||||||
|
|
||||||
func BuildForumPost(projectSlug string, subforums []string, threadId int, postId int) string {
|
func BuildForumPost(projectSlug string, subforums []string, threadId int, postId int) string {
|
||||||
if projectSlug == "hmn" {
|
|
||||||
projectSlug = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
builder.WriteString("/forums")
|
builder.WriteString("/forums")
|
||||||
for _, subforum := range subforums {
|
for _, subforum := range subforums {
|
||||||
|
@ -180,7 +169,7 @@ func BuildForumPost(projectSlug string, subforums []string, threadId int, postId
|
||||||
var RegexProjectCSS *regexp.Regexp = regexp.MustCompile("^/assets/project.css$")
|
var RegexProjectCSS *regexp.Regexp = regexp.MustCompile("^/assets/project.css$")
|
||||||
|
|
||||||
func BuildProjectCSS(color string) string {
|
func BuildProjectCSS(color string) string {
|
||||||
return Url("/assets/project.css", []Q{Q{"color", color}})
|
return Url("/assets/project.css", []Q{{"color", color}})
|
||||||
}
|
}
|
||||||
|
|
||||||
var RegexPublic *regexp.Regexp = regexp.MustCompile("^/public/.+$")
|
var RegexPublic *regexp.Regexp = regexp.MustCompile("^/public/.+$")
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.handmade.network/hmn/hmn/src/migration/types"
|
||||||
|
"git.handmade.network/hmn/hmn/src/oops"
|
||||||
|
"github.com/jackc/pgx/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerMigration(RemoveProjectNulls{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveProjectNulls struct{}
|
||||||
|
|
||||||
|
func (m RemoveProjectNulls) Version() types.MigrationVersion {
|
||||||
|
return types.MigrationVersion(time.Date(2021, 5, 6, 3, 13, 28, 0, time.UTC))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RemoveProjectNulls) Name() string {
|
||||||
|
return "RemoveProjectNulls"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RemoveProjectNulls) Description() string {
|
||||||
|
return "Make project fields non-nullable"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RemoveProjectNulls) Up(ctx context.Context, tx pgx.Tx) error {
|
||||||
|
_, err := tx.Exec(ctx, `
|
||||||
|
ALTER TABLE handmade_project
|
||||||
|
ALTER slug SET NOT NULL,
|
||||||
|
ALTER name SET NOT NULL,
|
||||||
|
ALTER blurb SET NOT NULL,
|
||||||
|
ALTER description SET NOT NULL;
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return oops.New(err, "failed to make project fields non-null")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RemoveProjectNulls) Down(ctx context.Context, tx pgx.Tx) error {
|
||||||
|
panic("Implement me")
|
||||||
|
}
|
|
@ -34,13 +34,66 @@ type Category struct {
|
||||||
Depth int `db:"depth"` // TODO: What is this?
|
Depth int `db:"depth"` // TODO: What is this?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CategoryTree map[int]*CategoryTreeNode
|
||||||
|
|
||||||
|
type CategoryTreeNode struct {
|
||||||
|
Category
|
||||||
|
Parent *CategoryTreeNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *CategoryTreeNode) GetLineage() []*Category {
|
||||||
|
current := node
|
||||||
|
length := 0
|
||||||
|
for current != nil {
|
||||||
|
current = current.Parent
|
||||||
|
length += 1
|
||||||
|
}
|
||||||
|
result := make([]*Category, length)
|
||||||
|
current = node
|
||||||
|
for i := length - 1; i >= 0; i -= 1 {
|
||||||
|
result[i] = ¤t.Category
|
||||||
|
current = current.Parent
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFullCategoryTree(ctx context.Context, conn *pgxpool.Pool) CategoryTree {
|
||||||
|
type categoryRow struct {
|
||||||
|
Cat Category `db:"cat"`
|
||||||
|
}
|
||||||
|
rows, err := db.Query(ctx, conn, categoryRow{},
|
||||||
|
`
|
||||||
|
SELECT $columns
|
||||||
|
FROM
|
||||||
|
handmade_category as cat
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(oops.New(err, "Failed to fetch category tree"))
|
||||||
|
}
|
||||||
|
|
||||||
|
rowsSlice := rows.ToSlice()
|
||||||
|
catTreeMap := make(map[int]*CategoryTreeNode, len(rowsSlice))
|
||||||
|
for _, row := range rowsSlice {
|
||||||
|
cat := row.(*categoryRow).Cat
|
||||||
|
catTreeMap[cat.ID] = &CategoryTreeNode{Category: cat}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range catTreeMap {
|
||||||
|
if node.ParentID != nil {
|
||||||
|
node.Parent = catTreeMap[*node.ParentID]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return catTreeMap
|
||||||
|
}
|
||||||
|
|
||||||
type CategoryLineageBuilder struct {
|
type CategoryLineageBuilder struct {
|
||||||
Tree map[int]*CategoryTreeNode
|
Tree CategoryTree
|
||||||
CategoryCache map[int][]*Category
|
CategoryCache map[int][]*Category
|
||||||
SlugCache map[int][]string
|
SlugCache map[int][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeCategoryLineageBuilder(fullCategoryTree map[int]*CategoryTreeNode) *CategoryLineageBuilder {
|
func MakeCategoryLineageBuilder(fullCategoryTree CategoryTree) *CategoryLineageBuilder {
|
||||||
return &CategoryLineageBuilder{
|
return &CategoryLineageBuilder{
|
||||||
Tree: fullCategoryTree,
|
Tree: fullCategoryTree,
|
||||||
CategoryCache: make(map[int][]*Category),
|
CategoryCache: make(map[int][]*Category),
|
||||||
|
@ -72,93 +125,3 @@ func (cl *CategoryLineageBuilder) GetLineageSlugs(catId int) []string {
|
||||||
}
|
}
|
||||||
return cl.SlugCache[catId]
|
return cl.SlugCache[catId]
|
||||||
}
|
}
|
||||||
|
|
||||||
type CategoryTreeNode struct {
|
|
||||||
Category
|
|
||||||
Parent *CategoryTreeNode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *CategoryTreeNode) GetLineage() []*Category {
|
|
||||||
current := node
|
|
||||||
length := 0
|
|
||||||
for current != nil {
|
|
||||||
current = current.Parent
|
|
||||||
length += 1
|
|
||||||
}
|
|
||||||
result := make([]*Category, length)
|
|
||||||
current = node
|
|
||||||
for i := length - 1; i >= 0; i -= 1 {
|
|
||||||
result[i] = ¤t.Category
|
|
||||||
current = current.Parent
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetFullCategoryTree(ctx context.Context, conn *pgxpool.Pool) map[int]*CategoryTreeNode {
|
|
||||||
type categoryRow struct {
|
|
||||||
Cat Category `db:"cat"`
|
|
||||||
}
|
|
||||||
rows, err := db.Query(ctx, conn, categoryRow{},
|
|
||||||
`
|
|
||||||
SELECT $columns
|
|
||||||
FROM
|
|
||||||
handmade_category as cat
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
panic(oops.New(err, "Failed to fetch category tree"))
|
|
||||||
}
|
|
||||||
|
|
||||||
rowsSlice := rows.ToSlice()
|
|
||||||
catTreeMap := make(map[int]*CategoryTreeNode, len(rowsSlice))
|
|
||||||
for _, row := range rowsSlice {
|
|
||||||
cat := row.(*categoryRow).Cat
|
|
||||||
catTreeMap[cat.ID] = &CategoryTreeNode{Category: cat}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, node := range catTreeMap {
|
|
||||||
if node.ParentID != nil {
|
|
||||||
node.Parent = catTreeMap[*node.ParentID]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return catTreeMap
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Gets the category and its parent categories, starting from the root and working toward the
|
|
||||||
category itself. Useful for breadcrumbs and the like.
|
|
||||||
*/
|
|
||||||
func (c *Category) GetHierarchy(ctx context.Context, conn *pgxpool.Pool) []Category {
|
|
||||||
// TODO: Make this work for a whole set of categories at once. Should be doable.
|
|
||||||
type breadcrumbRow struct {
|
|
||||||
Cat Category `db:"cats"`
|
|
||||||
}
|
|
||||||
rows, err := db.Query(ctx, conn, breadcrumbRow{},
|
|
||||||
`
|
|
||||||
WITH RECURSIVE cats AS (
|
|
||||||
SELECT *
|
|
||||||
FROM handmade_category AS cat
|
|
||||||
WHERE cat.id = $1
|
|
||||||
UNION ALL
|
|
||||||
SELECT parentcat.*
|
|
||||||
FROM
|
|
||||||
handmade_category AS parentcat
|
|
||||||
JOIN cats ON cats.parent_id = parentcat.id
|
|
||||||
)
|
|
||||||
SELECT $columns FROM cats;
|
|
||||||
`,
|
|
||||||
c.ID,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rowsSlice := rows.ToSlice()
|
|
||||||
var result []Category
|
|
||||||
for i := len(rowsSlice) - 1; i >= 0; i-- {
|
|
||||||
row := rowsSlice[i].(*breadcrumbRow)
|
|
||||||
result = append(result, row.Cat)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const HMNProjectID = 1
|
const (
|
||||||
|
HMNProjectID = 1
|
||||||
|
HMNProjectSlug = "hmn"
|
||||||
|
)
|
||||||
|
|
||||||
var ProjectType = reflect.TypeOf(Project{})
|
var ProjectType = reflect.TypeOf(Project{})
|
||||||
|
|
||||||
|
@ -24,10 +27,10 @@ const (
|
||||||
type Project struct {
|
type Project struct {
|
||||||
ID int `db:"id"`
|
ID int `db:"id"`
|
||||||
|
|
||||||
Slug *string `db:"slug"` // TODO: Migrate these to NOT NULL
|
Slug string `db:"slug"`
|
||||||
Name *string `db:"name"`
|
Name string `db:"name"`
|
||||||
Blurb *string `db:"blurb"`
|
Blurb string `db:"blurb"`
|
||||||
Description *string `db:"description"`
|
Description string `db:"description"`
|
||||||
|
|
||||||
Lifecycle ProjectLifecycle `db:"lifecycle"`
|
Lifecycle ProjectLifecycle `db:"lifecycle"`
|
||||||
|
|
||||||
|
@ -46,5 +49,5 @@ func (p *Project) Subdomain() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return *p.Slug
|
return p.Slug
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ func PostToTemplateWithContent(p *models.Post, author *models.User, content stri
|
||||||
|
|
||||||
func ProjectToTemplate(p *models.Project) Project {
|
func ProjectToTemplate(p *models.Project) Project {
|
||||||
return Project{
|
return Project{
|
||||||
Name: maybeString(p.Name),
|
Name: p.Name,
|
||||||
Subdomain: p.Subdomain(),
|
Subdomain: p.Subdomain(),
|
||||||
Color1: p.Color1,
|
Color1: p.Color1,
|
||||||
Color2: p.Color2,
|
Color2: p.Color2,
|
||||||
|
|
|
@ -124,13 +124,13 @@ func Feed(c *RequestContext) ResponseData {
|
||||||
c.Perf.EndBlock()
|
c.Perf.EndBlock()
|
||||||
|
|
||||||
categoryUrlCache := make(map[int]string)
|
categoryUrlCache := make(map[int]string)
|
||||||
getCategoryUrl := func(subdomain string, cat *models.Category) string {
|
getCategoryUrl := func(projectSlug string, cat *models.Category) string {
|
||||||
_, ok := categoryUrlCache[cat.ID]
|
_, ok := categoryUrlCache[cat.ID]
|
||||||
if !ok {
|
if !ok {
|
||||||
lineageNames := lineageBuilder.GetLineageSlugs(cat.ID)
|
lineageNames := lineageBuilder.GetLineageSlugs(cat.ID)
|
||||||
switch cat.Kind {
|
switch cat.Kind {
|
||||||
case models.CatKindForum:
|
case models.CatKindForum:
|
||||||
categoryUrlCache[cat.ID] = hmnurl.BuildForumCategory(subdomain, lineageNames[1:], 1)
|
categoryUrlCache[cat.ID] = hmnurl.BuildForumCategory(projectSlug, lineageNames[1:], 1)
|
||||||
// TODO(asaf): Add more kinds!!!
|
// TODO(asaf): Add more kinds!!!
|
||||||
default:
|
default:
|
||||||
categoryUrlCache[cat.ID] = ""
|
categoryUrlCache[cat.ID] = ""
|
||||||
|
@ -153,8 +153,8 @@ func Feed(c *RequestContext) ResponseData {
|
||||||
|
|
||||||
breadcrumbs := make([]templates.Breadcrumb, 0, len(lineageBuilder.GetLineage(postResult.Cat.ID)))
|
breadcrumbs := make([]templates.Breadcrumb, 0, len(lineageBuilder.GetLineage(postResult.Cat.ID)))
|
||||||
breadcrumbs = append(breadcrumbs, templates.Breadcrumb{
|
breadcrumbs = append(breadcrumbs, templates.Breadcrumb{
|
||||||
Name: *postResult.Proj.Name,
|
Name: postResult.Proj.Name,
|
||||||
Url: hmnurl.ProjectUrl("/", nil, postResult.Proj.Subdomain()),
|
Url: hmnurl.ProjectUrl("/", nil, postResult.Proj.Slug),
|
||||||
})
|
})
|
||||||
if postResult.Post.CategoryKind == models.CatKindLibraryResource {
|
if postResult.Post.CategoryKind == models.CatKindLibraryResource {
|
||||||
// TODO(asaf): Fetch library root topic for the project and construct breadcrumb for it
|
// TODO(asaf): Fetch library root topic for the project and construct breadcrumb for it
|
||||||
|
|
|
@ -261,11 +261,11 @@ func ForumCategory(c *RequestContext) ResponseData {
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
||||||
baseData := getBaseData(c)
|
baseData := getBaseData(c)
|
||||||
baseData.Title = *c.CurrentProject.Name + " Forums"
|
baseData.Title = c.CurrentProject.Name + " Forums"
|
||||||
baseData.Breadcrumbs = []templates.Breadcrumb{
|
baseData.Breadcrumbs = []templates.Breadcrumb{
|
||||||
{
|
{
|
||||||
Name: *c.CurrentProject.Name,
|
Name: c.CurrentProject.Name,
|
||||||
Url: hmnurl.ProjectUrl("/", nil, c.CurrentProject.Subdomain()),
|
Url: hmnurl.ProjectUrl("/", nil, c.CurrentProject.Slug),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Forums",
|
Name: "Forums",
|
||||||
|
|
|
@ -75,7 +75,7 @@ func Index(c *RequestContext) ResponseData {
|
||||||
for _, projRow := range allProjects {
|
for _, projRow := range allProjects {
|
||||||
proj := projRow.(*models.Project)
|
proj := projRow.(*models.Project)
|
||||||
|
|
||||||
c.Perf.StartBlock("SQL", fmt.Sprintf("Fetch posts for %s", *proj.Name))
|
c.Perf.StartBlock("SQL", fmt.Sprintf("Fetch posts for %s", proj.Name))
|
||||||
type projectPostQuery struct {
|
type projectPostQuery struct {
|
||||||
Post models.Post `db:"post"`
|
Post models.Post `db:"post"`
|
||||||
Thread models.Thread `db:"thread"`
|
Thread models.Thread `db:"thread"`
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
@ -33,7 +34,7 @@ func TestLogContextErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
routes.GET("^/test$", func(c *RequestContext) ResponseData {
|
routes.GET(regexp.MustCompile("^/test$"), func(c *RequestContext) ResponseData {
|
||||||
return ErrorResponse(http.StatusInternalServerError, err1, err2)
|
return ErrorResponse(http.StatusInternalServerError, err1, err2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -87,20 +87,20 @@ func makeCategoryUrls(rows []interface{}) map[int]string {
|
||||||
hierarchy[len(hierarchyReverse)-1-i] = hierarchyReverse[i]
|
hierarchy[len(hierarchyReverse)-1-i] = hierarchyReverse[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
result[row.Cat.ID] = CategoryUrl(row.Project.Subdomain(), hierarchy...)
|
result[row.Cat.ID] = CategoryUrl(row.Project.Slug, hierarchy...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func CategoryUrl(subdomain string, cats ...*models.Category) string {
|
func CategoryUrl(projectSlug string, cats ...*models.Category) string {
|
||||||
catNames := make([]string, 0, len(cats))
|
catNames := make([]string, 0, len(cats))
|
||||||
for _, cat := range cats {
|
for _, cat := range cats {
|
||||||
catNames = append(catNames, *cat.Name)
|
catNames = append(catNames, *cat.Name)
|
||||||
}
|
}
|
||||||
switch cats[0].Kind {
|
switch cats[0].Kind {
|
||||||
case models.CatKindForum:
|
case models.CatKindForum:
|
||||||
return hmnurl.BuildForumCategory(subdomain, catNames[1:], 1)
|
return hmnurl.BuildForumCategory(projectSlug, catNames[1:], 1)
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue