From: James Moger Date: Thu, 18 Oct 2012 21:25:07 +0000 (-0400) Subject: New permissions UI for EditUser and EditTeam (issue 36) X-Git-Tag: v1.2.0~154 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b0e164283fee6f993589cce849ba1fc7d294e89d;p=gitblit.git New permissions UI for EditUser and EditTeam (issue 36) --- diff --git a/resources/gitblit.css b/resources/gitblit.css index 1d17dc84..4d7e3ab1 100644 --- a/resources/gitblit.css +++ b/resources/gitblit.css @@ -180,6 +180,15 @@ navbar div>ul .menu-dropdown li a:hover,.nav .menu-dropdown li a:hover,.navbar d vertical-align: middle; } +div.odd { + +} + +div.even { + background-color: whiteSmoke; + vertical-align: middle; +} + div.page_footer { clear: both; height: 17px; diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index ed48bd27..0e68355e 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -320,6 +320,8 @@ public class Constants { public static enum AccessPermission { NONE("N"), VIEW("V"), CLONE("R"), PUSH("RW"), CREATE("RWC"), DELETE("RWD"), REWIND("RW+"); + public static final AccessPermission [] NEWPERMISSIONS = { VIEW, CLONE, PUSH, CREATE, DELETE, REWIND }; + public static AccessPermission LEGACY = REWIND; public final String code; diff --git a/src/com/gitblit/models/RepositoryAccessPermission.java b/src/com/gitblit/models/RepositoryAccessPermission.java new file mode 100644 index 00000000..06f5c053 --- /dev/null +++ b/src/com/gitblit/models/RepositoryAccessPermission.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.models; + +import java.io.Serializable; + +import com.gitblit.Constants.AccessPermission; +import com.gitblit.utils.StringUtils; + +/** + * Represents a Repository-AccessPermission tuple. + * + * @author James Moger + */ +public class RepositoryAccessPermission implements Serializable, Comparable { + + private static final long serialVersionUID = 1L; + + public String repository; + public AccessPermission permission; + + public RepositoryAccessPermission() { + } + + public RepositoryAccessPermission(String repository, AccessPermission permission) { + this.repository = repository; + this.permission = permission; + } + + @Override + public int compareTo(RepositoryAccessPermission p) { + return StringUtils.compareRepositoryNames(repository, p.repository); + } + + @Override + public String toString() { + return permission.asRole(repository); + } +} \ No newline at end of file diff --git a/src/com/gitblit/models/TeamModel.java b/src/com/gitblit/models/TeamModel.java index 149c7659..95e6ef4e 100644 --- a/src/com/gitblit/models/TeamModel.java +++ b/src/com/gitblit/models/TeamModel.java @@ -18,6 +18,7 @@ package com.gitblit.models; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -85,6 +86,21 @@ public class TeamModel implements Serializable, Comparable { public void removeRepository(String name) { removeRepositoryPermission(name); } + + + /** + * Returns a list of repository permissions for this team. + * + * @return the team's list of permissions + */ + public List getRepositoryPermissions() { + List list = new ArrayList(); + for (Map.Entry entry : permissions.entrySet()) { + list.add(new RepositoryAccessPermission(entry.getKey(), entry.getValue())); + } + Collections.sort(list); + return list; + } /** * Returns true if the team has any type of specified access permission for diff --git a/src/com/gitblit/models/UserModel.java b/src/com/gitblit/models/UserModel.java index f14c1ae8..38a7aaed 100644 --- a/src/com/gitblit/models/UserModel.java +++ b/src/com/gitblit/models/UserModel.java @@ -17,8 +17,11 @@ package com.gitblit.models; import java.io.Serializable; import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -124,6 +127,21 @@ public class UserModel implements Principal, Serializable, Comparable removeRepositoryPermission(name); } + /** + * Returns a list of repository permissions for this user exclusive of + * permissions inherited from team memberships. + * + * @return the user's list of permissions + */ + public List getRepositoryPermissions() { + List list = new ArrayList(); + for (Map.Entry entry : permissions.entrySet()) { + list.add(new RepositoryAccessPermission(entry.getKey(), entry.getValue())); + } + Collections.sort(list); + return list; + } + /** * Returns true if the user has any type of specified access permission for * this repository. diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index f6d60dca..eb7d7725 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -345,4 +345,12 @@ gb.verifyCommitter = verify committer gb.verifyCommitterDescription = require committer identity to match pushing Gitblt user account (all merges require "--no-ff" to enforce committer identity) gb.repositoryPermissions = repository permissions gb.userPermissions = user permissions -gb.teamPermissions = team permissions \ No newline at end of file +gb.teamPermissions = team permissions +gb.add = add +gb.noPermission = NO ACCESS +gb.viewPermission = {0} (view) +gb.clonePermission = {0} (clone) +gb.pushPermission = {0} (push) +gb.createPermission = {0} (push, ref creation) +gb.deletePermission = {0} (push, ref creation+deletion) +gb.rewindPermission = {0} (push, ref creation+deletion+rewind) \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/BasePage.java b/src/com/gitblit/wicket/pages/BasePage.java index 4d376114..48a872a8 100644 --- a/src/com/gitblit/wicket/pages/BasePage.java +++ b/src/com/gitblit/wicket/pages/BasePage.java @@ -15,6 +15,7 @@ */ package com.gitblit.wicket.pages; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -52,6 +53,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitblit.Constants; +import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.FederationStrategy; import com.gitblit.GitBlit; @@ -203,6 +205,36 @@ public abstract class BasePage extends WebPage { return map; } + protected Map getAccessPermissions() { + Map map = new LinkedHashMap(); + for (AccessPermission type : AccessPermission.values()) { + switch (type) { + case NONE: + map.put(type, MessageFormat.format(getString("gb.noPermission"), type.code)); + break; + case VIEW: + map.put(type, MessageFormat.format(getString("gb.viewPermission"), type.code)); + break; + case CLONE: + map.put(type, MessageFormat.format(getString("gb.clonePermission"), type.code)); + break; + case PUSH: + map.put(type, MessageFormat.format(getString("gb.pushPermission"), type.code)); + break; + case CREATE: + map.put(type, MessageFormat.format(getString("gb.createPermission"), type.code)); + break; + case DELETE: + map.put(type, MessageFormat.format(getString("gb.deletePermission"), type.code)); + break; + case REWIND: + map.put(type, MessageFormat.format(getString("gb.rewindPermission"), type.code)); + break; + } + } + return map; + } + protected Map getFederationTypes() { Map map = new LinkedHashMap(); for (FederationStrategy type : FederationStrategy.values()) { diff --git a/src/com/gitblit/wicket/pages/EditTeamPage.html b/src/com/gitblit/wicket/pages/EditTeamPage.html index 30d2a94c..fb0819f9 100644 --- a/src/com/gitblit/wicket/pages/EditTeamPage.html +++ b/src/com/gitblit/wicket/pages/EditTeamPage.html @@ -19,7 +19,7 @@

 


- +

 

diff --git a/src/com/gitblit/wicket/pages/EditTeamPage.java b/src/com/gitblit/wicket/pages/EditTeamPage.java index 9cbccb59..05c91215 100644 --- a/src/com/gitblit/wicket/pages/EditTeamPage.java +++ b/src/com/gitblit/wicket/pages/EditTeamPage.java @@ -40,12 +40,14 @@ import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.GitBlit; import com.gitblit.GitBlitException; import com.gitblit.models.RepositoryModel; +import com.gitblit.models.RepositoryAccessPermission; import com.gitblit.models.TeamModel; import com.gitblit.utils.StringUtils; import com.gitblit.wicket.RequiresAdminRole; import com.gitblit.wicket.StringChoiceRenderer; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.BulletListPanel; +import com.gitblit.wicket.panels.RepositoryPermissionsPanel; @RequiresAdminRole public class EditTeamPage extends RootSubPage { @@ -59,6 +61,7 @@ public class EditTeamPage extends RootSubPage { super(); isCreate = true; setupPage(new TeamModel("")); + setStatelessHint(false); } public EditTeamPage(PageParameters params) { @@ -68,6 +71,7 @@ public class EditTeamPage extends RootSubPage { String name = WicketUtils.getTeamname(params); TeamModel model = GitBlit.self().getTeamModel(name); setupPage(model); + setStatelessHint(false); } protected void setupPage(final TeamModel teamModel) { @@ -94,11 +98,7 @@ public class EditTeamPage extends RootSubPage { List postReceiveScripts = new ArrayList(); final String oldName = teamModel.name; - - // repositories palette - final Palette repositories = new Palette("repositories", - new ListModel(new ArrayList(teamModel.repositories)), - new CollectionModel(repos), new StringChoiceRenderer(), 10, false); + final List permissions = teamModel.getRepositoryPermissions(); // users palette final Palette users = new Palette("users", new ListModel( @@ -146,17 +146,10 @@ public class EditTeamPage extends RootSubPage { return; } } - Iterator selectedRepositories = repositories.getSelectedChoices(); - List repos = new ArrayList(); - while (selectedRepositories.hasNext()) { - repos.add(selectedRepositories.next().toLowerCase()); - } - if (repos.size() == 0) { - error(getString("gb.teamMustSpecifyRepository")); - return; + // update team permissions + for (RepositoryAccessPermission repositoryPermission : permissions) { + teamModel.setRepositoryPermission(repositoryPermission.repository, repositoryPermission.permission); } - teamModel.repositories.clear(); - teamModel.repositories.addAll(repos); Iterator selectedUsers = users.getSelectedChoices(); List members = new ArrayList(); @@ -231,7 +224,7 @@ public class EditTeamPage extends RootSubPage { : StringUtils.flattenStrings(teamModel.mailingLists, " ")); form.add(new TextField("mailingLists", mailingLists)); - form.add(repositories); + form.add(new RepositoryPermissionsPanel("repositories", permissions, getAccessPermissions())); form.add(preReceivePalette); form.add(new BulletListPanel("inheritedPreReceive", "inherited", GitBlit.self() .getPreReceiveScriptsInherited(null))); diff --git a/src/com/gitblit/wicket/pages/EditUserPage.html b/src/com/gitblit/wicket/pages/EditUserPage.html index 9f178df8..f993d46e 100644 --- a/src/com/gitblit/wicket/pages/EditUserPage.html +++ b/src/com/gitblit/wicket/pages/EditUserPage.html @@ -23,11 +23,16 @@

 


- + + +
+ +
 
- + + \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/EditUserPage.java b/src/com/gitblit/wicket/pages/EditUserPage.java index 49515fb9..6e353543 100644 --- a/src/com/gitblit/wicket/pages/EditUserPage.java +++ b/src/com/gitblit/wicket/pages/EditUserPage.java @@ -39,18 +39,20 @@ import com.gitblit.GitBlit; import com.gitblit.GitBlitException; import com.gitblit.Keys; import com.gitblit.models.RepositoryModel; +import com.gitblit.models.RepositoryAccessPermission; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; import com.gitblit.wicket.RequiresAdminRole; import com.gitblit.wicket.StringChoiceRenderer; import com.gitblit.wicket.WicketUtils; +import com.gitblit.wicket.panels.RepositoryPermissionsPanel; @RequiresAdminRole public class EditUserPage extends RootSubPage { private final boolean isCreate; - + public EditUserPage() { // create constructor super(); @@ -60,6 +62,7 @@ public class EditUserPage extends RootSubPage { } isCreate = true; setupPage(new UserModel("")); + setStatelessHint(false); } public EditUserPage(PageParameters params) { @@ -69,6 +72,7 @@ public class EditUserPage extends RootSubPage { String name = WicketUtils.getUsername(params); UserModel model = GitBlit.self().getUserModel(name); setupPage(model); + setStatelessHint(false); } protected void setupPage(final UserModel userModel) { @@ -96,16 +100,15 @@ public class EditUserPage extends RootSubPage { Collections.sort(userTeams); final String oldName = userModel.username; - final Palette repositories = new Palette("repositories", - new ListModel(new ArrayList(userModel.repositories)), - new CollectionModel(repos), new StringChoiceRenderer(), 10, false); + final List permissions = userModel.getRepositoryPermissions(); + final Palette teams = new Palette("teams", new ListModel( new ArrayList(userTeams)), new CollectionModel(GitBlit.self() .getAllTeamnames()), new StringChoiceRenderer(), 10, false); Form form = new Form("editForm", model) { private static final long serialVersionUID = 1L; - + /* * (non-Javadoc) * @@ -167,13 +170,10 @@ public class EditUserPage extends RootSubPage { } } - Iterator selectedRepositories = repositories.getSelectedChoices(); - List repos = new ArrayList(); - while (selectedRepositories.hasNext()) { - repos.add(selectedRepositories.next().toLowerCase()); + // update user permissions + for (RepositoryAccessPermission repositoryPermission : permissions) { + userModel.setRepositoryPermission(repositoryPermission.repository, repositoryPermission.permission); } - userModel.repositories.clear(); - userModel.repositories.addAll(repos); Iterator selectedTeams = teams.getSelectedChoices(); userModel.teams.clear(); @@ -234,7 +234,7 @@ public class EditUserPage extends RootSubPage { form.add(new CheckBox("canFork")); form.add(new CheckBox("canCreate")); form.add(new CheckBox("excludeFromFederation")); - form.add(repositories); + form.add(new RepositoryPermissionsPanel("repositories", permissions, getAccessPermissions())); form.add(teams.setEnabled(editTeams)); form.add(new Button("save")); diff --git a/src/com/gitblit/wicket/panels/RepositoryPermissionsPanel.html b/src/com/gitblit/wicket/panels/RepositoryPermissionsPanel.html new file mode 100644 index 00000000..1c7e44ef --- /dev/null +++ b/src/com/gitblit/wicket/panels/RepositoryPermissionsPanel.html @@ -0,0 +1,24 @@ + + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+ + \ No newline at end of file diff --git a/src/com/gitblit/wicket/panels/RepositoryPermissionsPanel.java b/src/com/gitblit/wicket/panels/RepositoryPermissionsPanel.java new file mode 100644 index 00000000..3d967d36 --- /dev/null +++ b/src/com/gitblit/wicket/panels/RepositoryPermissionsPanel.java @@ -0,0 +1,157 @@ +/* + * Copyright 2012 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.wicket.panels; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; +import org.apache.wicket.ajax.markup.html.form.AjaxButton; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.DropDownChoice; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.IChoiceRenderer; +import org.apache.wicket.markup.repeater.Item; +import org.apache.wicket.markup.repeater.OddEvenItem; +import org.apache.wicket.markup.repeater.RefreshingView; +import org.apache.wicket.markup.repeater.util.ModelIteratorAdapter; +import org.apache.wicket.model.CompoundPropertyModel; +import org.apache.wicket.model.IModel; + +import com.gitblit.Constants.AccessPermission; +import com.gitblit.GitBlit; +import com.gitblit.models.RepositoryAccessPermission; +import com.gitblit.utils.DeepCopier; + +/** + * Allows user to manipulate repository access permissions. + * + * @author James Moger + * + */ +public class RepositoryPermissionsPanel extends BasePanel { + + private static final long serialVersionUID = 1L; + + public RepositoryPermissionsPanel(String wicketId, final List permissions, final Map translations) { + super(wicketId); + + // update existing permissions repeater + RefreshingView dataView = new RefreshingView("permissionRow") { + private static final long serialVersionUID = 1L; + + @Override + protected Iterator> getItemModels() { + // the iterator returns RepositoryPermission objects, but we need it to + // return models + return new ModelIteratorAdapter(permissions.iterator()) { + @Override + protected IModel model(RepositoryAccessPermission permission) { + return new CompoundPropertyModel(permission); + } + }; + } + + @Override + protected Item newItem(String id, int index, IModel model) { + // this item sets markup class attribute to either 'odd' or + // 'even' for decoration + return new OddEvenItem(id, index, model); + } + + public void populateItem(final Item item) { + final RepositoryAccessPermission entry = item.getModelObject(); + item.add(new Label("repository", entry.repository)); + + // use ajax to get immediate update of permission level change + // otherwise we can lose it if they change levels and then add + // a new repository permission + final DropDownChoice permissionChoice = new DropDownChoice( + "permission", Arrays.asList(AccessPermission.values()), new AccessPermissionRenderer(translations)); + permissionChoice.add(new AjaxFormComponentUpdatingBehavior("onchange") { + + private static final long serialVersionUID = 1L; + + protected void onUpdate(AjaxRequestTarget target) { + target.addComponent(permissionChoice); + } + }); + + item.add(permissionChoice); + } + }; + add(dataView); + setOutputMarkupId(true); + + // filter out repositories we already have permissions for + final List repositories = GitBlit.self().getRepositoryList(); + for (RepositoryAccessPermission rp : permissions) { + repositories.remove(rp.repository); + } + + // add new permission form + IModel addPermissionModel = new CompoundPropertyModel(new RepositoryAccessPermission()); + Form addPermissionForm = new Form("addPermissionForm", addPermissionModel); + addPermissionForm.add(new DropDownChoice("repository", repositories)); + addPermissionForm.add(new DropDownChoice("permission", Arrays + .asList(AccessPermission.NEWPERMISSIONS), new AccessPermissionRenderer(translations))); + AjaxButton button = new AjaxButton("addPermissionButton", addPermissionForm) { + + private static final long serialVersionUID = 1L; + + @Override + protected void onSubmit(AjaxRequestTarget target, Form form) { + // add permission to our list + RepositoryAccessPermission rp = (RepositoryAccessPermission) form.getModel().getObject(); + permissions.add(DeepCopier.copy(rp)); + + // remove repository from available choices + repositories.remove(rp.repository); + + // force the panel to refresh + target.addComponent(RepositoryPermissionsPanel.this); + } + }; + addPermissionForm.add(button); + + // only show add permission form if we have a repository choice + add(addPermissionForm.setVisible(repositories.size() > 0)); + } + + private class AccessPermissionRenderer implements IChoiceRenderer { + + private static final long serialVersionUID = 1L; + + private final Map map; + + public AccessPermissionRenderer(Map map) { + this.map = map; + } + + @Override + public String getDisplayValue(AccessPermission type) { + return map.get(type); + } + + @Override + public String getIdValue(AccessPermission type, int index) { + return Integer.toString(index); + } + } +}