diff --git a/src/templates/src/project_edit.html b/src/templates/src/project_edit.html
index 37e2617..0771348 100644
--- a/src/templates/src/project_edit.html
+++ b/src/templates/src/project_edit.html
@@ -236,6 +236,8 @@
const OWNER_QUERY_STATE_IDLE = 0;
const OWNER_QUERY_STATE_QUERYING = 1;
+ const MAX_OWNERS = {{ .MaxOwners }};
+
let ownerCheckUrl = "{{ .APICheckUsernameUrl }}";
let ownerQueryState = OWNER_QUERY_STATE_IDLE;
let addOwnerInput = document.querySelector("#owner_name");
@@ -256,6 +258,12 @@
startAddOwner();
});
+ function updateAddOwnerStyles() {
+ const numOwnerRows = document.querySelectorAll('.owner_row').length;
+ addOwnerInput.disabled = numOwnerRows >= MAX_OWNERS;
+ }
+ updateAddOwnerStyles();
+
function startAddOwner() {
if (ownerQueryState == OWNER_QUERY_STATE_QUERYING) {
return;
@@ -310,6 +318,7 @@
querying = (ownerQueryState == OWNER_QUERY_STATE_QUERYING);
addOwnerInput.disabled = querying;
addOwnerButton.disabled = querying;
+ updateAddOwnerStyles();
}
function addOwner(username) {
@@ -317,12 +326,14 @@
ownerEl.input.value = username;
ownerEl.name.textContent = username;
ownerList.appendChild(ownerEl.root);
+ updateAddOwnerStyles();
}
ownerList.addEventListener("click", function(ev) {
if (ev.target.classList.contains("remove_owner")) {
ev.target.parentElement.remove();
}
+ updateAddOwnerStyles();
});
/////////////////////
diff --git a/src/website/projects.go b/src/website/projects.go
index 17d80d3..6101295 100644
--- a/src/website/projects.go
+++ b/src/website/projects.go
@@ -27,6 +27,7 @@ import (
)
const maxPersonalProjects = 5
+const maxProjectOwners = 5
type ProjectTemplateData struct {
templates.BaseData
@@ -383,6 +384,7 @@ type ProjectEditData struct {
Editing bool
ProjectSettings templates.ProjectSettings
+ MaxOwners int
APICheckUsernameUrl string
LogoMaxFileSize int
@@ -408,6 +410,7 @@ func ProjectNew(c *RequestContext) ResponseData {
BaseData: getBaseDataAutocrumb(c, "New Project"),
Editing: false,
ProjectSettings: project,
+ MaxOwners: maxProjectOwners,
APICheckUsernameUrl: hmnurl.BuildAPICheckUsername(),
LogoMaxFileSize: ProjectLogoMaxFileSize,
@@ -508,6 +511,7 @@ func ProjectEdit(c *RequestContext) ResponseData {
BaseData: getBaseDataAutocrumb(c, "Edit Project"),
Editing: true,
ProjectSettings: projectSettings,
+ MaxOwners: maxProjectOwners,
APICheckUsernameUrl: hmnurl.BuildAPICheckUsername(),
LogoMaxFileSize: ProjectLogoMaxFileSize,
@@ -629,6 +633,10 @@ func ParseProjectEditForm(c *RequestContext) ProjectEditFormResult {
}
owners := c.Req.Form["owners"]
+ if len(owners) > maxProjectOwners {
+ res.RejectionReason = fmt.Sprintf("Projects can have at most %d owners", maxProjectOwners)
+ return res
+ }
slug := strings.TrimSpace(c.Req.Form.Get("slug"))
officialStr := c.Req.Form.Get("official")