diff --git a/src/templates/src/user_profile.html b/src/templates/src/user_profile.html
index 256a8cbc..88c24af3 100644
--- a/src/templates/src/user_profile.html
+++ b/src/templates/src/user_profile.html
@@ -45,7 +45,11 @@
{{ end }}
{{ if .OwnProfile }}
- + New Project
+ {{ if .CanAddProject }}
+ + New Project
+ {{ else }}
+ You have reached the maximum number of personal projects.
+ {{ end }}
{{ end }}
{{ end }}
diff --git a/src/website/projects.go b/src/website/projects.go
index e8397ebd..17d80d34 100644
--- a/src/website/projects.go
+++ b/src/website/projects.go
@@ -26,6 +26,8 @@ import (
"github.com/jackc/pgx/v4"
)
+const maxPersonalProjects = 5
+
type ProjectTemplateData struct {
templates.BaseData
@@ -387,6 +389,17 @@ type ProjectEditData struct {
}
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
project.Owners = append(project.Owners, templates.UserToTemplate(c.CurrentUser, c.Theme))
project.Personal = true
@@ -417,6 +430,17 @@ func ProjectNewSubmit(c *RequestContext) ResponseData {
}
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
err = tx.QueryRow(c.Context(),
`
diff --git a/src/website/user.go b/src/website/user.go
index 16038c32..de68fc69 100644
--- a/src/website/user.go
+++ b/src/website/user.go
@@ -29,7 +29,9 @@ type UserProfileTemplateData struct {
TimelineItems []templates.TimelineItem
OwnProfile bool
ShowcaseUrl string
- NewProjectUrl string
+
+ CanAddProject bool
+ NewProjectUrl string
}
func UserProfile(c *RequestContext) ResponseData {
@@ -108,10 +110,15 @@ func UserProfile(c *RequestContext) ResponseData {
projectsAndStuff, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, projectsQuery)
templateProjects := make([]templates.Project, 0, len(projectsAndStuff))
+ numPersonalProjects := 0
for _, p := range projectsAndStuff {
templateProject := templates.ProjectToTemplate(&p.Project, hmndata.UrlContextForProject(&p.Project).BuildHomepage())
templateProject.AddLogo(p.LogoURL(c.Theme))
templateProjects = append(templateProjects, templateProject)
+
+ if p.Project.Personal {
+ numPersonalProjects++
+ }
}
c.Perf.EndBlock()
@@ -182,7 +189,9 @@ func UserProfile(c *RequestContext) ResponseData {
TimelineItems: timelineItems,
OwnProfile: (c.CurrentUser != nil && c.CurrentUser.ID == profileUser.ID),
ShowcaseUrl: hmnurl.BuildShowcase(),
- NewProjectUrl: hmnurl.BuildProjectNew(),
+
+ CanAddProject: numPersonalProjects < maxPersonalProjects,
+ NewProjectUrl: hmnurl.BuildProjectNew(),
}, c.Perf)
return res
}