Cap the number of personal projects per user
This commit is contained in:
parent
79c9738b96
commit
b88776af0c
|
@ -45,7 +45,11 @@
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if .OwnProfile }}
|
{{ if .OwnProfile }}
|
||||||
|
{{ if .CanAddProject }}
|
||||||
<a href="{{ .NewProjectUrl }}">+ New Project</a>
|
<a href="{{ .NewProjectUrl }}">+ New Project</a>
|
||||||
|
{{ else }}
|
||||||
|
<span class="c--dim i">You have reached the maximum number of personal projects.</span>
|
||||||
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import (
|
||||||
"github.com/jackc/pgx/v4"
|
"github.com/jackc/pgx/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxPersonalProjects = 5
|
||||||
|
|
||||||
type ProjectTemplateData struct {
|
type ProjectTemplateData struct {
|
||||||
templates.BaseData
|
templates.BaseData
|
||||||
|
|
||||||
|
@ -387,6 +389,17 @@ type ProjectEditData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProjectNew(c *RequestContext) ResponseData {
|
func ProjectNew(c *RequestContext) ResponseData {
|
||||||
|
numProjects, err := hmndata.CountProjects(c.Context(), c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
|
||||||
|
OwnerIDs: []int{c.CurrentUser.ID},
|
||||||
|
Types: hmndata.PersonalProjects,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to check number of personal projects"))
|
||||||
|
}
|
||||||
|
if numProjects >= maxPersonalProjects {
|
||||||
|
return RejectRequest(c, fmt.Sprintf("You have already reached the maximum of %d personal projects.", maxPersonalProjects))
|
||||||
|
}
|
||||||
|
|
||||||
var project templates.ProjectSettings
|
var project templates.ProjectSettings
|
||||||
project.Owners = append(project.Owners, templates.UserToTemplate(c.CurrentUser, c.Theme))
|
project.Owners = append(project.Owners, templates.UserToTemplate(c.CurrentUser, c.Theme))
|
||||||
project.Personal = true
|
project.Personal = true
|
||||||
|
@ -417,6 +430,17 @@ func ProjectNewSubmit(c *RequestContext) ResponseData {
|
||||||
}
|
}
|
||||||
defer tx.Rollback(c.Context())
|
defer tx.Rollback(c.Context())
|
||||||
|
|
||||||
|
numProjects, err := hmndata.CountProjects(c.Context(), c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
|
||||||
|
OwnerIDs: []int{c.CurrentUser.ID},
|
||||||
|
Types: hmndata.PersonalProjects,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to check number of personal projects"))
|
||||||
|
}
|
||||||
|
if numProjects >= maxPersonalProjects {
|
||||||
|
return RejectRequest(c, fmt.Sprintf("You have already reached the maximum of %d personal projects.", maxPersonalProjects))
|
||||||
|
}
|
||||||
|
|
||||||
var projectId int
|
var projectId int
|
||||||
err = tx.QueryRow(c.Context(),
|
err = tx.QueryRow(c.Context(),
|
||||||
`
|
`
|
||||||
|
|
|
@ -29,6 +29,8 @@ type UserProfileTemplateData struct {
|
||||||
TimelineItems []templates.TimelineItem
|
TimelineItems []templates.TimelineItem
|
||||||
OwnProfile bool
|
OwnProfile bool
|
||||||
ShowcaseUrl string
|
ShowcaseUrl string
|
||||||
|
|
||||||
|
CanAddProject bool
|
||||||
NewProjectUrl string
|
NewProjectUrl string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,10 +110,15 @@ func UserProfile(c *RequestContext) ResponseData {
|
||||||
|
|
||||||
projectsAndStuff, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, projectsQuery)
|
projectsAndStuff, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, projectsQuery)
|
||||||
templateProjects := make([]templates.Project, 0, len(projectsAndStuff))
|
templateProjects := make([]templates.Project, 0, len(projectsAndStuff))
|
||||||
|
numPersonalProjects := 0
|
||||||
for _, p := range projectsAndStuff {
|
for _, p := range projectsAndStuff {
|
||||||
templateProject := templates.ProjectToTemplate(&p.Project, hmndata.UrlContextForProject(&p.Project).BuildHomepage())
|
templateProject := templates.ProjectToTemplate(&p.Project, hmndata.UrlContextForProject(&p.Project).BuildHomepage())
|
||||||
templateProject.AddLogo(p.LogoURL(c.Theme))
|
templateProject.AddLogo(p.LogoURL(c.Theme))
|
||||||
templateProjects = append(templateProjects, templateProject)
|
templateProjects = append(templateProjects, templateProject)
|
||||||
|
|
||||||
|
if p.Project.Personal {
|
||||||
|
numPersonalProjects++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.Perf.EndBlock()
|
c.Perf.EndBlock()
|
||||||
|
|
||||||
|
@ -182,6 +189,8 @@ func UserProfile(c *RequestContext) ResponseData {
|
||||||
TimelineItems: timelineItems,
|
TimelineItems: timelineItems,
|
||||||
OwnProfile: (c.CurrentUser != nil && c.CurrentUser.ID == profileUser.ID),
|
OwnProfile: (c.CurrentUser != nil && c.CurrentUser.ID == profileUser.ID),
|
||||||
ShowcaseUrl: hmnurl.BuildShowcase(),
|
ShowcaseUrl: hmnurl.BuildShowcase(),
|
||||||
|
|
||||||
|
CanAddProject: numPersonalProjects < maxPersonalProjects,
|
||||||
NewProjectUrl: hmnurl.BuildProjectNew(),
|
NewProjectUrl: hmnurl.BuildProjectNew(),
|
||||||
}, c.Perf)
|
}, c.Perf)
|
||||||
return res
|
return res
|
||||||
|
|
Loading…
Reference in New Issue