New ProjectAccessScreen to edit access controls
Change-Id: Ica21a1b3bb8b3b1f3e2e4f4964f25f9c8a1741be Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
		@@ -110,6 +110,17 @@ public class AccessSection implements Comparable<AccessSection> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void mergeFrom(AccessSection section) {
 | 
				
			||||||
 | 
					    for (Permission src : section.getPermissions()) {
 | 
				
			||||||
 | 
					      Permission dst = getPermission(src.getName());
 | 
				
			||||||
 | 
					      if (dst != null) {
 | 
				
			||||||
 | 
					        dst.mergeFrom(src);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        permissions.add(dst);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public int compareTo(AccessSection o) {
 | 
					  public int compareTo(AccessSection o) {
 | 
				
			||||||
    return comparePattern().compareTo(o.comparePattern());
 | 
					    return comparePattern().compareTo(o.comparePattern());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,8 +21,8 @@ import com.google.gerrit.reviewdb.AccountGroupMember;
 | 
				
			|||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
 | 
					import com.google.gwt.user.client.rpc.AsyncCallback;
 | 
				
			||||||
import com.google.gwtjsonrpc.client.RemoteJsonService;
 | 
					import com.google.gwtjsonrpc.client.RemoteJsonService;
 | 
				
			||||||
import com.google.gwtjsonrpc.client.RpcImpl;
 | 
					import com.google.gwtjsonrpc.client.RpcImpl;
 | 
				
			||||||
import com.google.gwtjsonrpc.client.VoidResult;
 | 
					 | 
				
			||||||
import com.google.gwtjsonrpc.client.RpcImpl.Version;
 | 
					import com.google.gwtjsonrpc.client.RpcImpl.Version;
 | 
				
			||||||
 | 
					import com.google.gwtjsonrpc.client.VoidResult;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
@@ -36,7 +36,8 @@ public interface GroupAdminService extends RemoteJsonService {
 | 
				
			|||||||
  void createGroup(String newName, AsyncCallback<AccountGroup.Id> callback);
 | 
					  void createGroup(String newName, AsyncCallback<AccountGroup.Id> callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @SignInRequired
 | 
					  @SignInRequired
 | 
				
			||||||
  void groupDetail(AccountGroup.Id groupId, AsyncCallback<GroupDetail> callback);
 | 
					  void groupDetail(AccountGroup.Id groupId, AccountGroup.UUID uuid,
 | 
				
			||||||
 | 
					      AsyncCallback<GroupDetail> callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @SignInRequired
 | 
					  @SignInRequired
 | 
				
			||||||
  void changeGroupDescription(AccountGroup.Id groupId, String description,
 | 
					  void changeGroupDescription(AccountGroup.Id groupId, String description,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ public class GroupReference implements Comparable<GroupReference> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void setUUID(AccountGroup.UUID newUUID) {
 | 
					  public void setUUID(AccountGroup.UUID newUUID) {
 | 
				
			||||||
    uuid = newUUID.get();
 | 
					    uuid = newUUID != null ? newUUID.get() : null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public String getName() {
 | 
					  public String getName() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,14 +94,14 @@ public class Permission implements Comparable<Permission> {
 | 
				
			|||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public boolean getExclusiveGroup() {
 | 
					  public Boolean getExclusiveGroup() {
 | 
				
			||||||
    // Only permit exclusive group behavior on non OWNER permissions,
 | 
					    // Only permit exclusive group behavior on non OWNER permissions,
 | 
				
			||||||
    // otherwise an owner might lose access to a delegated subspace.
 | 
					    // otherwise an owner might lose access to a delegated subspace.
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
    return exclusiveGroup && !OWNER.equals(getName());
 | 
					    return exclusiveGroup && !OWNER.equals(getName());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void setExclusiveGroup(boolean newExclusiveGroup) {
 | 
					  public void setExclusiveGroup(Boolean newExclusiveGroup) {
 | 
				
			||||||
    exclusiveGroup = newExclusiveGroup;
 | 
					    exclusiveGroup = newExclusiveGroup;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -157,6 +157,17 @@ public class Permission implements Comparable<Permission> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void mergeFrom(Permission src) {
 | 
				
			||||||
 | 
					    for (PermissionRule srcRule : src.getRules()) {
 | 
				
			||||||
 | 
					      PermissionRule dstRule = getRule(srcRule.getGroup());
 | 
				
			||||||
 | 
					      if (dstRule != null) {
 | 
				
			||||||
 | 
					        dstRule.mergeFrom(srcRule);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        add(srcRule);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static boolean sameGroup(PermissionRule rule, GroupReference group) {
 | 
					  private static boolean sameGroup(PermissionRule rule, GroupReference group) {
 | 
				
			||||||
    if (group.getUUID() != null) {
 | 
					    if (group.getUUID() != null) {
 | 
				
			||||||
      return group.getUUID().equals(rule.getGroup().getUUID());
 | 
					      return group.getUUID().equals(rule.getGroup().getUUID());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,10 @@
 | 
				
			|||||||
package com.google.gerrit.common.data;
 | 
					package com.google.gerrit.common.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class PermissionRule implements Comparable<PermissionRule> {
 | 
					public class PermissionRule implements Comparable<PermissionRule> {
 | 
				
			||||||
 | 
					  public static enum Action {
 | 
				
			||||||
 | 
					    ALLOW, DENY;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  protected boolean deny;
 | 
					  protected boolean deny;
 | 
				
			||||||
  protected boolean force;
 | 
					  protected boolean force;
 | 
				
			||||||
  protected int min;
 | 
					  protected int min;
 | 
				
			||||||
@@ -28,6 +32,17 @@ public class PermissionRule implements Comparable<PermissionRule> {
 | 
				
			|||||||
    this.group = group;
 | 
					    this.group = group;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Action getAction() {
 | 
				
			||||||
 | 
					    return deny ? Action.DENY : Action.ALLOW;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setAction(Action action) {
 | 
				
			||||||
 | 
					    if (action == null) {
 | 
				
			||||||
 | 
					      throw new NullPointerException("action");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setDeny(action == Action.DENY);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public boolean getDeny() {
 | 
					  public boolean getDeny() {
 | 
				
			||||||
    return deny;
 | 
					    return deny;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -36,11 +51,11 @@ public class PermissionRule implements Comparable<PermissionRule> {
 | 
				
			|||||||
    deny = newDeny;
 | 
					    deny = newDeny;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public boolean getForce() {
 | 
					  public Boolean getForce() {
 | 
				
			||||||
    return force;
 | 
					    return force;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void setForce(boolean newForce) {
 | 
					  public void setForce(Boolean newForce) {
 | 
				
			||||||
    force = newForce;
 | 
					    force = newForce;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -78,6 +93,12 @@ public class PermissionRule implements Comparable<PermissionRule> {
 | 
				
			|||||||
    group = newGroup;
 | 
					    group = newGroup;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void mergeFrom(PermissionRule src) {
 | 
				
			||||||
 | 
					    setDeny(getDeny() || src.getDeny());
 | 
				
			||||||
 | 
					    setForce(getForce() || src.getForce());
 | 
				
			||||||
 | 
					    setRange(Math.min(getMin(), src.getMin()), Math.max(getMax(), src.getMax()));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public int compareTo(PermissionRule o) {
 | 
					  public int compareTo(PermissionRule o) {
 | 
				
			||||||
    int cmp = deny(this) - deny(o);
 | 
					    int cmp = deny(this) - deny(o);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.common.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ProjectAccess {
 | 
				
			||||||
 | 
					  protected String revision;
 | 
				
			||||||
 | 
					  protected Project.NameKey inheritsFrom;
 | 
				
			||||||
 | 
					  protected List<AccessSection> local;
 | 
				
			||||||
 | 
					  protected Set<String> ownerOf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public ProjectAccess() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public String getRevision() {
 | 
				
			||||||
 | 
					    return revision;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setRevision(String name) {
 | 
				
			||||||
 | 
					    revision = name;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Project.NameKey getInheritsFrom() {
 | 
				
			||||||
 | 
					    return inheritsFrom;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setInheritsFrom(Project.NameKey name) {
 | 
				
			||||||
 | 
					    inheritsFrom = name;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public List<AccessSection> getLocal() {
 | 
				
			||||||
 | 
					    return local;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setLocal(List<AccessSection> as) {
 | 
				
			||||||
 | 
					    local = as;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public boolean isOwnerOf(AccessSection section) {
 | 
				
			||||||
 | 
					    return getOwnerOf().contains(section.getRefPattern());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Set<String> getOwnerOf() {
 | 
				
			||||||
 | 
					    return ownerOf;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setOwnerOf(Set<String> refs) {
 | 
				
			||||||
 | 
					    ownerOf = refs;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -32,10 +32,18 @@ public interface ProjectAdminService extends RemoteJsonService {
 | 
				
			|||||||
  void projectDetail(Project.NameKey projectName,
 | 
					  void projectDetail(Project.NameKey projectName,
 | 
				
			||||||
      AsyncCallback<ProjectDetail> callback);
 | 
					      AsyncCallback<ProjectDetail> callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void projectAccess(Project.NameKey projectName,
 | 
				
			||||||
 | 
					      AsyncCallback<ProjectAccess> callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @SignInRequired
 | 
					  @SignInRequired
 | 
				
			||||||
  void changeProjectSettings(Project update,
 | 
					  void changeProjectSettings(Project update,
 | 
				
			||||||
      AsyncCallback<ProjectDetail> callback);
 | 
					      AsyncCallback<ProjectDetail> callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @SignInRequired
 | 
				
			||||||
 | 
					  void changeProjectAccess(Project.NameKey projectName, String baseRevision,
 | 
				
			||||||
 | 
					      String message, List<AccessSection> sections,
 | 
				
			||||||
 | 
					      AsyncCallback<ProjectAccess> callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void listBranches(Project.NameKey projectName,
 | 
					  void listBranches(Project.NameKey projectName,
 | 
				
			||||||
      AsyncCallback<ListBranchesResult> callback);
 | 
					      AsyncCallback<ListBranchesResult> callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.common.data;
 | 
					package com.google.gerrit.common.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGroupName;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
 | 
					import com.google.gwt.user.client.rpc.AsyncCallback;
 | 
				
			||||||
import com.google.gwtjsonrpc.client.RemoteJsonService;
 | 
					import com.google.gwtjsonrpc.client.RemoteJsonService;
 | 
				
			||||||
@@ -32,5 +31,5 @@ public interface SuggestService extends RemoteJsonService {
 | 
				
			|||||||
      AsyncCallback<List<AccountInfo>> callback);
 | 
					      AsyncCallback<List<AccountInfo>> callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void suggestAccountGroup(String query, int limit,
 | 
					  void suggestAccountGroup(String query, int limit,
 | 
				
			||||||
      AsyncCallback<List<AccountGroupName>> callback);
 | 
					      AsyncCallback<List<GroupReference>> callback);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
 limitations under the License.
 | 
					 limitations under the License.
 | 
				
			||||||
-->
 | 
					-->
 | 
				
			||||||
<module rename-to="gerrit">
 | 
					<module rename-to="gerrit">
 | 
				
			||||||
 | 
					  <inherits name='com.google.gwt.editor.Editor'/>
 | 
				
			||||||
  <inherits name='com.google.gwt.user.User'/>
 | 
					  <inherits name='com.google.gwt.user.User'/>
 | 
				
			||||||
  <inherits name='com.google.gwt.resources.Resources'/>
 | 
					  <inherits name='com.google.gwt.resources.Resources'/>
 | 
				
			||||||
  <inherits name='com.google.gwt.user.theme.chrome.Chrome'/>
 | 
					  <inherits name='com.google.gwt.user.theme.chrome.Chrome'/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@ import com.google.gerrit.client.account.RegisterScreen;
 | 
				
			|||||||
import com.google.gerrit.client.account.ValidateEmailScreen;
 | 
					import com.google.gerrit.client.account.ValidateEmailScreen;
 | 
				
			||||||
import com.google.gerrit.client.admin.AccountGroupScreen;
 | 
					import com.google.gerrit.client.admin.AccountGroupScreen;
 | 
				
			||||||
import com.google.gerrit.client.admin.GroupListScreen;
 | 
					import com.google.gerrit.client.admin.GroupListScreen;
 | 
				
			||||||
 | 
					import com.google.gerrit.client.admin.ProjectAccessScreen;
 | 
				
			||||||
import com.google.gerrit.client.admin.ProjectBranchesScreen;
 | 
					import com.google.gerrit.client.admin.ProjectBranchesScreen;
 | 
				
			||||||
import com.google.gerrit.client.admin.ProjectInfoScreen;
 | 
					import com.google.gerrit.client.admin.ProjectInfoScreen;
 | 
				
			||||||
import com.google.gerrit.client.admin.ProjectListScreen;
 | 
					import com.google.gerrit.client.admin.ProjectListScreen;
 | 
				
			||||||
@@ -86,6 +87,10 @@ public class Dispatcher {
 | 
				
			|||||||
    return "admin,group," + id.toString();
 | 
					    return "admin,group," + id.toString();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static String toGroup(final AccountGroup.UUID uuid) {
 | 
				
			||||||
 | 
					    return "admin,group,uuid-" + uuid.toString();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static String toProjectAdmin(final Project.NameKey n, final String tab) {
 | 
					  public static String toProjectAdmin(final Project.NameKey n, final String tab) {
 | 
				
			||||||
    return "admin,project," + n.toString() + "," + tab;
 | 
					    return "admin,project," + n.toString() + "," + tab;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -410,6 +415,10 @@ public class Dispatcher {
 | 
				
			|||||||
      private Screen select() {
 | 
					      private Screen select() {
 | 
				
			||||||
        String p;
 | 
					        String p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        p = "admin,group,uuid-";
 | 
				
			||||||
 | 
					        if (token.startsWith(p))
 | 
				
			||||||
 | 
					          return new AccountGroupScreen(AccountGroup.UUID.parse(skip(p, token)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        p = "admin,group,";
 | 
					        p = "admin,group,";
 | 
				
			||||||
        if (token.startsWith(p))
 | 
					        if (token.startsWith(p))
 | 
				
			||||||
          return new AccountGroupScreen(AccountGroup.Id.parse(skip(p, token)));
 | 
					          return new AccountGroupScreen(AccountGroup.Id.parse(skip(p, token)));
 | 
				
			||||||
@@ -431,7 +440,7 @@ public class Dispatcher {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (ProjectScreen.ACCESS.equals(p)) {
 | 
					          if (ProjectScreen.ACCESS.equals(p)) {
 | 
				
			||||||
            return new NotFoundScreen();
 | 
					            return new ProjectAccessScreen(k);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          return new NotFoundScreen();
 | 
					          return new NotFoundScreen();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,233 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.client.Gerrit;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ApprovalType;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ProjectAccess;
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.GWT;
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.Scheduler;
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.DivElement;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.SpanElement;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.Style.Display;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.Editor;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.EditorDelegate;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.ValueAwareEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.adapters.EditorSource;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.adapters.ListEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.ClickEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.MouseOutEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.MouseOverEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.ValueChangeEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.ValueChangeHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiBinder;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiField;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Anchor;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Composite;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.FlowPanel;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.HTMLPanel;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.ValueListBox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class AccessSectionEditor extends Composite implements
 | 
				
			||||||
 | 
					    Editor<AccessSection>, ValueAwareEditor<AccessSection> {
 | 
				
			||||||
 | 
					  interface Binder extends UiBinder<HTMLPanel, AccessSectionEditor> {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final Binder uiBinder = GWT.create(Binder.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  ValueEditor<String> refPattern;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  FlowPanel permissionContainer;
 | 
				
			||||||
 | 
					  ListEditor<Permission, PermissionEditor> permissions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement addContainer;
 | 
				
			||||||
 | 
					  @UiField(provided = true)
 | 
				
			||||||
 | 
					  @Editor.Ignore
 | 
				
			||||||
 | 
					  ValueListBox<String> permissionSelector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  SpanElement deletedName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Anchor deleteSection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement normal;
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement deleted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private final ProjectAccess projectAccess;
 | 
				
			||||||
 | 
					  private AccessSection value;
 | 
				
			||||||
 | 
					  private boolean readOnly;
 | 
				
			||||||
 | 
					  private boolean isDeleted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public AccessSectionEditor(ProjectAccess access) {
 | 
				
			||||||
 | 
					    projectAccess = access;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    permissionSelector =
 | 
				
			||||||
 | 
					        new ValueListBox<String>(PermissionNameRenderer.INSTANCE);
 | 
				
			||||||
 | 
					    permissionSelector.addValueChangeHandler(new ValueChangeHandler<String>() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void onValueChange(ValueChangeEvent<String> event) {
 | 
				
			||||||
 | 
					        if (!Util.C.addPermission().equals(event.getValue())) {
 | 
				
			||||||
 | 
					          onAddPermission(event.getValue());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initWidget(uiBinder.createAndBindUi(this));
 | 
				
			||||||
 | 
					    permissions = ListEditor.of(new PermissionEditorSource());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("deleteSection")
 | 
				
			||||||
 | 
					  void onDeleteHover(MouseOverEvent event) {
 | 
				
			||||||
 | 
					    normal.addClassName(AdminResources.I.css().deleteSectionHover());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("deleteSection")
 | 
				
			||||||
 | 
					  void onDeleteNonHover(MouseOutEvent event) {
 | 
				
			||||||
 | 
					    normal.removeClassName(AdminResources.I.css().deleteSectionHover());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("deleteSection")
 | 
				
			||||||
 | 
					  void onDeleteSection(ClickEvent event) {
 | 
				
			||||||
 | 
					    isDeleted = true;
 | 
				
			||||||
 | 
					    deletedName.setInnerText(refPattern.getValue());
 | 
				
			||||||
 | 
					    normal.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    deleted.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("undoDelete")
 | 
				
			||||||
 | 
					  void onUndoDelete(ClickEvent event) {
 | 
				
			||||||
 | 
					    isDeleted = false;
 | 
				
			||||||
 | 
					    deleted.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    normal.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void onAddPermission(String varName) {
 | 
				
			||||||
 | 
					    Permission p = value.getPermission(varName, true);
 | 
				
			||||||
 | 
					    permissions.getList().add(p);
 | 
				
			||||||
 | 
					    rebuildPermissionSelector();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void editRefPattern() {
 | 
				
			||||||
 | 
					    refPattern.edit();
 | 
				
			||||||
 | 
					    Scheduler.get().scheduleDeferred(new ScheduledCommand() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void execute() {
 | 
				
			||||||
 | 
					        refPattern.setFocus(true);
 | 
				
			||||||
 | 
					      }});
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void enableEditing() {
 | 
				
			||||||
 | 
					    readOnly = false;
 | 
				
			||||||
 | 
					    addContainer.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					    rebuildPermissionSelector();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  boolean isDeleted() {
 | 
				
			||||||
 | 
					    return isDeleted;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setValue(AccessSection value) {
 | 
				
			||||||
 | 
					    this.value = value;
 | 
				
			||||||
 | 
					    this.readOnly = !projectAccess.isOwnerOf(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    refPattern.setEnabled(!readOnly);
 | 
				
			||||||
 | 
					    deleteSection.setVisible(!readOnly);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (readOnly) {
 | 
				
			||||||
 | 
					      addContainer.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      enableEditing();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void rebuildPermissionSelector() {
 | 
				
			||||||
 | 
					    List<String> perms = new ArrayList<String>();
 | 
				
			||||||
 | 
					    for (ApprovalType t : Gerrit.getConfig().getApprovalTypes()
 | 
				
			||||||
 | 
					        .getApprovalTypes()) {
 | 
				
			||||||
 | 
					      String varName = Permission.LABEL + t.getCategory().getLabelName();
 | 
				
			||||||
 | 
					      if (value.getPermission(varName) == null) {
 | 
				
			||||||
 | 
					        perms.add(varName);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (String varName : Util.C.permissionNames().keySet()) {
 | 
				
			||||||
 | 
					      if (value.getPermission(varName) == null) {
 | 
				
			||||||
 | 
					        perms.add(varName);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (perms.isEmpty()) {
 | 
				
			||||||
 | 
					      addContainer.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      addContainer.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					      perms.add(0, Util.C.addPermission());
 | 
				
			||||||
 | 
					      permissionSelector.setValue(Util.C.addPermission());
 | 
				
			||||||
 | 
					      permissionSelector.setAcceptableValues(perms);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void flush() {
 | 
				
			||||||
 | 
					    List<Permission> src = permissions.getList();
 | 
				
			||||||
 | 
					    List<Permission> keep = new ArrayList<Permission>(src.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int i = 0; i < src.size(); i++) {
 | 
				
			||||||
 | 
					      PermissionEditor e = (PermissionEditor) permissionContainer.getWidget(i);
 | 
				
			||||||
 | 
					      if (!e.isDeleted()) {
 | 
				
			||||||
 | 
					        keep.add(src.get(i));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    value.setPermissions(keep);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void onPropertyChange(String... paths) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setDelegate(EditorDelegate<AccessSection> delegate) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private class PermissionEditorSource extends EditorSource<PermissionEditor> {
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public PermissionEditor create(int index) {
 | 
				
			||||||
 | 
					      PermissionEditor subEditor = new PermissionEditor(readOnly, value);
 | 
				
			||||||
 | 
					      permissionContainer.insert(subEditor, index);
 | 
				
			||||||
 | 
					      return subEditor;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void dispose(PermissionEditor subEditor) {
 | 
				
			||||||
 | 
					      subEditor.removeFromParent();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setIndex(PermissionEditor subEditor, int index) {
 | 
				
			||||||
 | 
					      permissionContainer.insert(subEditor, index);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,157 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<!--
 | 
				
			||||||
 | 
					Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<ui:UiBinder
 | 
				
			||||||
 | 
					  xmlns:ui='urn:ui:com.google.gwt.uibinder'
 | 
				
			||||||
 | 
					  xmlns:g='urn:import:com.google.gwt.user.client.ui'
 | 
				
			||||||
 | 
					  xmlns:e='urn:import:com.google.gwt.editor.ui.client'
 | 
				
			||||||
 | 
					  xmlns:my='urn:import:com.google.gerrit.client.admin'
 | 
				
			||||||
 | 
					  ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
 | 
				
			||||||
 | 
					  ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
 | 
				
			||||||
 | 
					  ui:generateLocales='default,en'
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
 | 
				
			||||||
 | 
					<ui:style>
 | 
				
			||||||
 | 
					  @eval selectionColor com.google.gerrit.client.Gerrit.getConfig().getSelectionColor();
 | 
				
			||||||
 | 
					  @eval trimColor com.google.gerrit.client.Gerrit.getConfig().getTrimColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .panel {
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .content {
 | 
				
			||||||
 | 
					    margin-top: 4px;
 | 
				
			||||||
 | 
					    margin-bottom: 4px;
 | 
				
			||||||
 | 
					    padding-bottom: 2px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .normal {
 | 
				
			||||||
 | 
					    background-color: trimColor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .deleted {
 | 
				
			||||||
 | 
					    padding-left: 7px;
 | 
				
			||||||
 | 
					    padding-bottom: 2px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .header {
 | 
				
			||||||
 | 
					    padding-left: 5px;
 | 
				
			||||||
 | 
					    padding-right: 5px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .headerText {
 | 
				
			||||||
 | 
					    vertical-align: top;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
 | 
					    font-weight: bold;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .headerTable {
 | 
				
			||||||
 | 
					    border: 0;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    padding-right: 40px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .header:hover {
 | 
				
			||||||
 | 
					    background-color: selectionColor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .refName {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .refNameEdit {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .permissionList {
 | 
				
			||||||
 | 
					    margin-left: 5px;
 | 
				
			||||||
 | 
					    margin-right: 5px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .addContainer {
 | 
				
			||||||
 | 
					    padding-left: 16px;
 | 
				
			||||||
 | 
					    padding-right: 16px;
 | 
				
			||||||
 | 
					    font-size: 80%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .addContainer:hover {
 | 
				
			||||||
 | 
					    background-color: selectionColor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .addSelector {
 | 
				
			||||||
 | 
					    font-size: 80%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .deleteIcon {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 5px;
 | 
				
			||||||
 | 
					    right: 17px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .undoIcon {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 2px;
 | 
				
			||||||
 | 
					    right: 17px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					</ui:style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<g:HTMLPanel styleName='{style.panel}'>
 | 
				
			||||||
 | 
					<div ui:field='normal' class='{style.normal} {style.content}'>
 | 
				
			||||||
 | 
					  <div class='{style.header}'>
 | 
				
			||||||
 | 
					    <table class='{style.headerTable}'><tr>
 | 
				
			||||||
 | 
					      <td class='{style.headerText}'><ui:msg>Reference:</ui:msg></td>
 | 
				
			||||||
 | 
					      <td width='100%'>
 | 
				
			||||||
 | 
					        <my:ValueEditor
 | 
				
			||||||
 | 
					            ui:field='refPattern'
 | 
				
			||||||
 | 
					            addStyleNames='{style.refName}'
 | 
				
			||||||
 | 
					            editTitle='Edit reference pattern'>
 | 
				
			||||||
 | 
					          <ui:attribute name='editTitle'/>
 | 
				
			||||||
 | 
					          <my:editor>
 | 
				
			||||||
 | 
					            <my:RefPatternBox styleName='{style.refNameEdit}'/>
 | 
				
			||||||
 | 
					          </my:editor>
 | 
				
			||||||
 | 
					        </my:ValueEditor>
 | 
				
			||||||
 | 
					      </td>
 | 
				
			||||||
 | 
					    </tr></table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <g:Anchor
 | 
				
			||||||
 | 
					        ui:field='deleteSection'
 | 
				
			||||||
 | 
					        href='javascript:void'
 | 
				
			||||||
 | 
					        styleName='{style.deleteIcon} {res.css.deleteIcon}'
 | 
				
			||||||
 | 
					        title='Delete this section (and nested rules)'>
 | 
				
			||||||
 | 
					      <ui:attribute name='title'/>
 | 
				
			||||||
 | 
					    </g:Anchor>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <g:FlowPanel
 | 
				
			||||||
 | 
					      ui:field='permissionContainer'
 | 
				
			||||||
 | 
					      styleName='{style.permissionList}'/>
 | 
				
			||||||
 | 
					  <div ui:field='addContainer' class='{style.addContainer}'>
 | 
				
			||||||
 | 
					    <g:ValueListBox
 | 
				
			||||||
 | 
					        ui:field='permissionSelector'
 | 
				
			||||||
 | 
					        styleName='{style.addSelector}' />
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
					    ui:field='deleted'
 | 
				
			||||||
 | 
					    class='{style.deleted} {res.css.deleted}'
 | 
				
			||||||
 | 
					    style='display: none'>
 | 
				
			||||||
 | 
					  <ui:msg>Reference <span ui:field='deletedName'/> was deleted</ui:msg>
 | 
				
			||||||
 | 
					  <g:Anchor
 | 
				
			||||||
 | 
					      ui:field='undoDelete'
 | 
				
			||||||
 | 
					      href='javascript:void'
 | 
				
			||||||
 | 
					      styleName='{style.undoIcon} {res.css.undoIcon}'
 | 
				
			||||||
 | 
					      title='Undo deletion'>
 | 
				
			||||||
 | 
					    <ui:attribute name='title'/>
 | 
				
			||||||
 | 
					  </g:Anchor>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</g:HTMLPanel>
 | 
				
			||||||
 | 
					</ui:UiBinder>
 | 
				
			||||||
@@ -63,7 +63,9 @@ import java.util.HashSet;
 | 
				
			|||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class AccountGroupScreen extends AccountScreen {
 | 
					public class AccountGroupScreen extends AccountScreen {
 | 
				
			||||||
  private final AccountGroup.Id groupId;
 | 
					  private AccountGroup.Id groupId;
 | 
				
			||||||
 | 
					  private AccountGroup.UUID groupUUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private AccountInfoCache accounts = AccountInfoCache.empty();
 | 
					  private AccountInfoCache accounts = AccountInfoCache.empty();
 | 
				
			||||||
  private GroupInfoCache groups = GroupInfoCache.empty();
 | 
					  private GroupInfoCache groups = GroupInfoCache.empty();
 | 
				
			||||||
  private MemberTable members;
 | 
					  private MemberTable members;
 | 
				
			||||||
@@ -106,13 +108,21 @@ public class AccountGroupScreen extends AccountScreen {
 | 
				
			|||||||
    groupId = toShow;
 | 
					    groupId = toShow;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public AccountGroupScreen(final AccountGroup.UUID toShow) {
 | 
				
			||||||
 | 
					    groupUUID = toShow;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  protected void onLoad() {
 | 
					  protected void onLoad() {
 | 
				
			||||||
    super.onLoad();
 | 
					    super.onLoad();
 | 
				
			||||||
    Util.GROUP_SVC.groupDetail(groupId, new ScreenLoadCallback<GroupDetail>(
 | 
					    Util.GROUP_SVC.groupDetail(groupId, groupUUID,
 | 
				
			||||||
        this) {
 | 
					        new ScreenLoadCallback<GroupDetail>(this) {
 | 
				
			||||||
          @Override
 | 
					          @Override
 | 
				
			||||||
          protected void preDisplay(final GroupDetail result) {
 | 
					          protected void preDisplay(final GroupDetail result) {
 | 
				
			||||||
 | 
					            groupId = result.group.getId();
 | 
				
			||||||
 | 
					            groupUUID = result.group.getGroupUUID();
 | 
				
			||||||
 | 
					            display(result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            enableForm(result.canModify);
 | 
					            enableForm(result.canModify);
 | 
				
			||||||
            saveName.setVisible(result.canModify);
 | 
					            saveName.setVisible(result.canModify);
 | 
				
			||||||
            saveOwner.setVisible(result.canModify);
 | 
					            saveOwner.setVisible(result.canModify);
 | 
				
			||||||
@@ -121,7 +131,6 @@ public class AccountGroupScreen extends AccountScreen {
 | 
				
			|||||||
            delMember.setVisible(result.canModify);
 | 
					            delMember.setVisible(result.canModify);
 | 
				
			||||||
            saveType.setVisible(result.canModify);
 | 
					            saveType.setVisible(result.canModify);
 | 
				
			||||||
            delInclude.setVisible(result.canModify);
 | 
					            delInclude.setVisible(result.canModify);
 | 
				
			||||||
        display(result);
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,8 @@ package com.google.gerrit.client.admin;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.google.gwt.i18n.client.Constants;
 | 
					import com.google.gwt.i18n.client.Constants;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public interface AdminConstants extends Constants {
 | 
					public interface AdminConstants extends Constants {
 | 
				
			||||||
  String defaultAccountName();
 | 
					  String defaultAccountName();
 | 
				
			||||||
  String defaultAccountGroupName();
 | 
					  String defaultAccountGroupName();
 | 
				
			||||||
@@ -101,4 +103,14 @@ public interface AdminConstants extends Constants {
 | 
				
			|||||||
  String noGroupSelected();
 | 
					  String noGroupSelected();
 | 
				
			||||||
  String errorNoMatchingGroups();
 | 
					  String errorNoMatchingGroups();
 | 
				
			||||||
  String errorNoGitRepository();
 | 
					  String errorNoGitRepository();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String addPermission();
 | 
				
			||||||
 | 
					  Map<String,String> permissionNames();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String refErrorEmpty();
 | 
				
			||||||
 | 
					  String refErrorBeginSlash();
 | 
				
			||||||
 | 
					  String refErrorDoubleSlash();
 | 
				
			||||||
 | 
					  String refErrorNoSpace();
 | 
				
			||||||
 | 
					  String refErrorPrintable();
 | 
				
			||||||
 | 
					  String errorsMustBeFixed();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,3 +83,36 @@ projectAdminTabAccess = Access
 | 
				
			|||||||
noGroupSelected = (No group selected)
 | 
					noGroupSelected = (No group selected)
 | 
				
			||||||
errorNoMatchingGroups = No Matching Groups
 | 
					errorNoMatchingGroups = No Matching Groups
 | 
				
			||||||
errorNoGitRepository = No Git Repository
 | 
					errorNoGitRepository = No Git Repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					addPermission = Add Permission ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Permission Names
 | 
				
			||||||
 | 
					permissionNames = \
 | 
				
			||||||
 | 
						create, \
 | 
				
			||||||
 | 
						forgeAuthor, \
 | 
				
			||||||
 | 
						forgeCommitter, \
 | 
				
			||||||
 | 
						forgeServerAsCommitter, \
 | 
				
			||||||
 | 
						owner, \
 | 
				
			||||||
 | 
						push, \
 | 
				
			||||||
 | 
						pushMerge, \
 | 
				
			||||||
 | 
						pushTag, \
 | 
				
			||||||
 | 
						read, \
 | 
				
			||||||
 | 
						submit
 | 
				
			||||||
 | 
					create = Create Reference
 | 
				
			||||||
 | 
					forgeAuthor = Forge Author Identity
 | 
				
			||||||
 | 
					forgeCommitter = Forge Committer Identity
 | 
				
			||||||
 | 
					forgeServerAsCommitter = Forge Server Identity
 | 
				
			||||||
 | 
					owner = Owner
 | 
				
			||||||
 | 
					push = Push
 | 
				
			||||||
 | 
					pushMerge = Push Merge Commit
 | 
				
			||||||
 | 
					pushTag = Push Annotated Tag
 | 
				
			||||||
 | 
					read = Read
 | 
				
			||||||
 | 
					submit = Submit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					refErrorEmpty = Reference must be supplied
 | 
				
			||||||
 | 
					refErrorBeginSlash = Reference must not start with '/'
 | 
				
			||||||
 | 
					refErrorDoubleSlash = References cannot contain '//'
 | 
				
			||||||
 | 
					refErrorNoSpace = References cannot contain spaces
 | 
				
			||||||
 | 
					refErrorPrintable = References may contain only printable characters
 | 
				
			||||||
 | 
					errorsMustBeFixed = Errors must be fixed before committing changes.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gwt.resources.client.CssResource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface AdminCss extends CssResource {
 | 
				
			||||||
 | 
					  String deleteIcon();
 | 
				
			||||||
 | 
					  String undoIcon();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String deleted();
 | 
				
			||||||
 | 
					  String deletedBorder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String deleteSectionHover();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -18,6 +18,7 @@ import com.google.gwt.i18n.client.Messages;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public interface AdminMessages extends Messages {
 | 
					public interface AdminMessages extends Messages {
 | 
				
			||||||
  String group(String name);
 | 
					  String group(String name);
 | 
				
			||||||
 | 
					  String label(String name);
 | 
				
			||||||
  String project(String name);
 | 
					  String project(String name);
 | 
				
			||||||
  String deletedGroup(int id);
 | 
					  String deletedGroup(int id);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
group = Group {0}
 | 
					group = Group {0}
 | 
				
			||||||
 | 
					label = Label {0}
 | 
				
			||||||
project = Project {0}
 | 
					project = Project {0}
 | 
				
			||||||
deletedGroup = Deleted Group {0}
 | 
					deletedGroup = Deleted Group {0}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.GWT;
 | 
				
			||||||
 | 
					import com.google.gwt.resources.client.ClientBundle;
 | 
				
			||||||
 | 
					import com.google.gwt.resources.client.ImageResource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface AdminResources extends ClientBundle {
 | 
				
			||||||
 | 
					  public static final AdminResources I = GWT.create(AdminResources.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Source("admin.css")
 | 
				
			||||||
 | 
					  AdminCss css();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Source("editText.png")
 | 
				
			||||||
 | 
					  public ImageResource editText();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Source("deleteNormal.png")
 | 
				
			||||||
 | 
					  public ImageResource deleteNormal();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Source("deleteHover.png")
 | 
				
			||||||
 | 
					  public ImageResource deleteHover();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Source("undoNormal.png")
 | 
				
			||||||
 | 
					  public ImageResource undoNormal();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
 | 
				
			||||||
 | 
					import com.google.gerrit.client.ui.RPCSuggestOracle;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.GroupReference;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.LeafValueEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.KeyCodes;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.KeyPressEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.KeyPressHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.KeyUpEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.KeyUpHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.CloseEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.CloseHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.HasCloseHandlers;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.HasSelectionHandlers;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.SelectionEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.SelectionHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.event.shared.HandlerRegistration;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Composite;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Focusable;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.SuggestBox;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
 | 
				
			||||||
 | 
					import com.google.gwtexpui.globalkey.client.NpTextBox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class GroupReferenceBox extends Composite implements
 | 
				
			||||||
 | 
					    LeafValueEditor<GroupReference>, HasSelectionHandlers<GroupReference>,
 | 
				
			||||||
 | 
					    HasCloseHandlers<GroupReferenceBox>, Focusable {
 | 
				
			||||||
 | 
					  private final DefaultSuggestionDisplay suggestions;
 | 
				
			||||||
 | 
					  private final NpTextBox textBox;
 | 
				
			||||||
 | 
					  private final AccountGroupSuggestOracle oracle;
 | 
				
			||||||
 | 
					  private final SuggestBox suggestBox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private boolean submitOnSelection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public GroupReferenceBox() {
 | 
				
			||||||
 | 
					    suggestions = new DefaultSuggestionDisplay();
 | 
				
			||||||
 | 
					    textBox = new NpTextBox();
 | 
				
			||||||
 | 
					    oracle = new AccountGroupSuggestOracle();
 | 
				
			||||||
 | 
					    suggestBox = new SuggestBox( //
 | 
				
			||||||
 | 
					        new RPCSuggestOracle(oracle), //
 | 
				
			||||||
 | 
					        textBox, //
 | 
				
			||||||
 | 
					        suggestions);
 | 
				
			||||||
 | 
					    initWidget(suggestBox);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    suggestBox.addKeyPressHandler(new KeyPressHandler() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void onKeyPress(KeyPressEvent event) {
 | 
				
			||||||
 | 
					        submitOnSelection = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (event.getCharCode() == KeyCodes.KEY_ENTER) {
 | 
				
			||||||
 | 
					          if (suggestions.isSuggestionListShowing()) {
 | 
				
			||||||
 | 
					            submitOnSelection = true;
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            SelectionEvent.fire(GroupReferenceBox.this, getValue());
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    suggestBox.addKeyUpHandler(new KeyUpHandler() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void onKeyUp(KeyUpEvent event) {
 | 
				
			||||||
 | 
					        if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
 | 
				
			||||||
 | 
					          suggestBox.setText("");
 | 
				
			||||||
 | 
					          CloseEvent.fire(GroupReferenceBox.this, GroupReferenceBox.this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    suggestBox.addSelectionHandler(new SelectionHandler<Suggestion>() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void onSelection(SelectionEvent<Suggestion> event) {
 | 
				
			||||||
 | 
					        if (submitOnSelection) {
 | 
				
			||||||
 | 
					          submitOnSelection = false;
 | 
				
			||||||
 | 
					          SelectionEvent.fire(GroupReferenceBox.this, getValue());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setVisibleLength(int len) {
 | 
				
			||||||
 | 
					    textBox.setVisibleLength(len);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public HandlerRegistration addSelectionHandler(
 | 
				
			||||||
 | 
					      SelectionHandler<GroupReference> handler) {
 | 
				
			||||||
 | 
					    return addHandler(handler, SelectionEvent.getType());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public HandlerRegistration addCloseHandler(
 | 
				
			||||||
 | 
					      CloseHandler<GroupReferenceBox> handler) {
 | 
				
			||||||
 | 
					    return addHandler(handler, CloseEvent.getType());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public GroupReference getValue() {
 | 
				
			||||||
 | 
					    String name = suggestBox.getText();
 | 
				
			||||||
 | 
					    if (name != null && !name.isEmpty()) {
 | 
				
			||||||
 | 
					      return new GroupReference(oracle.getUUID(name), name);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setValue(GroupReference value) {
 | 
				
			||||||
 | 
					    suggestBox.setText(value != null ? value.getName() : "");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public int getTabIndex() {
 | 
				
			||||||
 | 
					    return suggestBox.getTabIndex();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setTabIndex(int index) {
 | 
				
			||||||
 | 
					    suggestBox.setTabIndex(index);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setFocus(boolean focused) {
 | 
				
			||||||
 | 
					    suggestBox.setFocus(focused);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setAccessKey(char key) {
 | 
				
			||||||
 | 
					    suggestBox.setAccessKey(key);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,297 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.client.Gerrit;
 | 
				
			||||||
 | 
					import com.google.gerrit.client.rpc.GerritCallback;
 | 
				
			||||||
 | 
					import com.google.gerrit.client.ui.SuggestUtil;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ApprovalType;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.GroupReference;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.PermissionRule;
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.GWT;
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.Scheduler;
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.DivElement;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.Style.Display;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.Editor;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.EditorDelegate;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.ValueAwareEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.adapters.EditorSource;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.adapters.ListEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.ClickEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.MouseOutEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.MouseOverEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.CloseEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.logical.shared.SelectionEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiBinder;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiField;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Anchor;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Button;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.CheckBox;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Composite;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.FlowPanel;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.HTMLPanel;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.ValueLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PermissionEditor extends Composite implements Editor<Permission>,
 | 
				
			||||||
 | 
					    ValueAwareEditor<Permission> {
 | 
				
			||||||
 | 
					  interface Binder extends UiBinder<HTMLPanel, PermissionEditor> {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final Binder uiBinder = GWT.create(Binder.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField(provided = true)
 | 
				
			||||||
 | 
					  @Path("name")
 | 
				
			||||||
 | 
					  ValueLabel<String> normalName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField(provided = true)
 | 
				
			||||||
 | 
					  @Path("name")
 | 
				
			||||||
 | 
					  ValueLabel<String> deletedName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  CheckBox exclusiveGroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  FlowPanel ruleContainer;
 | 
				
			||||||
 | 
					  ListEditor<PermissionRule, PermissionRuleEditor> rules;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement addContainer;
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement addStage1;
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement addStage2;
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Anchor beginAddRule;
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  @Editor.Ignore
 | 
				
			||||||
 | 
					  GroupReferenceBox groupToAdd;
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Button addRule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Anchor deletePermission;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement normal;
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement deleted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private final boolean readOnly;
 | 
				
			||||||
 | 
					  private final AccessSection section;
 | 
				
			||||||
 | 
					  private Permission value;
 | 
				
			||||||
 | 
					  private ApprovalType rangeType;
 | 
				
			||||||
 | 
					  private boolean isDeleted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public PermissionEditor(boolean readOnly, AccessSection section) {
 | 
				
			||||||
 | 
					    this.readOnly = readOnly;
 | 
				
			||||||
 | 
					    this.section = section;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    normalName = new ValueLabel<String>(PermissionNameRenderer.INSTANCE);
 | 
				
			||||||
 | 
					    deletedName = new ValueLabel<String>(PermissionNameRenderer.INSTANCE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initWidget(uiBinder.createAndBindUi(this));
 | 
				
			||||||
 | 
					    rules = ListEditor.of(new RuleEditorSource());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    exclusiveGroup.setEnabled(!readOnly);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (readOnly) {
 | 
				
			||||||
 | 
					      addContainer.removeFromParent();
 | 
				
			||||||
 | 
					      addContainer = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      deletePermission.removeFromParent();
 | 
				
			||||||
 | 
					      deletePermission = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("deletePermission")
 | 
				
			||||||
 | 
					  void onDeleteHover(MouseOverEvent event) {
 | 
				
			||||||
 | 
					    addStyleName(AdminResources.I.css().deleteSectionHover());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("deletePermission")
 | 
				
			||||||
 | 
					  void onDeleteNonHover(MouseOutEvent event) {
 | 
				
			||||||
 | 
					    removeStyleName(AdminResources.I.css().deleteSectionHover());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("deletePermission")
 | 
				
			||||||
 | 
					  void onDeletePermission(ClickEvent event) {
 | 
				
			||||||
 | 
					    isDeleted = true;
 | 
				
			||||||
 | 
					    normal.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    deleted.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("undoDelete")
 | 
				
			||||||
 | 
					  void onUndoDelete(ClickEvent event) {
 | 
				
			||||||
 | 
					    isDeleted = false;
 | 
				
			||||||
 | 
					    deleted.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    normal.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("beginAddRule")
 | 
				
			||||||
 | 
					  void onBeginAddRule(ClickEvent event) {
 | 
				
			||||||
 | 
					    addStage1.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    addStage2.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Scheduler.get().scheduleDeferred(new ScheduledCommand() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void execute() {
 | 
				
			||||||
 | 
					        groupToAdd.setFocus(true);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("addRule")
 | 
				
			||||||
 | 
					  void onAddGroupByClick(ClickEvent event) {
 | 
				
			||||||
 | 
					    GroupReference ref = groupToAdd.getValue();
 | 
				
			||||||
 | 
					    if (ref != null) {
 | 
				
			||||||
 | 
					      addGroup(ref);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      groupToAdd.setFocus(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("groupToAdd")
 | 
				
			||||||
 | 
					  void onAddGroupByEnter(SelectionEvent<GroupReference> event) {
 | 
				
			||||||
 | 
					    GroupReference ref = event.getSelectedItem();
 | 
				
			||||||
 | 
					    if (ref != null) {
 | 
				
			||||||
 | 
					      addGroup(ref);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("groupToAdd")
 | 
				
			||||||
 | 
					  void onAbortAddGroup(CloseEvent<GroupReferenceBox> event) {
 | 
				
			||||||
 | 
					    hideAddGroup();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("hideAddGroup")
 | 
				
			||||||
 | 
					  void hideAddGroup(ClickEvent event) {
 | 
				
			||||||
 | 
					    hideAddGroup();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void hideAddGroup() {
 | 
				
			||||||
 | 
					    addStage1.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					    addStage2.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void addGroup(GroupReference ref) {
 | 
				
			||||||
 | 
					    if (ref.getUUID() != null) {
 | 
				
			||||||
 | 
					      if (value.getRule(ref) == null) {
 | 
				
			||||||
 | 
					        PermissionRule newRule = value.getRule(ref, true);
 | 
				
			||||||
 | 
					        if (rangeType != null) {
 | 
				
			||||||
 | 
					          int min = rangeType.getMin().getValue();
 | 
				
			||||||
 | 
					          int max = rangeType.getMax().getValue();
 | 
				
			||||||
 | 
					          newRule.setRange(min, max);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        rules.getList().add(newRule);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      groupToAdd.setValue(null);
 | 
				
			||||||
 | 
					      groupToAdd.setFocus(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // If the oracle didn't get to complete a UUID, resolve it now.
 | 
				
			||||||
 | 
					      //
 | 
				
			||||||
 | 
					      addRule.setEnabled(false);
 | 
				
			||||||
 | 
					      SuggestUtil.SVC.suggestAccountGroup(ref.getName(), 1,
 | 
				
			||||||
 | 
					          new GerritCallback<List<GroupReference>>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onSuccess(List<GroupReference> result) {
 | 
				
			||||||
 | 
					              addRule.setEnabled(true);
 | 
				
			||||||
 | 
					              if (result.size() == 1) {
 | 
				
			||||||
 | 
					                addGroup(result.get(0));
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                groupToAdd.setFocus(true);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onFailure(Throwable caught) {
 | 
				
			||||||
 | 
					              addRule.setEnabled(true);
 | 
				
			||||||
 | 
					              super.onFailure(caught);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  boolean isDeleted() {
 | 
				
			||||||
 | 
					    return isDeleted;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setValue(Permission value) {
 | 
				
			||||||
 | 
					    this.value = value;
 | 
				
			||||||
 | 
					    if (value.isLabel()) {
 | 
				
			||||||
 | 
					      rangeType =
 | 
				
			||||||
 | 
					          Gerrit.getConfig().getApprovalTypes().byLabel(value.getLabel());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      rangeType = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (value != null && Permission.OWNER.equals(value.getName())) {
 | 
				
			||||||
 | 
					      exclusiveGroup.setEnabled(false);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      exclusiveGroup.setEnabled(!readOnly);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void flush() {
 | 
				
			||||||
 | 
					    List<PermissionRule> src = rules.getList();
 | 
				
			||||||
 | 
					    List<PermissionRule> keep = new ArrayList<PermissionRule>(src.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int i = 0; i < src.size(); i++) {
 | 
				
			||||||
 | 
					      PermissionRuleEditor e =
 | 
				
			||||||
 | 
					          (PermissionRuleEditor) ruleContainer.getWidget(i);
 | 
				
			||||||
 | 
					      if (!e.isDeleted()) {
 | 
				
			||||||
 | 
					        keep.add(src.get(i));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    value.setRules(keep);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void onPropertyChange(String... paths) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setDelegate(EditorDelegate<Permission> delegate) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private class RuleEditorSource extends EditorSource<PermissionRuleEditor> {
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public PermissionRuleEditor create(int index) {
 | 
				
			||||||
 | 
					      PermissionRuleEditor subEditor =
 | 
				
			||||||
 | 
					          new PermissionRuleEditor(readOnly, section, value, rangeType);
 | 
				
			||||||
 | 
					      ruleContainer.insert(subEditor, index);
 | 
				
			||||||
 | 
					      return subEditor;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void dispose(PermissionRuleEditor subEditor) {
 | 
				
			||||||
 | 
					      subEditor.removeFromParent();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setIndex(PermissionRuleEditor subEditor, int index) {
 | 
				
			||||||
 | 
					      ruleContainer.insert(subEditor, index);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<!--
 | 
				
			||||||
 | 
					Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<ui:UiBinder
 | 
				
			||||||
 | 
					  xmlns:ui='urn:ui:com.google.gwt.uibinder'
 | 
				
			||||||
 | 
					  xmlns:g='urn:import:com.google.gwt.user.client.ui'
 | 
				
			||||||
 | 
					  xmlns:e='urn:import:com.google.gwt.editor.ui.client'
 | 
				
			||||||
 | 
					  xmlns:my='urn:import:com.google.gerrit.client.admin'
 | 
				
			||||||
 | 
					  ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
 | 
				
			||||||
 | 
					  ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
 | 
				
			||||||
 | 
					  ui:generateLocales='default,en'
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
 | 
				
			||||||
 | 
					<ui:style>
 | 
				
			||||||
 | 
					  @eval selectionColor com.google.gerrit.client.Gerrit.getConfig().getSelectionColor();
 | 
				
			||||||
 | 
					  @eval backgroundColor com.google.gerrit.client.Gerrit.getConfig().getBackgroundColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .panel {
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .normal {
 | 
				
			||||||
 | 
					    border: 1px solid backgroundColor;
 | 
				
			||||||
 | 
					    margin-top: -1px;
 | 
				
			||||||
 | 
					    margin-bottom: -1px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .header {
 | 
				
			||||||
 | 
					    padding-left: 5px;
 | 
				
			||||||
 | 
					    padding-right: 5px;
 | 
				
			||||||
 | 
					    padding-bottom: 1px;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .header:hover {
 | 
				
			||||||
 | 
					    background-color: selectionColor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .name {
 | 
				
			||||||
 | 
					    font-style: italic;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .exclusiveGroup {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    right: 36px;
 | 
				
			||||||
 | 
					    width: 7em;
 | 
				
			||||||
 | 
					    font-size: 80%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .addContainer {
 | 
				
			||||||
 | 
					    padding-left: 10px;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .addContainer:hover {
 | 
				
			||||||
 | 
					    background-color: selectionColor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .addLink {
 | 
				
			||||||
 | 
					    font-size: 80%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .deleteIcon {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 1px;
 | 
				
			||||||
 | 
					    right: 12px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					</ui:style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<g:HTMLPanel stylePrimaryName='{style.panel}'>
 | 
				
			||||||
 | 
					<div ui:field='normal' class='{style.normal}'>
 | 
				
			||||||
 | 
					  <div class='{style.header}'>
 | 
				
			||||||
 | 
					    <g:ValueLabel styleName='{style.name}' ui:field='normalName'/>
 | 
				
			||||||
 | 
					    <g:CheckBox
 | 
				
			||||||
 | 
					        ui:field='exclusiveGroup'
 | 
				
			||||||
 | 
					        addStyleNames='{style.exclusiveGroup}'
 | 
				
			||||||
 | 
					        text='Exclusive'>
 | 
				
			||||||
 | 
					      <ui:attribute name='text'/>
 | 
				
			||||||
 | 
					    </g:CheckBox>
 | 
				
			||||||
 | 
					  <g:Anchor
 | 
				
			||||||
 | 
					      ui:field='deletePermission'
 | 
				
			||||||
 | 
					      href='javascript:void'
 | 
				
			||||||
 | 
					      styleName='{style.deleteIcon} {res.css.deleteIcon}'
 | 
				
			||||||
 | 
					      title='Delete this permission (and nested rules)'>
 | 
				
			||||||
 | 
					    <ui:attribute name='title'/>
 | 
				
			||||||
 | 
					  </g:Anchor>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <g:FlowPanel ui:field='ruleContainer'/>
 | 
				
			||||||
 | 
					  <div ui:field='addContainer' class='{style.addContainer}'>
 | 
				
			||||||
 | 
					    <div ui:field='addStage1'>
 | 
				
			||||||
 | 
					      <g:Anchor
 | 
				
			||||||
 | 
					          ui:field='beginAddRule'
 | 
				
			||||||
 | 
					          styleName='{style.addLink}'
 | 
				
			||||||
 | 
					          href='javascript:void'
 | 
				
			||||||
 | 
					          text='Add Group'>
 | 
				
			||||||
 | 
					        <ui:attribute name='text'/>
 | 
				
			||||||
 | 
					      </g:Anchor>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div ui:field='addStage2' style='display: none'>
 | 
				
			||||||
 | 
					      <ui:msg>Group Name: <my:GroupReferenceBox
 | 
				
			||||||
 | 
					                                            ui:field='groupToAdd'
 | 
				
			||||||
 | 
					                                            visibleLength='45'/></ui:msg>
 | 
				
			||||||
 | 
					      <g:Button
 | 
				
			||||||
 | 
					          ui:field='addRule'
 | 
				
			||||||
 | 
					          text='Add'>
 | 
				
			||||||
 | 
					        <ui:attribute name='text'/>
 | 
				
			||||||
 | 
					      </g:Button>
 | 
				
			||||||
 | 
					      <g:Anchor
 | 
				
			||||||
 | 
					          ui:field='hideAddGroup'
 | 
				
			||||||
 | 
					          href='javascript:void'
 | 
				
			||||||
 | 
					          styleName='{style.deleteIcon} {res.css.deleteIcon}'
 | 
				
			||||||
 | 
					          title='Cancel additional group'>
 | 
				
			||||||
 | 
					        <ui:attribute name='title'/>
 | 
				
			||||||
 | 
					      </g:Anchor>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
					    ui:field='deleted'
 | 
				
			||||||
 | 
					    class='{res.css.deleted} {res.css.deletedBorder}'
 | 
				
			||||||
 | 
					    style='display: none'>
 | 
				
			||||||
 | 
					  <ui:msg>Permission <g:ValueLabel styleName='{style.name}' ui:field='deletedName'/> was deleted</ui:msg>
 | 
				
			||||||
 | 
					  <g:Anchor
 | 
				
			||||||
 | 
					      ui:field='undoDelete'
 | 
				
			||||||
 | 
					      href='javascript:void'
 | 
				
			||||||
 | 
					      styleName='{style.deleteIcon} {res.css.undoIcon}'
 | 
				
			||||||
 | 
					      title='Undo deletion'>
 | 
				
			||||||
 | 
					    <ui:attribute name='title'/>
 | 
				
			||||||
 | 
					  </g:Anchor>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</g:HTMLPanel>
 | 
				
			||||||
 | 
					</ui:UiBinder>
 | 
				
			||||||
@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
 | 
					import com.google.gwt.text.shared.Renderer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PermissionNameRenderer implements Renderer<String> {
 | 
				
			||||||
 | 
					  static final PermissionNameRenderer INSTANCE = new PermissionNameRenderer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static Map<String, String> LC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public String render(String varName) {
 | 
				
			||||||
 | 
					    if (Permission.isLabel(varName)) {
 | 
				
			||||||
 | 
					      return Util.M.label(new Permission(varName).getLabel());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Map<String, String> m = Util.C.permissionNames();
 | 
				
			||||||
 | 
					    String desc = m.get(varName);
 | 
				
			||||||
 | 
					    if (desc == null) {
 | 
				
			||||||
 | 
					      if (LC == null) {
 | 
				
			||||||
 | 
					        LC = new HashMap<String, String>();
 | 
				
			||||||
 | 
					        for (Map.Entry<String, String> e : m.entrySet()) {
 | 
				
			||||||
 | 
					          LC.put(e.getKey().toLowerCase(), e.getValue());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      desc = LC.get(varName.toLowerCase());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return desc != null ? desc : varName;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void render(String object, Appendable appendable) throws IOException {
 | 
				
			||||||
 | 
					    appendable.append(render(object));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,200 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.PUSH;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.PUSH_TAG;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.client.Dispatcher;
 | 
				
			||||||
 | 
					import com.google.gerrit.client.ui.Hyperlink;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ApprovalType;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.GroupReference;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.PermissionRule;
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.GWT;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.DivElement;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.SpanElement;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.Style.Display;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.Editor;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.EditorDelegate;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.ValueAwareEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.ClickEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.text.shared.Renderer;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiBinder;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiField;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.DOM;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Anchor;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.CheckBox;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Composite;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.HTMLPanel;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.ValueListBox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PermissionRuleEditor extends Composite implements
 | 
				
			||||||
 | 
					    Editor<PermissionRule>, ValueAwareEditor<PermissionRule> {
 | 
				
			||||||
 | 
					  interface Binder extends UiBinder<HTMLPanel, PermissionRuleEditor> {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final Binder uiBinder = GWT.create(Binder.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField(provided = true)
 | 
				
			||||||
 | 
					  ValueListBox<PermissionRule.Action> action;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField(provided = true)
 | 
				
			||||||
 | 
					  ValueListBox<Integer> min;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField(provided = true)
 | 
				
			||||||
 | 
					  ValueListBox<Integer> max;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  CheckBox force;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Hyperlink normalGroupName;
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  SpanElement deletedGroupName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Anchor deleteRule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement normal;
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement deleted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  SpanElement rangeEditor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private boolean isDeleted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public PermissionRuleEditor(boolean readOnly, AccessSection section,
 | 
				
			||||||
 | 
					      Permission permission, ApprovalType labelRange) {
 | 
				
			||||||
 | 
					    action = new ValueListBox<PermissionRule.Action>(actionRenderer);
 | 
				
			||||||
 | 
					    min = new ValueListBox<Integer>(rangeRenderer);
 | 
				
			||||||
 | 
					    max = new ValueListBox<Integer>(rangeRenderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (labelRange != null){
 | 
				
			||||||
 | 
					      min.setValue((int) labelRange.getMin().getValue());
 | 
				
			||||||
 | 
					      max.setValue((int) labelRange.getMax().getValue());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      min.setAcceptableValues(labelRange.getValuesAsList());
 | 
				
			||||||
 | 
					      max.setAcceptableValues(labelRange.getValuesAsList());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      action.setValue(PermissionRule.Action.ALLOW);
 | 
				
			||||||
 | 
					      action.setAcceptableValues(Arrays.asList(PermissionRule.Action.values()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initWidget(uiBinder.createAndBindUi(this));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String name = permission.getName();
 | 
				
			||||||
 | 
					    boolean canForce = PUSH.equals(name) || PUSH_TAG.equals(name);
 | 
				
			||||||
 | 
					    if (canForce) {
 | 
				
			||||||
 | 
					      String ref = section.getRefPattern();
 | 
				
			||||||
 | 
					      canForce = !ref.startsWith("refs/for/") && !ref.startsWith("^refs/for/");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    force.setVisible(canForce);
 | 
				
			||||||
 | 
					    force.setEnabled(!readOnly);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (labelRange != null) {
 | 
				
			||||||
 | 
					      action.getElement().getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					      DOM.setElementPropertyBoolean(min.getElement(), "disabled", readOnly);
 | 
				
			||||||
 | 
					      DOM.setElementPropertyBoolean(max.getElement(), "disabled", readOnly);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      rangeEditor.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					      DOM.setElementPropertyBoolean(action.getElement(), "disabled", readOnly);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (readOnly) {
 | 
				
			||||||
 | 
					      deleteRule.removeFromParent();
 | 
				
			||||||
 | 
					      deleteRule = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  boolean isDeleted() {
 | 
				
			||||||
 | 
					    return isDeleted;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("deleteRule")
 | 
				
			||||||
 | 
					  void onDeleteRule(ClickEvent event) {
 | 
				
			||||||
 | 
					    isDeleted = true;
 | 
				
			||||||
 | 
					    normal.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    deleted.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("undoDelete")
 | 
				
			||||||
 | 
					  void onUndoDelete(ClickEvent event) {
 | 
				
			||||||
 | 
					    isDeleted = false;
 | 
				
			||||||
 | 
					    deleted.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    normal.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setValue(PermissionRule value) {
 | 
				
			||||||
 | 
					    GroupReference ref = value.getGroup();
 | 
				
			||||||
 | 
					    normalGroupName.setTargetHistoryToken(Dispatcher.toGroup(ref.getUUID()));
 | 
				
			||||||
 | 
					    normalGroupName.setText(ref.getName());
 | 
				
			||||||
 | 
					    deletedGroupName.setInnerText(ref.getName());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setDelegate(EditorDelegate<PermissionRule> delegate) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void flush() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void onPropertyChange(String... paths) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static class ActionRenderer implements
 | 
				
			||||||
 | 
					      Renderer<PermissionRule.Action> {
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String render(PermissionRule.Action object) {
 | 
				
			||||||
 | 
					      return object != null ? object.toString() : "";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void render(PermissionRule.Action object, Appendable appendable)
 | 
				
			||||||
 | 
					        throws IOException {
 | 
				
			||||||
 | 
					      appendable.append(render(object));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static class RangeRenderer implements Renderer<Integer> {
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String render(Integer object) {
 | 
				
			||||||
 | 
					      if (0 <= object) {
 | 
				
			||||||
 | 
					        return "+" + object;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return String.valueOf(object);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void render(Integer object, Appendable appendable)
 | 
				
			||||||
 | 
					        throws IOException {
 | 
				
			||||||
 | 
					      appendable.append(render(object));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final ActionRenderer actionRenderer = new ActionRenderer();
 | 
				
			||||||
 | 
					  private static final RangeRenderer rangeRenderer = new RangeRenderer();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,112 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<!--
 | 
				
			||||||
 | 
					Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<ui:UiBinder
 | 
				
			||||||
 | 
					  xmlns:ui='urn:ui:com.google.gwt.uibinder'
 | 
				
			||||||
 | 
					  xmlns:g='urn:import:com.google.gwt.user.client.ui'
 | 
				
			||||||
 | 
					  xmlns:e='urn:import:com.google.gwt.editor.ui.client'
 | 
				
			||||||
 | 
					  xmlns:my='urn:import:com.google.gerrit.client.admin'
 | 
				
			||||||
 | 
					  xmlns:q='urn:import:com.google.gerrit.client.ui'
 | 
				
			||||||
 | 
					  ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
 | 
				
			||||||
 | 
					  ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
 | 
				
			||||||
 | 
					  ui:generateLocales='default,en'
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
 | 
				
			||||||
 | 
					<ui:style>
 | 
				
			||||||
 | 
					  @eval selectionColor com.google.gerrit.client.Gerrit.getConfig().getSelectionColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .panel {
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    height: 1.5em;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .panel:hover {
 | 
				
			||||||
 | 
					    background-color: selectionColor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .normal {
 | 
				
			||||||
 | 
					    padding-left: 10px;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .deleted {
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .actionList, .minmax {
 | 
				
			||||||
 | 
					    font-size: 80%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .forcePush {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    right: 36px;
 | 
				
			||||||
 | 
					    width: 7em;
 | 
				
			||||||
 | 
					    font-size: 80%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .deleteIcon {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 2px;
 | 
				
			||||||
 | 
					    right: 11px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .groupName {
 | 
				
			||||||
 | 
					    display: inline;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					</ui:style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<g:HTMLPanel styleName='{style.panel}'>
 | 
				
			||||||
 | 
					<div ui:field='normal' class='{style.normal}'>
 | 
				
			||||||
 | 
					  <g:ValueListBox ui:field='action' styleName='{style.actionList}'/>
 | 
				
			||||||
 | 
					  <span ui:field='rangeEditor'>
 | 
				
			||||||
 | 
					    <g:ValueListBox ui:field='min' styleName='{style.minmax}'/>
 | 
				
			||||||
 | 
					    <g:ValueListBox ui:field='max' styleName='{style.minmax}'/>
 | 
				
			||||||
 | 
					  </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <q:Hyperlink ui:field='normalGroupName' styleName='{style.groupName}'/>
 | 
				
			||||||
 | 
					  <g:CheckBox
 | 
				
			||||||
 | 
					      ui:field='force'
 | 
				
			||||||
 | 
					      addStyleNames='{style.forcePush}'
 | 
				
			||||||
 | 
					      text='Force Push'>
 | 
				
			||||||
 | 
					    <ui:attribute name='text'/>
 | 
				
			||||||
 | 
					  </g:CheckBox>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <g:Anchor
 | 
				
			||||||
 | 
					      ui:field='deleteRule'
 | 
				
			||||||
 | 
					      href='javascript:void'
 | 
				
			||||||
 | 
					      styleName='{style.deleteIcon} {res.css.deleteIcon}'
 | 
				
			||||||
 | 
					      title='Delete this rule'>
 | 
				
			||||||
 | 
					    <ui:attribute name='title'/>
 | 
				
			||||||
 | 
					  </g:Anchor>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div
 | 
				
			||||||
 | 
					    ui:field='deleted'
 | 
				
			||||||
 | 
					    class='{res.css.deleted} {style.deleted}'
 | 
				
			||||||
 | 
					    style='display: none'>
 | 
				
			||||||
 | 
					  <ui:msg>Group <span ui:field='deletedGroupName'/> was deleted</ui:msg>
 | 
				
			||||||
 | 
					  <g:Anchor
 | 
				
			||||||
 | 
					      ui:field='undoDelete'
 | 
				
			||||||
 | 
					      href='javascript:void'
 | 
				
			||||||
 | 
					      styleName='{style.deleteIcon} {res.css.undoIcon}'
 | 
				
			||||||
 | 
					      title='Undo deletion'>
 | 
				
			||||||
 | 
					    <ui:attribute name='title'/>
 | 
				
			||||||
 | 
					  </g:Anchor>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</g:HTMLPanel>
 | 
				
			||||||
 | 
					</ui:UiBinder>
 | 
				
			||||||
@@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.client.Dispatcher;
 | 
				
			||||||
 | 
					import com.google.gerrit.client.ui.Hyperlink;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ProjectAccess;
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.GWT;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.DivElement;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.Style.Display;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.Editor;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.EditorDelegate;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.ValueAwareEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.adapters.EditorSource;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.adapters.ListEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.ClickEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiBinder;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiField;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Anchor;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Composite;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.FlowPanel;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.HTMLPanel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ProjectAccessEditor extends Composite implements
 | 
				
			||||||
 | 
					    Editor<ProjectAccess>, ValueAwareEditor<ProjectAccess> {
 | 
				
			||||||
 | 
					  interface Binder extends UiBinder<HTMLPanel, ProjectAccessEditor> {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final Binder uiBinder = GWT.create(Binder.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement inheritsFrom;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Hyperlink parentProject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  FlowPanel localContainer;
 | 
				
			||||||
 | 
					  ListEditor<AccessSection, AccessSectionEditor> local;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Anchor addSection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private ProjectAccess value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public ProjectAccessEditor() {
 | 
				
			||||||
 | 
					    initWidget(uiBinder.createAndBindUi(this));
 | 
				
			||||||
 | 
					    local = ListEditor.of(new Source(localContainer));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("addSection")
 | 
				
			||||||
 | 
					  void onAddSection(ClickEvent event) {
 | 
				
			||||||
 | 
					    int index = local.getList().size();
 | 
				
			||||||
 | 
					    local.getList().add(new AccessSection("refs/heads/*"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AccessSectionEditor editor = local.getEditors().get(index);
 | 
				
			||||||
 | 
					    editor.enableEditing();
 | 
				
			||||||
 | 
					    editor.editRefPattern();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setValue(ProjectAccess value) {
 | 
				
			||||||
 | 
					    this.value = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Project.NameKey parent = value.getInheritsFrom();
 | 
				
			||||||
 | 
					    if (parent != null) {
 | 
				
			||||||
 | 
					      inheritsFrom.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					      parentProject.setText(parent.get());
 | 
				
			||||||
 | 
					      parentProject.setTargetHistoryToken( //
 | 
				
			||||||
 | 
					          Dispatcher.toProjectAdmin(parent, ProjectScreen.ACCESS));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      inheritsFrom.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addSection.setVisible(value != null && !value.getOwnerOf().isEmpty());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void flush() {
 | 
				
			||||||
 | 
					    List<AccessSection> src = local.getList();
 | 
				
			||||||
 | 
					    List<AccessSection> keep = new ArrayList<AccessSection>(src.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int i = 0; i < src.size(); i++) {
 | 
				
			||||||
 | 
					      AccessSectionEditor e = (AccessSectionEditor) localContainer.getWidget(i);
 | 
				
			||||||
 | 
					      if (!e.isDeleted()) {
 | 
				
			||||||
 | 
					        keep.add(src.get(i));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    value.setLocal(keep);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void onPropertyChange(String... paths) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setDelegate(EditorDelegate<ProjectAccess> delegate) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private class Source extends EditorSource<AccessSectionEditor> {
 | 
				
			||||||
 | 
					    private final FlowPanel container;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Source(FlowPanel container) {
 | 
				
			||||||
 | 
					      this.container = container;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public AccessSectionEditor create(int index) {
 | 
				
			||||||
 | 
					      AccessSectionEditor subEditor = new AccessSectionEditor(value);
 | 
				
			||||||
 | 
					      container.insert(subEditor, index);
 | 
				
			||||||
 | 
					      return subEditor;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void dispose(AccessSectionEditor subEditor) {
 | 
				
			||||||
 | 
					      subEditor.removeFromParent();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setIndex(AccessSectionEditor subEditor, int index) {
 | 
				
			||||||
 | 
					      container.insert(subEditor, index);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<!--
 | 
				
			||||||
 | 
					Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<ui:UiBinder
 | 
				
			||||||
 | 
					  xmlns:ui='urn:ui:com.google.gwt.uibinder'
 | 
				
			||||||
 | 
					  xmlns:g='urn:import:com.google.gwt.user.client.ui'
 | 
				
			||||||
 | 
					  xmlns:q='urn:import:com.google.gerrit.client.ui'
 | 
				
			||||||
 | 
					  ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
 | 
				
			||||||
 | 
					  ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
 | 
				
			||||||
 | 
					  ui:generateLocales='default,en'
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					<ui:style>
 | 
				
			||||||
 | 
					  .inheritsFrom {
 | 
				
			||||||
 | 
					    margin-bottom: 0.5em;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .parentTitle {
 | 
				
			||||||
 | 
					    font-weight: bold;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .parentLink {
 | 
				
			||||||
 | 
					    display: inline;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .addContainer {
 | 
				
			||||||
 | 
					    margin-top: 5px;
 | 
				
			||||||
 | 
					    font-size: 80%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .addContainer:hover {
 | 
				
			||||||
 | 
					    background-color: selectionColor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					</ui:style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<g:HTMLPanel>
 | 
				
			||||||
 | 
					  <div ui:field='inheritsFrom' class='{style.inheritsFrom}'>
 | 
				
			||||||
 | 
					    <span class='{style.parentTitle}'><ui:msg>Rights Inherit From:</ui:msg></span>
 | 
				
			||||||
 | 
					    <q:Hyperlink ui:field='parentProject' styleName='{style.parentLink}'/>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <g:FlowPanel ui:field='localContainer'/>
 | 
				
			||||||
 | 
					  <div class='{style.addContainer}'>
 | 
				
			||||||
 | 
					    <g:Anchor
 | 
				
			||||||
 | 
					        ui:field='addSection'
 | 
				
			||||||
 | 
					        href='javascript:void'
 | 
				
			||||||
 | 
					        text='Add Reference'>
 | 
				
			||||||
 | 
					      <ui:attribute name='text'/>
 | 
				
			||||||
 | 
					    </g:Anchor>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</g:HTMLPanel>
 | 
				
			||||||
 | 
					</ui:UiBinder>
 | 
				
			||||||
@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.client.rpc.GerritCallback;
 | 
				
			||||||
 | 
					import com.google.gerrit.client.rpc.ScreenLoadCallback;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ProjectAccess;
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.GWT;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.DivElement;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.SimpleBeanEditorDriver;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.ClickEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiBinder;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiField;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.Window;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Button;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.HTMLPanel;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.UIObject;
 | 
				
			||||||
 | 
					import com.google.gwtexpui.globalkey.client.NpTextArea;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ProjectAccessScreen extends ProjectScreen {
 | 
				
			||||||
 | 
					  interface Binder extends UiBinder<HTMLPanel, ProjectAccessScreen> {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final Binder uiBinder = GWT.create(Binder.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  interface Driver extends SimpleBeanEditorDriver< //
 | 
				
			||||||
 | 
					      ProjectAccess, //
 | 
				
			||||||
 | 
					      ProjectAccessEditor> {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  ProjectAccessEditor accessEditor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement commitTools;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  NpTextArea commitMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Button commit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Driver driver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public ProjectAccessScreen(final Project.NameKey toShow) {
 | 
				
			||||||
 | 
					    super(toShow);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  protected void onInitUI() {
 | 
				
			||||||
 | 
					    super.onInitUI();
 | 
				
			||||||
 | 
					    add(uiBinder.createAndBindUi(this));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    driver = GWT.create(Driver.class);
 | 
				
			||||||
 | 
					    driver.initialize(accessEditor);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  protected void onLoad() {
 | 
				
			||||||
 | 
					    super.onLoad();
 | 
				
			||||||
 | 
					    Util.PROJECT_SVC.projectAccess(getProjectKey(),
 | 
				
			||||||
 | 
					        new ScreenLoadCallback<ProjectAccess>(this) {
 | 
				
			||||||
 | 
					          @Override
 | 
				
			||||||
 | 
					          public void preDisplay(ProjectAccess access) {
 | 
				
			||||||
 | 
					            edit(access);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void edit(ProjectAccess access) {
 | 
				
			||||||
 | 
					    driver.edit(access);
 | 
				
			||||||
 | 
					    UIObject.setVisible(commitTools, !access.getOwnerOf().isEmpty());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiHandler("commit")
 | 
				
			||||||
 | 
					  void onCommit(ClickEvent event) {
 | 
				
			||||||
 | 
					    ProjectAccess access = driver.flush();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (driver.hasErrors()) {
 | 
				
			||||||
 | 
					      Window.alert(Util.C.errorsMustBeFixed());
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String message = commitMessage.getText().trim();
 | 
				
			||||||
 | 
					    if ("".equals(message)) {
 | 
				
			||||||
 | 
					      message = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enable(false);
 | 
				
			||||||
 | 
					    Util.PROJECT_SVC.changeProjectAccess( //
 | 
				
			||||||
 | 
					        getProjectKey(), //
 | 
				
			||||||
 | 
					        access.getRevision(), //
 | 
				
			||||||
 | 
					        message, //
 | 
				
			||||||
 | 
					        access.getLocal(), //
 | 
				
			||||||
 | 
					        new GerritCallback<ProjectAccess>() {
 | 
				
			||||||
 | 
					          @Override
 | 
				
			||||||
 | 
					          public void onSuccess(ProjectAccess access) {
 | 
				
			||||||
 | 
					            commitMessage.setText("");
 | 
				
			||||||
 | 
					            edit(access);
 | 
				
			||||||
 | 
					            enable(true);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          @Override
 | 
				
			||||||
 | 
					          public void onFailure(Throwable caught) {
 | 
				
			||||||
 | 
					            enable(true);
 | 
				
			||||||
 | 
					            super.onFailure(caught);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void enable(boolean enabled) {
 | 
				
			||||||
 | 
					    commitMessage.setEnabled(enabled);
 | 
				
			||||||
 | 
					    commit.setEnabled(enabled);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<!--
 | 
				
			||||||
 | 
					Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<ui:UiBinder
 | 
				
			||||||
 | 
					  xmlns:ui='urn:ui:com.google.gwt.uibinder'
 | 
				
			||||||
 | 
					  xmlns:g='urn:import:com.google.gwt.user.client.ui'
 | 
				
			||||||
 | 
					  xmlns:my='urn:import:com.google.gerrit.client.admin'
 | 
				
			||||||
 | 
					  xmlns:expui='urn:import:com.google.gwtexpui.globalkey.client'
 | 
				
			||||||
 | 
					  ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
 | 
				
			||||||
 | 
					  ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
 | 
				
			||||||
 | 
					  ui:generateLocales='default,en'
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					<ui:style>
 | 
				
			||||||
 | 
					  @external .gwt-TextArea;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .commitMessage {
 | 
				
			||||||
 | 
					    margin-top: 2em;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .commitMessage .gwt-TextArea {
 | 
				
			||||||
 | 
					    margin: 5px 5px 5px 5px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					</ui:style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<g:HTMLPanel>
 | 
				
			||||||
 | 
					  <my:ProjectAccessEditor ui:field='accessEditor'/>
 | 
				
			||||||
 | 
					  <div ui:field='commitTools'>
 | 
				
			||||||
 | 
					    <div class='{style.commitMessage}'>
 | 
				
			||||||
 | 
					      <ui:msg>Commit Message (optional):</ui:msg><br/>
 | 
				
			||||||
 | 
					      <expui:NpTextArea
 | 
				
			||||||
 | 
					          ui:field='commitMessage'
 | 
				
			||||||
 | 
					          visibleLines='4'
 | 
				
			||||||
 | 
					          characterWidth='60'
 | 
				
			||||||
 | 
					          spellCheck='true'
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <g:Button
 | 
				
			||||||
 | 
					        ui:field='commit'
 | 
				
			||||||
 | 
					        text='Save Changes'>
 | 
				
			||||||
 | 
					      <ui:attribute name='text'/>
 | 
				
			||||||
 | 
					    </g:Button>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <div style='width: 35em; visibility: hidden;' />
 | 
				
			||||||
 | 
					</g:HTMLPanel>
 | 
				
			||||||
 | 
					</ui:UiBinder>
 | 
				
			||||||
@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.Document;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.KeyPressEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.KeyPressHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.text.shared.Parser;
 | 
				
			||||||
 | 
					import com.google.gwt.text.shared.Renderer;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.ValueBox;
 | 
				
			||||||
 | 
					import com.google.gwtexpui.globalkey.client.GlobalKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.text.ParseException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class RefPatternBox extends ValueBox<String> {
 | 
				
			||||||
 | 
					  private static final Renderer<String> RENDERER = new Renderer<String>() {
 | 
				
			||||||
 | 
					    public String render(String ref) {
 | 
				
			||||||
 | 
					      return ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void render(String ref, Appendable dst) throws IOException {
 | 
				
			||||||
 | 
					      dst.append(render(ref));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final Parser<String> PARSER = new Parser<String>() {
 | 
				
			||||||
 | 
					    public String parse(CharSequence text) throws ParseException {
 | 
				
			||||||
 | 
					      String ref = text.toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (ref.isEmpty()) {
 | 
				
			||||||
 | 
					        throw new ParseException(Util.C.refErrorEmpty(), 0);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (ref.charAt(0) == '/') {
 | 
				
			||||||
 | 
					        throw new ParseException(Util.C.refErrorBeginSlash(), 0);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      final boolean re = ref.charAt(0) == '^';
 | 
				
			||||||
 | 
					      if (re && !ref.startsWith("^refs/")) {
 | 
				
			||||||
 | 
					        ref = "^refs/heads/" + ref.substring(1);
 | 
				
			||||||
 | 
					      } else if (!ref.startsWith("refs/")) {
 | 
				
			||||||
 | 
					        ref = "refs/heads/" + ref;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (int i = 0; i < ref.length(); i++) {
 | 
				
			||||||
 | 
					        final char c = ref.charAt(i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (c == '/' && 0 < i && ref.charAt(i - 1) == '/') {
 | 
				
			||||||
 | 
					          throw new ParseException(Util.C.refErrorDoubleSlash(), i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (c == ' ') {
 | 
				
			||||||
 | 
					          throw new ParseException(Util.C.refErrorNoSpace(), i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (c < ' ') {
 | 
				
			||||||
 | 
					          throw new ParseException(Util.C.refErrorPrintable(), i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public RefPatternBox() {
 | 
				
			||||||
 | 
					    super(Document.get().createTextInputElement(), RENDERER, PARSER);
 | 
				
			||||||
 | 
					    addKeyPressHandler(GlobalKey.STOP_PROPAGATION);
 | 
				
			||||||
 | 
					    addKeyPressHandler(new KeyPressHandler() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void onKeyPress(KeyPressEvent event) {
 | 
				
			||||||
 | 
					        if (event.getCharCode() == ' ') {
 | 
				
			||||||
 | 
					          event.preventDefault();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -32,6 +32,8 @@ public class Util {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    PROJECT_SVC = GWT.create(ProjectAdminService.class);
 | 
					    PROJECT_SVC = GWT.create(ProjectAdminService.class);
 | 
				
			||||||
    JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
 | 
					    JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AdminResources.I.css().ensureInjected();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static String toLongString(final Project.SubmitType type) {
 | 
					  public static String toLongString(final Project.SubmitType type) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,206 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.client.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gwt.core.client.GWT;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.DivElement;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.NativeEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.dom.client.Style.Display;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.EditorError;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.HasEditorErrors;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.IsEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.client.LeafValueEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.editor.ui.client.adapters.ValueBoxEditor;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.ClickEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.ClickHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.DoubleClickEvent;
 | 
				
			||||||
 | 
					import com.google.gwt.event.dom.client.DoubleClickHandler;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiBinder;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiChild;
 | 
				
			||||||
 | 
					import com.google.gwt.uibinder.client.UiField;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Composite;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Focusable;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Image;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Label;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.SimplePanel;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.ValueBoxBase;
 | 
				
			||||||
 | 
					import com.google.gwt.user.client.ui.Widget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.text.ParseException;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ValueEditor<T> extends Composite implements HasEditorErrors<T>,
 | 
				
			||||||
 | 
					    IsEditor<ValueBoxEditor<T>>, LeafValueEditor<T>, Focusable {
 | 
				
			||||||
 | 
					  interface Binder extends UiBinder<Widget, ValueEditor<?>> {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static final Binder uiBinder = GWT.create(Binder.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  SimplePanel textPanel;
 | 
				
			||||||
 | 
					  private Label textLabel;
 | 
				
			||||||
 | 
					  private StartEditHandlers startHandlers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  Image editIcon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  SimplePanel editPanel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiField
 | 
				
			||||||
 | 
					  DivElement errorLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private ValueBoxBase<T> editChild;
 | 
				
			||||||
 | 
					  private ValueBoxEditor<T> editProxy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public ValueEditor() {
 | 
				
			||||||
 | 
					    startHandlers = new StartEditHandlers();
 | 
				
			||||||
 | 
					    initWidget(uiBinder.createAndBindUi(this));
 | 
				
			||||||
 | 
					    editPanel.setVisible(false);
 | 
				
			||||||
 | 
					    editIcon.addClickHandler(startHandlers);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void edit() {
 | 
				
			||||||
 | 
					    textPanel.removeFromParent();
 | 
				
			||||||
 | 
					    textPanel = null;
 | 
				
			||||||
 | 
					    textLabel = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    editIcon.removeFromParent();
 | 
				
			||||||
 | 
					    editIcon = null;
 | 
				
			||||||
 | 
					    startHandlers = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    editPanel.setVisible(true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public ValueBoxEditor<T> asEditor() {
 | 
				
			||||||
 | 
					    if (editProxy == null) {
 | 
				
			||||||
 | 
					      editProxy = new EditorProxy();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return editProxy;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public T getValue() {
 | 
				
			||||||
 | 
					    return asEditor().getValue();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setValue(T value) {
 | 
				
			||||||
 | 
					    asEditor().setValue(value);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setEditTitle(String title) {
 | 
				
			||||||
 | 
					    editIcon.setTitle(title);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiChild(limit = 1, tagname = "display")
 | 
				
			||||||
 | 
					  public void setDisplay(Label widget) {
 | 
				
			||||||
 | 
					    textLabel = widget;
 | 
				
			||||||
 | 
					    textPanel.add(textLabel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    textLabel.addClickHandler(startHandlers);
 | 
				
			||||||
 | 
					    textLabel.addDoubleClickHandler(startHandlers);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UiChild(limit = 1, tagname = "editor")
 | 
				
			||||||
 | 
					  public void setEditor(ValueBoxBase<T> widget) {
 | 
				
			||||||
 | 
					    editChild = widget;
 | 
				
			||||||
 | 
					    editPanel.add(editChild);
 | 
				
			||||||
 | 
					    editProxy = null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setEnabled(boolean enabled) {
 | 
				
			||||||
 | 
					    editIcon.setVisible(enabled);
 | 
				
			||||||
 | 
					    startHandlers.enabled = enabled;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void showErrors(List<EditorError> errors) {
 | 
				
			||||||
 | 
					    StringBuilder buf = new StringBuilder();
 | 
				
			||||||
 | 
					    for (EditorError error : errors) {
 | 
				
			||||||
 | 
					      if (error.getEditor().equals(editProxy)) {
 | 
				
			||||||
 | 
					        buf.append("\n");
 | 
				
			||||||
 | 
					        if (error.getUserData() instanceof ParseException) {
 | 
				
			||||||
 | 
					          buf.append(((ParseException) error.getUserData()).getMessage());
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          buf.append(error.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (0 < buf.length()) {
 | 
				
			||||||
 | 
					      errorLabel.setInnerText(buf.substring(1));
 | 
				
			||||||
 | 
					      errorLabel.getStyle().setDisplay(Display.BLOCK);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      errorLabel.setInnerText("");
 | 
				
			||||||
 | 
					      errorLabel.getStyle().setDisplay(Display.NONE);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setAccessKey(char key) {
 | 
				
			||||||
 | 
					    editChild.setAccessKey(key);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setFocus(boolean focused) {
 | 
				
			||||||
 | 
					    editChild.setFocus(focused);
 | 
				
			||||||
 | 
					    if (focused) {
 | 
				
			||||||
 | 
					      editChild.setCursorPos(editChild.getText().length());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public int getTabIndex() {
 | 
				
			||||||
 | 
					    return editChild.getTabIndex();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void setTabIndex(int index) {
 | 
				
			||||||
 | 
					    editChild.setTabIndex(index);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private class StartEditHandlers implements ClickHandler, DoubleClickHandler {
 | 
				
			||||||
 | 
					    boolean enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onClick(ClickEvent event) {
 | 
				
			||||||
 | 
					      if (enabled && event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
 | 
				
			||||||
 | 
					        edit();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onDoubleClick(DoubleClickEvent event) {
 | 
				
			||||||
 | 
					      if (enabled && event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
 | 
				
			||||||
 | 
					        edit();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private class EditorProxy extends ValueBoxEditor<T> {
 | 
				
			||||||
 | 
					    EditorProxy() {
 | 
				
			||||||
 | 
					      super(editChild);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setValue(T value) {
 | 
				
			||||||
 | 
					      super.setValue(value);
 | 
				
			||||||
 | 
					      if (textLabel == null) {
 | 
				
			||||||
 | 
					        setDisplay(new Label());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      textLabel.setText(editChild.getText());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<!--
 | 
				
			||||||
 | 
					Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<ui:UiBinder
 | 
				
			||||||
 | 
					  xmlns:ui='urn:ui:com.google.gwt.uibinder'
 | 
				
			||||||
 | 
					  xmlns:g='urn:import:com.google.gwt.user.client.ui'
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
 | 
				
			||||||
 | 
					<ui:style>
 | 
				
			||||||
 | 
					  .panel {
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .textPanel {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    padding-right: 21px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .editIcon {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    right: 5px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .editPanel {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .errorLabel {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					    color: red;
 | 
				
			||||||
 | 
					    white-space: pre;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					</ui:style>
 | 
				
			||||||
 | 
					<g:HTMLPanel stylePrimaryName='{style.panel}'>
 | 
				
			||||||
 | 
					  <g:Image
 | 
				
			||||||
 | 
					      ui:field='editIcon'
 | 
				
			||||||
 | 
					      resource='{res.editText}'
 | 
				
			||||||
 | 
					      stylePrimaryName='{style.editIcon}'
 | 
				
			||||||
 | 
					      title='Edit'>
 | 
				
			||||||
 | 
					    <ui:attribute name='title'/>
 | 
				
			||||||
 | 
					  </g:Image>
 | 
				
			||||||
 | 
					  <g:SimplePanel ui:field='textPanel' stylePrimaryName='{style.textPanel}'/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <g:SimplePanel ui:field='editPanel' stylePrimaryName='{style.editPanel}'/>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					      ui:field='errorLabel'
 | 
				
			||||||
 | 
					      class='{style.errorLabel}'/>
 | 
				
			||||||
 | 
					</g:HTMLPanel>
 | 
				
			||||||
 | 
					</ui:UiBinder>
 | 
				
			||||||
@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					/* Copyright (C) 2009 The Android Open Source Project
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@eval selectionColor com.google.gerrit.client.Gerrit.getConfig().getSelectionColor();
 | 
				
			||||||
 | 
					@eval textColor com.google.gerrit.client.Gerrit.getConfig().getTextColor();
 | 
				
			||||||
 | 
					@def deletedBackground #a9a9a9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@sprite .deleteIcon {
 | 
				
			||||||
 | 
					  gwt-image: 'deleteNormal';
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@sprite .deleteIcon:hover {
 | 
				
			||||||
 | 
					  gwt-image: 'deleteHover';
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@sprite .undoIcon {
 | 
				
			||||||
 | 
					  gwt-image: 'undoNormal';
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleted {
 | 
				
			||||||
 | 
					  background-color: deletedBackground;
 | 
				
			||||||
 | 
					  color: #ffffff;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					  padding-left: 50px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleted:hover {
 | 
				
			||||||
 | 
					  background-color: selectionColor;
 | 
				
			||||||
 | 
					  color: textColor;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deletedBorder {
 | 
				
			||||||
 | 
					  background: 1px solid deletedBackground;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteSectionHover {
 | 
				
			||||||
 | 
					  background-color: selectionColor !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 385 B  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.0 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 574 B  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 650 B  | 
@@ -16,26 +16,34 @@ package com.google.gerrit.client.ui;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.client.RpcStatus;
 | 
					import com.google.gerrit.client.RpcStatus;
 | 
				
			||||||
import com.google.gerrit.client.rpc.GerritCallback;
 | 
					import com.google.gerrit.client.rpc.GerritCallback;
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGroupName;
 | 
					import com.google.gerrit.common.data.GroupReference;
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.AccountGroup;
 | 
				
			||||||
import com.google.gwt.user.client.ui.SuggestOracle;
 | 
					import com.google.gwt.user.client.ui.SuggestOracle;
 | 
				
			||||||
import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
 | 
					import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Suggestion Oracle for AccountGroup entities. */
 | 
					/** Suggestion Oracle for AccountGroup entities. */
 | 
				
			||||||
public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
 | 
					public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
 | 
				
			||||||
 | 
					  private Map<String, AccountGroup.UUID> priorResults =
 | 
				
			||||||
 | 
					      new HashMap<String, AccountGroup.UUID>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public void onRequestSuggestions(final Request req, final Callback callback) {
 | 
					  public void onRequestSuggestions(final Request req, final Callback callback) {
 | 
				
			||||||
    RpcStatus.hide(new Runnable() {
 | 
					    RpcStatus.hide(new Runnable() {
 | 
				
			||||||
      public void run() {
 | 
					      public void run() {
 | 
				
			||||||
        SuggestUtil.SVC.suggestAccountGroup(req.getQuery(), req.getLimit(),
 | 
					        SuggestUtil.SVC.suggestAccountGroup(req.getQuery(), req.getLimit(),
 | 
				
			||||||
            new GerritCallback<List<AccountGroupName>>() {
 | 
					            new GerritCallback<List<GroupReference>>() {
 | 
				
			||||||
              public void onSuccess(final List<AccountGroupName> result) {
 | 
					              public void onSuccess(final List<GroupReference> result) {
 | 
				
			||||||
 | 
					                priorResults.clear();
 | 
				
			||||||
                final ArrayList<AccountGroupSuggestion> r =
 | 
					                final ArrayList<AccountGroupSuggestion> r =
 | 
				
			||||||
                    new ArrayList<AccountGroupSuggestion>(result.size());
 | 
					                    new ArrayList<AccountGroupSuggestion>(result.size());
 | 
				
			||||||
                for (final AccountGroupName p : result) {
 | 
					                for (final GroupReference p : result) {
 | 
				
			||||||
                  r.add(new AccountGroupSuggestion(p));
 | 
					                  r.add(new AccountGroupSuggestion(p));
 | 
				
			||||||
 | 
					                  priorResults.put(p.getName(), p.getUUID());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                callback.onSuggestionsReady(req, new Response(r));
 | 
					                callback.onSuggestionsReady(req, new Response(r));
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
@@ -46,9 +54,9 @@ public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private static class AccountGroupSuggestion implements
 | 
					  private static class AccountGroupSuggestion implements
 | 
				
			||||||
      SuggestOracle.Suggestion {
 | 
					      SuggestOracle.Suggestion {
 | 
				
			||||||
    private final AccountGroupName info;
 | 
					    private final GroupReference info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    AccountGroupSuggestion(final AccountGroupName k) {
 | 
					    AccountGroupSuggestion(final GroupReference k) {
 | 
				
			||||||
      info = k;
 | 
					      info = k;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -60,4 +68,9 @@ public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
 | 
				
			|||||||
      return info.getName();
 | 
					      return info.getName();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** @return the group UUID, or null if it cannot be found. */
 | 
				
			||||||
 | 
					  public AccountGroup.UUID getUUID(String name) {
 | 
				
			||||||
 | 
					    return priorResults.get(name);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,10 @@ import com.google.gwt.user.client.ui.impl.HyperlinkImpl;
 | 
				
			|||||||
public class Hyperlink extends com.google.gwt.user.client.ui.Hyperlink {
 | 
					public class Hyperlink extends com.google.gwt.user.client.ui.Hyperlink {
 | 
				
			||||||
  static final HyperlinkImpl impl = GWT.create(HyperlinkImpl.class);
 | 
					  static final HyperlinkImpl impl = GWT.create(HyperlinkImpl.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Initialize a default hyperlink with no target and no text. */
 | 
				
			||||||
 | 
					  public Hyperlink() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Creates a hyperlink with its text and target history token specified.
 | 
					   * Creates a hyperlink with its text and target history token specified.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@
 | 
				
			|||||||
package com.google.gerrit.httpd.rpc;
 | 
					package com.google.gerrit.httpd.rpc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.common.data.AccountInfo;
 | 
					import com.google.gerrit.common.data.AccountInfo;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.GroupReference;
 | 
				
			||||||
import com.google.gerrit.common.data.SuggestService;
 | 
					import com.google.gerrit.common.data.SuggestService;
 | 
				
			||||||
import com.google.gerrit.common.errors.NoSuchGroupException;
 | 
					import com.google.gerrit.common.errors.NoSuchGroupException;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Account;
 | 
					import com.google.gerrit.reviewdb.Account;
 | 
				
			||||||
@@ -29,6 +30,7 @@ import com.google.gerrit.server.account.AccountCache;
 | 
				
			|||||||
import com.google.gerrit.server.account.GroupControl;
 | 
					import com.google.gerrit.server.account.GroupControl;
 | 
				
			||||||
import com.google.gerrit.server.config.AuthConfig;
 | 
					import com.google.gerrit.server.config.AuthConfig;
 | 
				
			||||||
import com.google.gerrit.server.config.GerritServerConfig;
 | 
					import com.google.gerrit.server.config.GerritServerConfig;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.account.GroupCache;
 | 
				
			||||||
import com.google.gerrit.server.project.NoSuchProjectException;
 | 
					import com.google.gerrit.server.project.NoSuchProjectException;
 | 
				
			||||||
import com.google.gerrit.server.project.ProjectCache;
 | 
					import com.google.gerrit.server.project.ProjectCache;
 | 
				
			||||||
import com.google.gerrit.server.project.ProjectControl;
 | 
					import com.google.gerrit.server.project.ProjectControl;
 | 
				
			||||||
@@ -59,6 +61,7 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
 | 
				
			|||||||
  private final IdentifiedUser.GenericFactory userFactory;
 | 
					  private final IdentifiedUser.GenericFactory userFactory;
 | 
				
			||||||
  private final Provider<CurrentUser> currentUser;
 | 
					  private final Provider<CurrentUser> currentUser;
 | 
				
			||||||
  private final SuggestAccountsEnum suggestAccounts;
 | 
					  private final SuggestAccountsEnum suggestAccounts;
 | 
				
			||||||
 | 
					  private final GroupCache groupCache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  SuggestServiceImpl(final Provider<ReviewDb> schema,
 | 
					  SuggestServiceImpl(final Provider<ReviewDb> schema,
 | 
				
			||||||
@@ -68,7 +71,8 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
 | 
				
			|||||||
      final GroupControl.Factory groupControlFactory,
 | 
					      final GroupControl.Factory groupControlFactory,
 | 
				
			||||||
      final IdentifiedUser.GenericFactory userFactory,
 | 
					      final IdentifiedUser.GenericFactory userFactory,
 | 
				
			||||||
      final Provider<CurrentUser> currentUser,
 | 
					      final Provider<CurrentUser> currentUser,
 | 
				
			||||||
      @GerritServerConfig final Config cfg) {
 | 
					      @GerritServerConfig final Config cfg,
 | 
				
			||||||
 | 
					      final GroupCache groupCache) {
 | 
				
			||||||
    super(schema, currentUser);
 | 
					    super(schema, currentUser);
 | 
				
			||||||
    this.authConfig = authConfig;
 | 
					    this.authConfig = authConfig;
 | 
				
			||||||
    this.projectControlFactory = projectControlFactory;
 | 
					    this.projectControlFactory = projectControlFactory;
 | 
				
			||||||
@@ -79,6 +83,7 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
 | 
				
			|||||||
    this.currentUser = currentUser;
 | 
					    this.currentUser = currentUser;
 | 
				
			||||||
    this.suggestAccounts =
 | 
					    this.suggestAccounts =
 | 
				
			||||||
        cfg.getEnum("suggest", null, "accounts", SuggestAccountsEnum.ALL);
 | 
					        cfg.getEnum("suggest", null, "accounts", SuggestAccountsEnum.ALL);
 | 
				
			||||||
 | 
					    this.groupCache = groupCache;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void suggestProjectNameKey(final String query, final int limit,
 | 
					  public void suggestProjectNameKey(final String query, final int limit,
 | 
				
			||||||
@@ -182,27 +187,30 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void suggestAccountGroup(final String query, final int limit,
 | 
					  public void suggestAccountGroup(final String query, final int limit,
 | 
				
			||||||
      final AsyncCallback<List<AccountGroupName>> callback) {
 | 
					      final AsyncCallback<List<GroupReference>> callback) {
 | 
				
			||||||
    run(callback, new Action<List<AccountGroupName>>() {
 | 
					    run(callback, new Action<List<GroupReference>>() {
 | 
				
			||||||
      public List<AccountGroupName> run(final ReviewDb db) throws OrmException {
 | 
					      public List<GroupReference> run(final ReviewDb db) throws OrmException {
 | 
				
			||||||
        final String a = query;
 | 
					        final String a = query;
 | 
				
			||||||
        final String b = a + MAX_SUFFIX;
 | 
					        final String b = a + MAX_SUFFIX;
 | 
				
			||||||
        final int max = 10;
 | 
					        final int max = 10;
 | 
				
			||||||
        final int n = limit <= 0 ? max : Math.min(limit, max);
 | 
					        final int n = limit <= 0 ? max : Math.min(limit, max);
 | 
				
			||||||
        Set<AccountGroup.UUID> memberOf = currentUser.get().getEffectiveGroups();
 | 
					        Set<AccountGroup.UUID> memberOf = currentUser.get().getEffectiveGroups();
 | 
				
			||||||
        List<AccountGroupName> names = new ArrayList<AccountGroupName>(n);
 | 
					        List<GroupReference> r = new ArrayList<GroupReference>(n);
 | 
				
			||||||
        for (AccountGroupName group : db.accountGroupNames()
 | 
					        for (AccountGroupName group : db.accountGroupNames()
 | 
				
			||||||
              .suggestByName(a, b, n)) {
 | 
					              .suggestByName(a, b, n)) {
 | 
				
			||||||
          try {
 | 
					          try {
 | 
				
			||||||
            if (memberOf.contains(group.getId())
 | 
					            if (memberOf.contains(group.getId())
 | 
				
			||||||
                || groupControlFactory.controlFor(group.getId()).isVisible()) {
 | 
					                || groupControlFactory.controlFor(group.getId()).isVisible()) {
 | 
				
			||||||
              names.add(group);
 | 
					              AccountGroup g = groupCache.get(group.getId());
 | 
				
			||||||
 | 
					              if (g != null && g.getGroupUUID() != null) {
 | 
				
			||||||
 | 
					                r.add(GroupReference.forGroup(g));
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          } catch (NoSuchGroupException e) {
 | 
					          } catch (NoSuchGroupException e) {
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return names;
 | 
					        return r;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -119,8 +119,14 @@ class GroupAdminServiceImpl extends BaseServiceImplementation implements
 | 
				
			|||||||
    createGroupFactory.create(newName).to(callback);
 | 
					    createGroupFactory.create(newName).to(callback);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void groupDetail(final AccountGroup.Id groupId,
 | 
					  public void groupDetail(AccountGroup.Id groupId, AccountGroup.UUID groupUUID,
 | 
				
			||||||
      final AsyncCallback<GroupDetail> callback) {
 | 
					      AsyncCallback<GroupDetail> callback) {
 | 
				
			||||||
 | 
					    if (groupId == null && groupUUID != null) {
 | 
				
			||||||
 | 
					      AccountGroup g = groupCache.get(groupUUID);
 | 
				
			||||||
 | 
					      if (g != null) {
 | 
				
			||||||
 | 
					        groupId = g.getId();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    groupDetailFactory.create(groupId).to(callback);
 | 
					    groupDetailFactory.create(groupId).to(callback);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,206 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.httpd.rpc.project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.GroupReference;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.PermissionRule;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ProjectAccess;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.errors.InvalidNameException;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.errors.NoSuchGroupException;
 | 
				
			||||||
 | 
					import com.google.gerrit.httpd.rpc.Handler;
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.AccountGroup;
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.account.GroupCache;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.git.MetaDataUpdate;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.git.ProjectConfig;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.project.NoSuchProjectException;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.project.ProjectCache;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.project.ProjectControl;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.project.RefControl;
 | 
				
			||||||
 | 
					import com.google.gwtorm.client.OrmConcurrencyException;
 | 
				
			||||||
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
 | 
					import com.google.inject.assistedinject.Assisted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.errors.RepositoryNotFoundException;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.lib.ObjectId;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.lib.Repository;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.LinkedHashMap;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChangeProjectAccess extends Handler<ProjectAccess> {
 | 
				
			||||||
 | 
					  interface Factory {
 | 
				
			||||||
 | 
					    ChangeProjectAccess create(@Assisted Project.NameKey projectName,
 | 
				
			||||||
 | 
					        @Assisted ObjectId base, @Assisted List<AccessSection> sectionList,
 | 
				
			||||||
 | 
					        @Nullable @Assisted String message);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private final ProjectAccessFactory.Factory projectAccessFactory;
 | 
				
			||||||
 | 
					  private final ProjectControl.Factory projectControlFactory;
 | 
				
			||||||
 | 
					  private final ProjectCache projectCache;
 | 
				
			||||||
 | 
					  private final GroupCache groupCache;
 | 
				
			||||||
 | 
					  private final MetaDataUpdate.User metaDataUpdateFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private final Project.NameKey projectName;
 | 
				
			||||||
 | 
					  private final ObjectId base;
 | 
				
			||||||
 | 
					  private List<AccessSection> sectionList;
 | 
				
			||||||
 | 
					  private String message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Inject
 | 
				
			||||||
 | 
					  ChangeProjectAccess(final ProjectAccessFactory.Factory projectAccessFactory,
 | 
				
			||||||
 | 
					      final ProjectControl.Factory projectControlFactory,
 | 
				
			||||||
 | 
					      final ProjectCache projectCache, final GroupCache groupCache,
 | 
				
			||||||
 | 
					      final MetaDataUpdate.User metaDataUpdateFactory,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Assisted final Project.NameKey projectName,
 | 
				
			||||||
 | 
					      @Assisted final ObjectId base, @Assisted List<AccessSection> sectionList,
 | 
				
			||||||
 | 
					      @Nullable @Assisted String message) {
 | 
				
			||||||
 | 
					    this.projectAccessFactory = projectAccessFactory;
 | 
				
			||||||
 | 
					    this.projectControlFactory = projectControlFactory;
 | 
				
			||||||
 | 
					    this.projectCache = projectCache;
 | 
				
			||||||
 | 
					    this.groupCache = groupCache;
 | 
				
			||||||
 | 
					    this.metaDataUpdateFactory = metaDataUpdateFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.projectName = projectName;
 | 
				
			||||||
 | 
					    this.base = base;
 | 
				
			||||||
 | 
					    this.sectionList = sectionList;
 | 
				
			||||||
 | 
					    this.message = message;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public ProjectAccess call() throws NoSuchProjectException, IOException,
 | 
				
			||||||
 | 
					      ConfigInvalidException, InvalidNameException, NoSuchGroupException,
 | 
				
			||||||
 | 
					      OrmConcurrencyException {
 | 
				
			||||||
 | 
					    final ProjectControl projectControl =
 | 
				
			||||||
 | 
					        projectControlFactory.controlFor(projectName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final MetaDataUpdate md;
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      md = metaDataUpdateFactory.create(projectName);
 | 
				
			||||||
 | 
					    } catch (RepositoryNotFoundException notFound) {
 | 
				
			||||||
 | 
					      throw new NoSuchProjectException(projectName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      ProjectConfig config = ProjectConfig.read(md, base);
 | 
				
			||||||
 | 
					      Set<String> toDelete = scanSectionNames(config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (AccessSection section : mergeSections(sectionList)) {
 | 
				
			||||||
 | 
					        final String name = section.getRefPattern();
 | 
				
			||||||
 | 
					        if (!projectControl.controlForRef(name).isOwner()) {
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (name.startsWith(AccessSection.REGEX_PREFIX)) {
 | 
				
			||||||
 | 
					          if (!Repository.isValidRefName(RefControl.shortestExample(name))) {
 | 
				
			||||||
 | 
					            throw new InvalidNameException();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if (name.equals(AccessSection.ALL)) {
 | 
				
			||||||
 | 
					          // This is a special case we have to allow, it fails below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if (name.endsWith("/*")) {
 | 
				
			||||||
 | 
					          String prefix = name.substring(0, name.length() - 2);
 | 
				
			||||||
 | 
					          if (!Repository.isValidRefName(prefix)) {
 | 
				
			||||||
 | 
					            throw new InvalidNameException();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if (!Repository.isValidRefName(name)) {
 | 
				
			||||||
 | 
					          throw new InvalidNameException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (Permission permission : section.getPermissions()) {
 | 
				
			||||||
 | 
					          for (PermissionRule rule : permission.getRules()) {
 | 
				
			||||||
 | 
					            lookupGroup(rule);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        config.replace(section);
 | 
				
			||||||
 | 
					        toDelete.remove(section.getRefPattern());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (String name : toDelete) {
 | 
				
			||||||
 | 
					        if (projectControl.controlForRef(name).isOwner()) {
 | 
				
			||||||
 | 
					          config.remove(config.getAccessSection(name));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (message != null && !message.isEmpty()) {
 | 
				
			||||||
 | 
					        if (!message.endsWith("\n")) {
 | 
				
			||||||
 | 
					          message += "\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        md.setMessage(message);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        md.setMessage("Modify access rules\n");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (config.commit(md)) {
 | 
				
			||||||
 | 
					        projectCache.evict(config.getProject());
 | 
				
			||||||
 | 
					        return projectAccessFactory.create(projectName).call();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        throw new OrmConcurrencyException("Cannot update " + projectName);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      md.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static List<AccessSection> mergeSections(List<AccessSection> src) {
 | 
				
			||||||
 | 
					    Map<String, AccessSection> map = new LinkedHashMap<String, AccessSection>();
 | 
				
			||||||
 | 
					    for (AccessSection section : src) {
 | 
				
			||||||
 | 
					      if (section.getPermissions().isEmpty()) {
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      AccessSection prior = map.get(section.getRefPattern());
 | 
				
			||||||
 | 
					      if (prior != null) {
 | 
				
			||||||
 | 
					        prior.mergeFrom(section);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        map.put(section.getRefPattern(), section);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return new ArrayList<AccessSection>(map.values());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static Set<String> scanSectionNames(ProjectConfig config) {
 | 
				
			||||||
 | 
					    Set<String> names = new HashSet<String>();
 | 
				
			||||||
 | 
					    for (AccessSection section : config.getAccessSections()) {
 | 
				
			||||||
 | 
					      names.add(section.getRefPattern());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return names;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void lookupGroup(PermissionRule rule) throws NoSuchGroupException {
 | 
				
			||||||
 | 
					    GroupReference ref = rule.getGroup();
 | 
				
			||||||
 | 
					    if (ref.getUUID() == null) {
 | 
				
			||||||
 | 
					      AccountGroup.NameKey name = new AccountGroup.NameKey(ref.getName());
 | 
				
			||||||
 | 
					      AccountGroup group = groupCache.get(name);
 | 
				
			||||||
 | 
					      if (group == null) {
 | 
				
			||||||
 | 
					        throw new NoSuchGroupException(name);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      ref.setUUID(group.getGroupUUID());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 The Android Open Source Project
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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.google.gerrit.httpd.rpc.project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ProjectAccess;
 | 
				
			||||||
 | 
					import com.google.gerrit.httpd.rpc.Handler;
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.account.GroupCache;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.config.WildProjectName;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.git.MetaDataUpdate;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.git.ProjectConfig;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.project.NoSuchProjectException;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.project.ProjectCache;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.project.ProjectControl;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.project.RefControl;
 | 
				
			||||||
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
 | 
					import com.google.inject.assistedinject.Assisted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProjectAccessFactory extends Handler<ProjectAccess> {
 | 
				
			||||||
 | 
					  interface Factory {
 | 
				
			||||||
 | 
					    ProjectAccessFactory create(@Assisted Project.NameKey name);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private final GroupCache groupCache;
 | 
				
			||||||
 | 
					  private final ProjectCache projectCache;
 | 
				
			||||||
 | 
					  private final ProjectControl.Factory projectControlFactory;
 | 
				
			||||||
 | 
					  private final MetaDataUpdate.Server metaDataUpdateFactory;
 | 
				
			||||||
 | 
					  private final Project.NameKey wildProject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private final Project.NameKey projectName;
 | 
				
			||||||
 | 
					  private ProjectControl pc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Inject
 | 
				
			||||||
 | 
					  ProjectAccessFactory(final GroupCache groupCache,
 | 
				
			||||||
 | 
					      final ProjectCache projectCache,
 | 
				
			||||||
 | 
					      final ProjectControl.Factory projectControlFactory,
 | 
				
			||||||
 | 
					      final MetaDataUpdate.Server metaDataUpdateFactory,
 | 
				
			||||||
 | 
					      @WildProjectName final Project.NameKey wildProject,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Assisted final Project.NameKey name) {
 | 
				
			||||||
 | 
					    this.groupCache = groupCache;
 | 
				
			||||||
 | 
					    this.projectCache = projectCache;
 | 
				
			||||||
 | 
					    this.projectControlFactory = projectControlFactory;
 | 
				
			||||||
 | 
					    this.metaDataUpdateFactory = metaDataUpdateFactory;
 | 
				
			||||||
 | 
					    this.wildProject = wildProject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.projectName = name;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public ProjectAccess call() throws NoSuchProjectException, IOException,
 | 
				
			||||||
 | 
					      ConfigInvalidException {
 | 
				
			||||||
 | 
					    pc = open();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Load the current configuration from the repository, ensuring its the most
 | 
				
			||||||
 | 
					    // recent version available. If it differs from what was in the project
 | 
				
			||||||
 | 
					    // state, force a cache flush now.
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    ProjectConfig config;
 | 
				
			||||||
 | 
					    MetaDataUpdate md = metaDataUpdateFactory.create(projectName);
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      config = ProjectConfig.read(md);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (config.updateGroupNames(groupCache)) {
 | 
				
			||||||
 | 
					        md.setMessage("Update group names\n");
 | 
				
			||||||
 | 
					        if (config.commit(md)) {
 | 
				
			||||||
 | 
					          projectCache.evict(config.getProject());
 | 
				
			||||||
 | 
					          pc = open();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else if (config.getRevision() != null
 | 
				
			||||||
 | 
					          && !config.getRevision().equals(
 | 
				
			||||||
 | 
					              pc.getProjectState().getConfig().getRevision())) {
 | 
				
			||||||
 | 
					        projectCache.evict(config.getProject());
 | 
				
			||||||
 | 
					        pc = open();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      md.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    List<AccessSection> local = new ArrayList<AccessSection>();
 | 
				
			||||||
 | 
					    Set<String> ownerOf = new HashSet<String>();
 | 
				
			||||||
 | 
					    for (AccessSection section : config.getAccessSections()) {
 | 
				
			||||||
 | 
					      RefControl rc = pc.controlForRef(section.getRefPattern());
 | 
				
			||||||
 | 
					      if (rc.isOwner()) {
 | 
				
			||||||
 | 
					        local.add(section);
 | 
				
			||||||
 | 
					        ownerOf.add(section.getRefPattern());
 | 
				
			||||||
 | 
					      } else if (rc.isVisible()) {
 | 
				
			||||||
 | 
					        local.add(section);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final ProjectAccess detail = new ProjectAccess();
 | 
				
			||||||
 | 
					    detail.setRevision(config.getRevision().name());
 | 
				
			||||||
 | 
					    detail.setLocal(local);
 | 
				
			||||||
 | 
					    detail.setOwnerOf(ownerOf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (projectName.equals(wildProject)) {
 | 
				
			||||||
 | 
					      detail.setInheritsFrom(null);
 | 
				
			||||||
 | 
					    } else if (config.getProject().getParent() != null) {
 | 
				
			||||||
 | 
					      detail.setInheritsFrom(config.getProject().getParent());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      detail.setInheritsFrom(wildProject);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return detail;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private ProjectControl open() throws NoSuchProjectException {
 | 
				
			||||||
 | 
					    return projectControlFactory.validateFor( //
 | 
				
			||||||
 | 
					        projectName, //
 | 
				
			||||||
 | 
					        ProjectControl.OWNER | ProjectControl.VISIBLE);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -14,7 +14,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.httpd.rpc.project;
 | 
					package com.google.gerrit.httpd.rpc.project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
import com.google.gerrit.common.data.ListBranchesResult;
 | 
					import com.google.gerrit.common.data.ListBranchesResult;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ProjectAccess;
 | 
				
			||||||
import com.google.gerrit.common.data.ProjectAdminService;
 | 
					import com.google.gerrit.common.data.ProjectAdminService;
 | 
				
			||||||
import com.google.gerrit.common.data.ProjectDetail;
 | 
					import com.google.gerrit.common.data.ProjectDetail;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Branch;
 | 
					import com.google.gerrit.reviewdb.Branch;
 | 
				
			||||||
@@ -22,29 +24,37 @@ import com.google.gerrit.reviewdb.Project;
 | 
				
			|||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
 | 
					import com.google.gwt.user.client.rpc.AsyncCallback;
 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.eclipse.jgit.lib.ObjectId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProjectAdminServiceImpl implements ProjectAdminService {
 | 
					class ProjectAdminServiceImpl implements ProjectAdminService {
 | 
				
			||||||
  private final AddBranch.Factory addBranchFactory;
 | 
					  private final AddBranch.Factory addBranchFactory;
 | 
				
			||||||
 | 
					  private final ChangeProjectAccess.Factory changeProjectAccessFactory;
 | 
				
			||||||
  private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
 | 
					  private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
 | 
				
			||||||
  private final DeleteBranches.Factory deleteBranchesFactory;
 | 
					  private final DeleteBranches.Factory deleteBranchesFactory;
 | 
				
			||||||
  private final ListBranches.Factory listBranchesFactory;
 | 
					  private final ListBranches.Factory listBranchesFactory;
 | 
				
			||||||
  private final VisibleProjects.Factory visibleProjectsFactory;
 | 
					  private final VisibleProjects.Factory visibleProjectsFactory;
 | 
				
			||||||
 | 
					  private final ProjectAccessFactory.Factory projectAccessFactory;
 | 
				
			||||||
  private final ProjectDetailFactory.Factory projectDetailFactory;
 | 
					  private final ProjectDetailFactory.Factory projectDetailFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory,
 | 
					  ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory,
 | 
				
			||||||
 | 
					      final ChangeProjectAccess.Factory changeProjectAccessFactory,
 | 
				
			||||||
      final ChangeProjectSettings.Factory changeProjectSettingsFactory,
 | 
					      final ChangeProjectSettings.Factory changeProjectSettingsFactory,
 | 
				
			||||||
      final DeleteBranches.Factory deleteBranchesFactory,
 | 
					      final DeleteBranches.Factory deleteBranchesFactory,
 | 
				
			||||||
      final ListBranches.Factory listBranchesFactory,
 | 
					      final ListBranches.Factory listBranchesFactory,
 | 
				
			||||||
      final VisibleProjects.Factory visibleProjectsFactory,
 | 
					      final VisibleProjects.Factory visibleProjectsFactory,
 | 
				
			||||||
 | 
					      final ProjectAccessFactory.Factory projectAccessFactory,
 | 
				
			||||||
      final ProjectDetailFactory.Factory projectDetailFactory) {
 | 
					      final ProjectDetailFactory.Factory projectDetailFactory) {
 | 
				
			||||||
    this.addBranchFactory = addBranchFactory;
 | 
					    this.addBranchFactory = addBranchFactory;
 | 
				
			||||||
 | 
					    this.changeProjectAccessFactory = changeProjectAccessFactory;
 | 
				
			||||||
    this.changeProjectSettingsFactory = changeProjectSettingsFactory;
 | 
					    this.changeProjectSettingsFactory = changeProjectSettingsFactory;
 | 
				
			||||||
    this.deleteBranchesFactory = deleteBranchesFactory;
 | 
					    this.deleteBranchesFactory = deleteBranchesFactory;
 | 
				
			||||||
    this.listBranchesFactory = listBranchesFactory;
 | 
					    this.listBranchesFactory = listBranchesFactory;
 | 
				
			||||||
    this.visibleProjectsFactory = visibleProjectsFactory;
 | 
					    this.visibleProjectsFactory = visibleProjectsFactory;
 | 
				
			||||||
 | 
					    this.projectAccessFactory = projectAccessFactory;
 | 
				
			||||||
    this.projectDetailFactory = projectDetailFactory;
 | 
					    this.projectDetailFactory = projectDetailFactory;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,12 +69,26 @@ class ProjectAdminServiceImpl implements ProjectAdminService {
 | 
				
			|||||||
    projectDetailFactory.create(projectName).to(callback);
 | 
					    projectDetailFactory.create(projectName).to(callback);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void projectAccess(final Project.NameKey projectName,
 | 
				
			||||||
 | 
					      final AsyncCallback<ProjectAccess> callback) {
 | 
				
			||||||
 | 
					    projectAccessFactory.create(projectName).to(callback);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public void changeProjectSettings(final Project update,
 | 
					  public void changeProjectSettings(final Project update,
 | 
				
			||||||
      final AsyncCallback<ProjectDetail> callback) {
 | 
					      final AsyncCallback<ProjectDetail> callback) {
 | 
				
			||||||
    changeProjectSettingsFactory.create(update).to(callback);
 | 
					    changeProjectSettingsFactory.create(update).to(callback);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void changeProjectAccess(Project.NameKey projectName,
 | 
				
			||||||
 | 
					      String baseRevision, String msg, List<AccessSection> sections,
 | 
				
			||||||
 | 
					      AsyncCallback<ProjectAccess> cb) {
 | 
				
			||||||
 | 
					    ObjectId base = ObjectId.fromString(baseRevision);
 | 
				
			||||||
 | 
					    changeProjectAccessFactory.create(projectName, base, sections, msg).to(cb);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public void listBranches(final Project.NameKey projectName,
 | 
					  public void listBranches(final Project.NameKey projectName,
 | 
				
			||||||
      final AsyncCallback<ListBranchesResult> callback) {
 | 
					      final AsyncCallback<ListBranchesResult> callback) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,10 +29,12 @@ public class ProjectModule extends RpcServletModule {
 | 
				
			|||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      protected void configure() {
 | 
					      protected void configure() {
 | 
				
			||||||
        factory(AddBranch.Factory.class);
 | 
					        factory(AddBranch.Factory.class);
 | 
				
			||||||
 | 
					        factory(ChangeProjectAccess.Factory.class);
 | 
				
			||||||
        factory(ChangeProjectSettings.Factory.class);
 | 
					        factory(ChangeProjectSettings.Factory.class);
 | 
				
			||||||
        factory(DeleteBranches.Factory.class);
 | 
					        factory(DeleteBranches.Factory.class);
 | 
				
			||||||
        factory(ListBranches.Factory.class);
 | 
					        factory(ListBranches.Factory.class);
 | 
				
			||||||
        factory(VisibleProjects.Factory.class);
 | 
					        factory(VisibleProjects.Factory.class);
 | 
				
			||||||
 | 
					        factory(ProjectAccessFactory.Factory.class);
 | 
				
			||||||
        factory(ProjectDetailFactory.Factory.class);
 | 
					        factory(ProjectDetailFactory.Factory.class);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,6 +70,13 @@ public final class AccountGroup {
 | 
				
			|||||||
    protected void set(String newValue) {
 | 
					    protected void set(String newValue) {
 | 
				
			||||||
      uuid = newValue;
 | 
					      uuid = newValue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Parse an AccountGroup.UUID out of a string representation. */
 | 
				
			||||||
 | 
					    public static UUID parse(final String str) {
 | 
				
			||||||
 | 
					      final UUID r = new UUID();
 | 
				
			||||||
 | 
					      r.fromString(str);
 | 
				
			||||||
 | 
					      return r;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Distinguished name, within organization directory server. */
 | 
					  /** Distinguished name, within organization directory server. */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -108,12 +108,24 @@ public class ProjectConfig extends VersionedMetaData {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Collection<AccessSection> getAccessSections() {
 | 
					  public Collection<AccessSection> getAccessSections() {
 | 
				
			||||||
    return accessSections.values();
 | 
					    return sort(accessSections.values());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void remove(AccessSection section) {
 | 
					  public void remove(AccessSection section) {
 | 
				
			||||||
 | 
					    if (section != null) {
 | 
				
			||||||
      accessSections.remove(section.getRefPattern());
 | 
					      accessSections.remove(section.getRefPattern());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void replace(AccessSection section) {
 | 
				
			||||||
 | 
					    for (Permission permission : section.getPermissions()) {
 | 
				
			||||||
 | 
					      for (PermissionRule rule : permission.getRules()) {
 | 
				
			||||||
 | 
					        rule.setGroup(resolve(rule.getGroup()));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    accessSections.put(section.getRefPattern(), section);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public GroupReference resolve(AccountGroup group) {
 | 
					  public GroupReference resolve(AccountGroup group) {
 | 
				
			||||||
    return resolve(GroupReference.forGroup(group));
 | 
					    return resolve(GroupReference.forGroup(group));
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user