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
 | 
			
		||||
  public int compareTo(AccessSection o) {
 | 
			
		||||
    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.gwtjsonrpc.client.RemoteJsonService;
 | 
			
		||||
import com.google.gwtjsonrpc.client.RpcImpl;
 | 
			
		||||
import com.google.gwtjsonrpc.client.VoidResult;
 | 
			
		||||
import com.google.gwtjsonrpc.client.RpcImpl.Version;
 | 
			
		||||
import com.google.gwtjsonrpc.client.VoidResult;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
@@ -36,7 +36,8 @@ public interface GroupAdminService extends RemoteJsonService {
 | 
			
		||||
  void createGroup(String newName, AsyncCallback<AccountGroup.Id> callback);
 | 
			
		||||
 | 
			
		||||
  @SignInRequired
 | 
			
		||||
  void groupDetail(AccountGroup.Id groupId, AsyncCallback<GroupDetail> callback);
 | 
			
		||||
  void groupDetail(AccountGroup.Id groupId, AccountGroup.UUID uuid,
 | 
			
		||||
      AsyncCallback<GroupDetail> callback);
 | 
			
		||||
 | 
			
		||||
  @SignInRequired
 | 
			
		||||
  void changeGroupDescription(AccountGroup.Id groupId, String description,
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ public class GroupReference implements Comparable<GroupReference> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void setUUID(AccountGroup.UUID newUUID) {
 | 
			
		||||
    uuid = newUUID.get();
 | 
			
		||||
    uuid = newUUID != null ? newUUID.get() : null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public String getName() {
 | 
			
		||||
 
 | 
			
		||||
@@ -94,14 +94,14 @@ public class Permission implements Comparable<Permission> {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean getExclusiveGroup() {
 | 
			
		||||
  public Boolean getExclusiveGroup() {
 | 
			
		||||
    // Only permit exclusive group behavior on non OWNER permissions,
 | 
			
		||||
    // otherwise an owner might lose access to a delegated subspace.
 | 
			
		||||
    //
 | 
			
		||||
    return exclusiveGroup && !OWNER.equals(getName());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void setExclusiveGroup(boolean newExclusiveGroup) {
 | 
			
		||||
  public void setExclusiveGroup(Boolean 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) {
 | 
			
		||||
    if (group.getUUID() != null) {
 | 
			
		||||
      return group.getUUID().equals(rule.getGroup().getUUID());
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,10 @@
 | 
			
		||||
package com.google.gerrit.common.data;
 | 
			
		||||
 | 
			
		||||
public class PermissionRule implements Comparable<PermissionRule> {
 | 
			
		||||
  public static enum Action {
 | 
			
		||||
    ALLOW, DENY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected boolean deny;
 | 
			
		||||
  protected boolean force;
 | 
			
		||||
  protected int min;
 | 
			
		||||
@@ -28,6 +32,17 @@ public class PermissionRule implements Comparable<PermissionRule> {
 | 
			
		||||
    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() {
 | 
			
		||||
    return deny;
 | 
			
		||||
  }
 | 
			
		||||
@@ -36,11 +51,11 @@ public class PermissionRule implements Comparable<PermissionRule> {
 | 
			
		||||
    deny = newDeny;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean getForce() {
 | 
			
		||||
  public Boolean getForce() {
 | 
			
		||||
    return force;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void setForce(boolean newForce) {
 | 
			
		||||
  public void setForce(Boolean newForce) {
 | 
			
		||||
    force = newForce;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -78,6 +93,12 @@ public class PermissionRule implements Comparable<PermissionRule> {
 | 
			
		||||
    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
 | 
			
		||||
  public int compareTo(PermissionRule 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,
 | 
			
		||||
      AsyncCallback<ProjectDetail> callback);
 | 
			
		||||
 | 
			
		||||
  void projectAccess(Project.NameKey projectName,
 | 
			
		||||
      AsyncCallback<ProjectAccess> callback);
 | 
			
		||||
 | 
			
		||||
  @SignInRequired
 | 
			
		||||
  void changeProjectSettings(Project update,
 | 
			
		||||
      AsyncCallback<ProjectDetail> callback);
 | 
			
		||||
 | 
			
		||||
  @SignInRequired
 | 
			
		||||
  void changeProjectAccess(Project.NameKey projectName, String baseRevision,
 | 
			
		||||
      String message, List<AccessSection> sections,
 | 
			
		||||
      AsyncCallback<ProjectAccess> callback);
 | 
			
		||||
 | 
			
		||||
  void listBranches(Project.NameKey projectName,
 | 
			
		||||
      AsyncCallback<ListBranchesResult> callback);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,6 @@
 | 
			
		||||
 | 
			
		||||
package com.google.gerrit.common.data;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.reviewdb.AccountGroupName;
 | 
			
		||||
import com.google.gerrit.reviewdb.Project;
 | 
			
		||||
import com.google.gwt.user.client.rpc.AsyncCallback;
 | 
			
		||||
import com.google.gwtjsonrpc.client.RemoteJsonService;
 | 
			
		||||
@@ -32,5 +31,5 @@ public interface SuggestService extends RemoteJsonService {
 | 
			
		||||
      AsyncCallback<List<AccountInfo>> callback);
 | 
			
		||||
 | 
			
		||||
  void suggestAccountGroup(String query, int limit,
 | 
			
		||||
      AsyncCallback<List<AccountGroupName>> callback);
 | 
			
		||||
      AsyncCallback<List<GroupReference>> callback);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@
 | 
			
		||||
 limitations under the License.
 | 
			
		||||
-->
 | 
			
		||||
<module rename-to="gerrit">
 | 
			
		||||
  <inherits name='com.google.gwt.editor.Editor'/>
 | 
			
		||||
  <inherits name='com.google.gwt.user.User'/>
 | 
			
		||||
  <inherits name='com.google.gwt.resources.Resources'/>
 | 
			
		||||
  <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.admin.AccountGroupScreen;
 | 
			
		||||
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.ProjectInfoScreen;
 | 
			
		||||
import com.google.gerrit.client.admin.ProjectListScreen;
 | 
			
		||||
@@ -86,6 +87,10 @@ public class Dispatcher {
 | 
			
		||||
    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) {
 | 
			
		||||
    return "admin,project," + n.toString() + "," + tab;
 | 
			
		||||
  }
 | 
			
		||||
@@ -410,6 +415,10 @@ public class Dispatcher {
 | 
			
		||||
      private Screen select() {
 | 
			
		||||
        String p;
 | 
			
		||||
 | 
			
		||||
        p = "admin,group,uuid-";
 | 
			
		||||
        if (token.startsWith(p))
 | 
			
		||||
          return new AccountGroupScreen(AccountGroup.UUID.parse(skip(p, token)));
 | 
			
		||||
 | 
			
		||||
        p = "admin,group,";
 | 
			
		||||
        if (token.startsWith(p))
 | 
			
		||||
          return new AccountGroupScreen(AccountGroup.Id.parse(skip(p, token)));
 | 
			
		||||
@@ -431,7 +440,7 @@ public class Dispatcher {
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (ProjectScreen.ACCESS.equals(p)) {
 | 
			
		||||
            return new NotFoundScreen();
 | 
			
		||||
            return new ProjectAccessScreen(k);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          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;
 | 
			
		||||
 | 
			
		||||
public class AccountGroupScreen extends AccountScreen {
 | 
			
		||||
  private final AccountGroup.Id groupId;
 | 
			
		||||
  private AccountGroup.Id groupId;
 | 
			
		||||
  private AccountGroup.UUID groupUUID;
 | 
			
		||||
 | 
			
		||||
  private AccountInfoCache accounts = AccountInfoCache.empty();
 | 
			
		||||
  private GroupInfoCache groups = GroupInfoCache.empty();
 | 
			
		||||
  private MemberTable members;
 | 
			
		||||
@@ -106,13 +108,21 @@ public class AccountGroupScreen extends AccountScreen {
 | 
			
		||||
    groupId = toShow;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public AccountGroupScreen(final AccountGroup.UUID toShow) {
 | 
			
		||||
    groupUUID = toShow;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  protected void onLoad() {
 | 
			
		||||
    super.onLoad();
 | 
			
		||||
    Util.GROUP_SVC.groupDetail(groupId, new ScreenLoadCallback<GroupDetail>(
 | 
			
		||||
        this) {
 | 
			
		||||
    Util.GROUP_SVC.groupDetail(groupId, groupUUID,
 | 
			
		||||
        new ScreenLoadCallback<GroupDetail>(this) {
 | 
			
		||||
          @Override
 | 
			
		||||
          protected void preDisplay(final GroupDetail result) {
 | 
			
		||||
            groupId = result.group.getId();
 | 
			
		||||
            groupUUID = result.group.getGroupUUID();
 | 
			
		||||
            display(result);
 | 
			
		||||
 | 
			
		||||
            enableForm(result.canModify);
 | 
			
		||||
            saveName.setVisible(result.canModify);
 | 
			
		||||
            saveOwner.setVisible(result.canModify);
 | 
			
		||||
@@ -121,7 +131,6 @@ public class AccountGroupScreen extends AccountScreen {
 | 
			
		||||
            delMember.setVisible(result.canModify);
 | 
			
		||||
            saveType.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 java.util.Map;
 | 
			
		||||
 | 
			
		||||
public interface AdminConstants extends Constants {
 | 
			
		||||
  String defaultAccountName();
 | 
			
		||||
  String defaultAccountGroupName();
 | 
			
		||||
@@ -101,4 +103,14 @@ public interface AdminConstants extends Constants {
 | 
			
		||||
  String noGroupSelected();
 | 
			
		||||
  String errorNoMatchingGroups();
 | 
			
		||||
  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)
 | 
			
		||||
errorNoMatchingGroups = No Matching Groups
 | 
			
		||||
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 {
 | 
			
		||||
  String group(String name);
 | 
			
		||||
  String label(String name);
 | 
			
		||||
  String project(String name);
 | 
			
		||||
  String deletedGroup(int id);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
group = Group {0}
 | 
			
		||||
label = Label {0}
 | 
			
		||||
project = Project {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);
 | 
			
		||||
    JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
 | 
			
		||||
 | 
			
		||||
    AdminResources.I.css().ensureInjected();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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.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.gwtexpui.safehtml.client.HighlightSuggestOracle;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/** Suggestion Oracle for AccountGroup entities. */
 | 
			
		||||
public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
 | 
			
		||||
  private Map<String, AccountGroup.UUID> priorResults =
 | 
			
		||||
      new HashMap<String, AccountGroup.UUID>();
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void onRequestSuggestions(final Request req, final Callback callback) {
 | 
			
		||||
    RpcStatus.hide(new Runnable() {
 | 
			
		||||
      public void run() {
 | 
			
		||||
        SuggestUtil.SVC.suggestAccountGroup(req.getQuery(), req.getLimit(),
 | 
			
		||||
            new GerritCallback<List<AccountGroupName>>() {
 | 
			
		||||
              public void onSuccess(final List<AccountGroupName> result) {
 | 
			
		||||
            new GerritCallback<List<GroupReference>>() {
 | 
			
		||||
              public void onSuccess(final List<GroupReference> result) {
 | 
			
		||||
                priorResults.clear();
 | 
			
		||||
                final ArrayList<AccountGroupSuggestion> r =
 | 
			
		||||
                    new ArrayList<AccountGroupSuggestion>(result.size());
 | 
			
		||||
                for (final AccountGroupName p : result) {
 | 
			
		||||
                for (final GroupReference p : result) {
 | 
			
		||||
                  r.add(new AccountGroupSuggestion(p));
 | 
			
		||||
                  priorResults.put(p.getName(), p.getUUID());
 | 
			
		||||
                }
 | 
			
		||||
                callback.onSuggestionsReady(req, new Response(r));
 | 
			
		||||
              }
 | 
			
		||||
@@ -46,9 +54,9 @@ public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
 | 
			
		||||
 | 
			
		||||
  private static class AccountGroupSuggestion implements
 | 
			
		||||
      SuggestOracle.Suggestion {
 | 
			
		||||
    private final AccountGroupName info;
 | 
			
		||||
    private final GroupReference info;
 | 
			
		||||
 | 
			
		||||
    AccountGroupSuggestion(final AccountGroupName k) {
 | 
			
		||||
    AccountGroupSuggestion(final GroupReference k) {
 | 
			
		||||
      info = k;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -60,4 +68,9 @@ public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
 | 
			
		||||
      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 {
 | 
			
		||||
  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.
 | 
			
		||||
   *
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
package com.google.gerrit.httpd.rpc;
 | 
			
		||||
 | 
			
		||||
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.errors.NoSuchGroupException;
 | 
			
		||||
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.config.AuthConfig;
 | 
			
		||||
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.ProjectCache;
 | 
			
		||||
import com.google.gerrit.server.project.ProjectControl;
 | 
			
		||||
@@ -59,6 +61,7 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
 | 
			
		||||
  private final IdentifiedUser.GenericFactory userFactory;
 | 
			
		||||
  private final Provider<CurrentUser> currentUser;
 | 
			
		||||
  private final SuggestAccountsEnum suggestAccounts;
 | 
			
		||||
  private final GroupCache groupCache;
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  SuggestServiceImpl(final Provider<ReviewDb> schema,
 | 
			
		||||
@@ -68,7 +71,8 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
 | 
			
		||||
      final GroupControl.Factory groupControlFactory,
 | 
			
		||||
      final IdentifiedUser.GenericFactory userFactory,
 | 
			
		||||
      final Provider<CurrentUser> currentUser,
 | 
			
		||||
      @GerritServerConfig final Config cfg) {
 | 
			
		||||
      @GerritServerConfig final Config cfg,
 | 
			
		||||
      final GroupCache groupCache) {
 | 
			
		||||
    super(schema, currentUser);
 | 
			
		||||
    this.authConfig = authConfig;
 | 
			
		||||
    this.projectControlFactory = projectControlFactory;
 | 
			
		||||
@@ -79,6 +83,7 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
 | 
			
		||||
    this.currentUser = currentUser;
 | 
			
		||||
    this.suggestAccounts =
 | 
			
		||||
        cfg.getEnum("suggest", null, "accounts", SuggestAccountsEnum.ALL);
 | 
			
		||||
    this.groupCache = groupCache;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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,
 | 
			
		||||
      final AsyncCallback<List<AccountGroupName>> callback) {
 | 
			
		||||
    run(callback, new Action<List<AccountGroupName>>() {
 | 
			
		||||
      public List<AccountGroupName> run(final ReviewDb db) throws OrmException {
 | 
			
		||||
      final AsyncCallback<List<GroupReference>> callback) {
 | 
			
		||||
    run(callback, new Action<List<GroupReference>>() {
 | 
			
		||||
      public List<GroupReference> run(final ReviewDb db) throws OrmException {
 | 
			
		||||
        final String a = query;
 | 
			
		||||
        final String b = a + MAX_SUFFIX;
 | 
			
		||||
        final int max = 10;
 | 
			
		||||
        final int n = limit <= 0 ? max : Math.min(limit, max);
 | 
			
		||||
        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()
 | 
			
		||||
              .suggestByName(a, b, n)) {
 | 
			
		||||
          try {
 | 
			
		||||
            if (memberOf.contains(group.getId())
 | 
			
		||||
                || 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) {
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        return names;
 | 
			
		||||
        return r;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -119,8 +119,14 @@ class GroupAdminServiceImpl extends BaseServiceImplementation implements
 | 
			
		||||
    createGroupFactory.create(newName).to(callback);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void groupDetail(final AccountGroup.Id groupId,
 | 
			
		||||
      final AsyncCallback<GroupDetail> callback) {
 | 
			
		||||
  public void groupDetail(AccountGroup.Id groupId, AccountGroup.UUID groupUUID,
 | 
			
		||||
      AsyncCallback<GroupDetail> callback) {
 | 
			
		||||
    if (groupId == null && groupUUID != null) {
 | 
			
		||||
      AccountGroup g = groupCache.get(groupUUID);
 | 
			
		||||
      if (g != null) {
 | 
			
		||||
        groupId = g.getId();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.common.data.AccessSection;
 | 
			
		||||
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.ProjectDetail;
 | 
			
		||||
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.inject.Inject;
 | 
			
		||||
 | 
			
		||||
import org.eclipse.jgit.lib.ObjectId;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
class ProjectAdminServiceImpl implements ProjectAdminService {
 | 
			
		||||
  private final AddBranch.Factory addBranchFactory;
 | 
			
		||||
  private final ChangeProjectAccess.Factory changeProjectAccessFactory;
 | 
			
		||||
  private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
 | 
			
		||||
  private final DeleteBranches.Factory deleteBranchesFactory;
 | 
			
		||||
  private final ListBranches.Factory listBranchesFactory;
 | 
			
		||||
  private final VisibleProjects.Factory visibleProjectsFactory;
 | 
			
		||||
  private final ProjectAccessFactory.Factory projectAccessFactory;
 | 
			
		||||
  private final ProjectDetailFactory.Factory projectDetailFactory;
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory,
 | 
			
		||||
      final ChangeProjectAccess.Factory changeProjectAccessFactory,
 | 
			
		||||
      final ChangeProjectSettings.Factory changeProjectSettingsFactory,
 | 
			
		||||
      final DeleteBranches.Factory deleteBranchesFactory,
 | 
			
		||||
      final ListBranches.Factory listBranchesFactory,
 | 
			
		||||
      final VisibleProjects.Factory visibleProjectsFactory,
 | 
			
		||||
      final ProjectAccessFactory.Factory projectAccessFactory,
 | 
			
		||||
      final ProjectDetailFactory.Factory projectDetailFactory) {
 | 
			
		||||
    this.addBranchFactory = addBranchFactory;
 | 
			
		||||
    this.changeProjectAccessFactory = changeProjectAccessFactory;
 | 
			
		||||
    this.changeProjectSettingsFactory = changeProjectSettingsFactory;
 | 
			
		||||
    this.deleteBranchesFactory = deleteBranchesFactory;
 | 
			
		||||
    this.listBranchesFactory = listBranchesFactory;
 | 
			
		||||
    this.visibleProjectsFactory = visibleProjectsFactory;
 | 
			
		||||
    this.projectAccessFactory = projectAccessFactory;
 | 
			
		||||
    this.projectDetailFactory = projectDetailFactory;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -59,12 +69,26 @@ class ProjectAdminServiceImpl implements ProjectAdminService {
 | 
			
		||||
    projectDetailFactory.create(projectName).to(callback);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void projectAccess(final Project.NameKey projectName,
 | 
			
		||||
      final AsyncCallback<ProjectAccess> callback) {
 | 
			
		||||
    projectAccessFactory.create(projectName).to(callback);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void changeProjectSettings(final Project update,
 | 
			
		||||
      final AsyncCallback<ProjectDetail> 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
 | 
			
		||||
  public void listBranches(final Project.NameKey projectName,
 | 
			
		||||
      final AsyncCallback<ListBranchesResult> callback) {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,10 +29,12 @@ public class ProjectModule extends RpcServletModule {
 | 
			
		||||
      @Override
 | 
			
		||||
      protected void configure() {
 | 
			
		||||
        factory(AddBranch.Factory.class);
 | 
			
		||||
        factory(ChangeProjectAccess.Factory.class);
 | 
			
		||||
        factory(ChangeProjectSettings.Factory.class);
 | 
			
		||||
        factory(DeleteBranches.Factory.class);
 | 
			
		||||
        factory(ListBranches.Factory.class);
 | 
			
		||||
        factory(VisibleProjects.Factory.class);
 | 
			
		||||
        factory(ProjectAccessFactory.Factory.class);
 | 
			
		||||
        factory(ProjectDetailFactory.Factory.class);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -70,6 +70,13 @@ public final class AccountGroup {
 | 
			
		||||
    protected void set(String 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. */
 | 
			
		||||
 
 | 
			
		||||
@@ -108,12 +108,24 @@ public class ProjectConfig extends VersionedMetaData {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public Collection<AccessSection> getAccessSections() {
 | 
			
		||||
    return accessSections.values();
 | 
			
		||||
    return sort(accessSections.values());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void remove(AccessSection section) {
 | 
			
		||||
    if (section != null) {
 | 
			
		||||
      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) {
 | 
			
		||||
    return resolve(GroupReference.forGroup(group));
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user