Merge "Hyperlink to groups in access editor"
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java
index dd6c22b..ea0c8d2 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.common.data;
 
+import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
 
 import java.util.List;
@@ -31,6 +32,7 @@
   protected boolean canChangeParent;
   protected LabelTypes labelTypes;
   protected Map<String, String> capabilities;
+  protected Map<AccountGroup.UUID, GroupInfo> groupInfo;
 
   public ProjectAccess() {
   }
@@ -131,4 +133,12 @@
   public void setCapabilities(Map<String, String> capabilities) {
     this.capabilities = capabilities;
   }
+
+  public Map<AccountGroup.UUID, GroupInfo> getGroupInfo() {
+    return groupInfo;
+  }
+
+  public void setGroupInfo(Map<AccountGroup.UUID, GroupInfo> m) {
+    groupInfo = m;
+  }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
index 5222751..f8119b4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.client.ui.SuggestUtil;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.common.data.GroupInfo;
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
@@ -26,6 +27,7 @@
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.common.data.ProjectAccess;
 import com.google.gerrit.common.data.RefConfigSection;
+import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.Scheduler;
@@ -55,6 +57,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 public class PermissionEditor extends Composite implements Editor<Permission>,
     ValueAwareEditor<Permission> {
@@ -101,6 +104,7 @@
   DivElement deleted;
 
   private final Project.NameKey projectName;
+  private final Map<AccountGroup.UUID, GroupInfo> groupInfo;
   private final boolean readOnly;
   private final AccessSection section;
   private final LabelTypes labelTypes;
@@ -115,6 +119,7 @@
     this.readOnly = readOnly;
     this.section = section;
     this.projectName = projectAccess.getProjectName();
+    this.groupInfo = projectAccess.getGroupInfo();
     this.labelTypes = labelTypes;
 
     PermissionNameRenderer nameRenderer =
@@ -314,7 +319,7 @@
     @Override
     public PermissionRuleEditor create(int index) {
       PermissionRuleEditor subEditor =
-          new PermissionRuleEditor(readOnly, section, value, validRange);
+          new PermissionRuleEditor(readOnly, groupInfo, section, value, validRange);
       ruleContainer.insert(subEditor, index);
       return subEditor;
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
index 6c9a9b7..07862f4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
@@ -19,9 +19,10 @@
 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.client.Gerrit;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.common.data.GroupInfo;
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRange;
@@ -35,6 +36,8 @@
 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.event.dom.client.ClickHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.text.shared.Renderer;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
@@ -49,6 +52,7 @@
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
 public class PermissionRuleEditor extends Composite implements
     Editor<PermissionRule>, ValueAwareEditor<PermissionRule> {
@@ -70,7 +74,7 @@
   CheckBox force;
 
   @UiField
-  Hyperlink groupNameLink;
+  Anchor groupNameLink;
   @UiField
   SpanElement groupNameSpan;
   @UiField
@@ -87,10 +91,16 @@
   @UiField
   SpanElement rangeEditor;
 
+  private Map<AccountGroup.UUID, GroupInfo> groupInfo;
   private boolean isDeleted;
+  private HandlerRegistration clickHandler;
 
-  public PermissionRuleEditor(boolean readOnly, AccessSection section,
-      Permission permission, PermissionRange.WithDefaults validRange) {
+  public PermissionRuleEditor(boolean readOnly,
+      Map<AccountGroup.UUID, GroupInfo> groupInfo,
+      AccessSection section,
+      Permission permission,
+      PermissionRange.WithDefaults validRange) {
+    this.groupInfo = groupInfo;
     action = new ValueListBox<PermissionRule.Action>(actionRenderer);
 
     if (validRange != null && 10 < validRange.getRangeSize()) {
@@ -181,12 +191,37 @@
 
   @Override
   public void setValue(PermissionRule value) {
+    if (clickHandler != null) {
+      clickHandler.removeHandler();
+      clickHandler = null;
+    }
+
     GroupReference ref = value.getGroup();
+    GroupInfo info = groupInfo != null && ref.getUUID() != null
+        ? groupInfo.get(ref.getUUID())
+        : null;
 
     boolean link;
     if (ref.getUUID() != null && AccountGroup.isInternalGroup(ref.getUUID())) {
+      final String token = Dispatcher.toGroup(ref.getUUID());
       groupNameLink.setText(ref.getName());
-      groupNameLink.setTargetHistoryToken(Dispatcher.toGroup(ref.getUUID()));
+      groupNameLink.setHref("#" + token);
+      groupNameLink.setTitle(info != null ? info.getDescription() : null);
+      groupNameLink.setTarget(null);
+      clickHandler = groupNameLink.addClickHandler(new ClickHandler() {
+        @Override
+        public void onClick(ClickEvent event) {
+          event.preventDefault();
+          event.stopPropagation();
+          Gerrit.display(token);
+        }
+      });
+      link = true;
+    } else if (info != null && info.getUrl() != null) {
+      groupNameLink.setText(ref.getName());
+      groupNameLink.setHref(info.getUrl());
+      groupNameLink.setTitle(info.getDescription());
+      groupNameLink.setTarget("_blank");
       link = true;
     } else {
       groupNameSpan.setInnerText(ref.getName());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml
index 26fc229..4d322c0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml
@@ -78,7 +78,7 @@
     <g:Widget ui:field='max' styleName='{style.minmax}'/>
   </span>
 
-  <q:Hyperlink ui:field='groupNameLink' styleName='{style.groupName}'/>
+  <g:Anchor ui:field='groupNameLink' styleName='{style.groupName}'/>
   <span ui:field='groupNameSpan' styleName='{style.groupName}'/>
   <g:CheckBox ui:field='force' addStyleNames='{style.forcePush}'/>
   <g:Anchor
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
index 6e7150d..9eef638 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
@@ -14,7 +14,11 @@
 
 package com.google.gerrit.httpd.rpc.project;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Maps;
 import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupInfo;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.common.data.ProjectAccess;
@@ -112,8 +116,8 @@
     final RefControl metaConfigControl = pc.controlForRef(GitRepositoryManager.REF_CONFIG);
     List<AccessSection> local = new ArrayList<AccessSection>();
     Set<String> ownerOf = new HashSet<String>();
-    Map<AccountGroup.UUID, Boolean> visibleGroups =
-        new HashMap<AccountGroup.UUID, Boolean>();
+    Map<AccountGroup.UUID, GroupInfo> groupInfo = new HashMap<>();
+    Map<AccountGroup.UUID, Boolean> visibleGroups = new HashMap<>();
 
     for (AccessSection section : config.getAccessSections()) {
       String name = section.getName();
@@ -206,10 +210,36 @@
     detail.setCanChangeParent(pc.getCurrentUser().getCapabilities()
         .canAdministrateServer());
     detail.setConfigVisible(pc.isOwner() || metaConfigControl.isVisible());
+    detail.setGroupInfo(buildGroupInfo(local));
     detail.setLabelTypes(pc.getLabelTypes());
     return detail;
   }
 
+  private Map<AccountGroup.UUID, GroupInfo> buildGroupInfo(List<AccessSection> local) {
+    Map<AccountGroup.UUID, GroupInfo> infos = new HashMap<>();
+    for (AccessSection section : local) {
+      for (Permission permission : section.getPermissions()) {
+        for (PermissionRule rule : permission.getRules()) {
+          if (rule.getGroup() != null) {
+            AccountGroup.UUID uuid = rule.getGroup().getUUID();
+            if (uuid != null && !infos.containsKey(uuid)) {
+              GroupDescription.Basic group = groupBackend.get(uuid);
+              infos.put(uuid, group != null ? new GroupInfo(group) : null);
+            }
+          }
+        }
+      }
+    }
+    return Maps.filterEntries(
+      infos,
+      new Predicate<Map.Entry<AccountGroup.UUID, GroupInfo>>() {
+        @Override
+        public boolean apply(Map.Entry<AccountGroup.UUID, GroupInfo> in) {
+          return in.getValue() != null;
+        }
+      });
+  }
+
   private ProjectControl open() throws NoSuchProjectException {
     return projectControlFactory.validateFor( //
         projectName, //