Merge "Add WebLink extension point for file history"
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 7041dcc..a83f46c 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
@@ -32,6 +32,7 @@
protected LabelTypes labelTypes;
protected Map<String, String> capabilities;
protected Map<AccountGroup.UUID, GroupInfo> groupInfo;
+ protected List<WebLinkInfoCommon> fileHistoryLinks;
public ProjectAccess() {
}
@@ -132,4 +133,12 @@
public void setGroupInfo(Map<AccountGroup.UUID, GroupInfo> m) {
groupInfo = m;
}
+
+ public void setFileHistoryLinks(List<WebLinkInfoCommon> links) {
+ fileHistoryLinks = links;
+ }
+
+ public List<WebLinkInfoCommon> getFileHistoryLinks() {
+ return fileHistoryLinks;
+ }
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/WebLinkInfoCommon.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/WebLinkInfoCommon.java
new file mode 100644
index 0000000..dd0a70a
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/WebLinkInfoCommon.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2015 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;
+
+public class WebLinkInfoCommon {
+ public WebLinkInfoCommon() {}
+
+ public String name;
+ public String imageUrl;
+ public String url;
+ public String target;
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/FileHistoryWebLink.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/FileHistoryWebLink.java
new file mode 100644
index 0000000..f9f9e58
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/FileHistoryWebLink.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2015 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.extensions.webui;
+
+import com.google.gerrit.extensions.common.WebLinkInfo;
+
+public interface FileHistoryWebLink extends WebLink {
+ /**
+ * {@link com.google.gerrit.extensions.common.WebLinkInfo}
+ * describing a link from a file to an external service displaying
+ * a log for that file.
+ *
+ * <p>In order for the web link to be visible
+ * {@link com.google.gerrit.extensions.common.WebLinkInfo#url}
+ * and {@link com.google.gerrit.extensions.common.WebLinkInfo#name}
+ * must be set.<p>
+ *
+ * @param projectName Name of the project
+ * @param revision Name of the revision (e.g. branch or commit ID)
+ * @param fileName Name of the file
+ * @return WebLinkInfo that links to a log for the file in external
+ * service, null if there should be no link.
+ */
+ WebLinkInfo getFileHistoryWebLink(String projectName, String revision, String fileName);
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
index 13b3a54..177faff0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
@@ -21,6 +21,7 @@
import com.google.gerrit.client.ui.ParentProjectBox;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ProjectAccess;
+import com.google.gerrit.common.data.WebLinkInfoCommon;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
@@ -40,6 +41,7 @@
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.Image;
import java.util.ArrayList;
import java.util.List;
@@ -65,7 +67,7 @@
DivElement history;
@UiField
- Anchor gitweb;
+ FlowPanel webLinkPanel;
@UiField
FlowPanel localContainer;
@@ -120,16 +122,7 @@
} else {
inheritsFrom.getStyle().setDisplay(Display.NONE);
}
-
- GitwebInfo c = Gerrit.info().gitweb();
- if (value.isConfigVisible() && c != null) {
- history.getStyle().setDisplay(Display.BLOCK);
- gitweb.setText(c.getLinkName());
- gitweb.setHref(c.toFileHistory(new Branch.NameKey(value.getProjectName(),
- RefNames.REFS_CONFIG), "project.config"));
- } else {
- history.getStyle().setDisplay(Display.NONE);
- }
+ setUpWebLinks();
addSection.setVisible(editing && (!value.getOwnerOf().isEmpty() || value.canUpload()));
}
@@ -162,6 +155,53 @@
addSection.setVisible(editing);
}
+ private void setUpWebLinks() {
+ if (!value.isConfigVisible()) {
+ history.getStyle().setDisplay(Display.NONE);
+ } else {
+ GitwebInfo c = Gerrit.info().gitweb();
+ List<WebLinkInfoCommon> links = value.getFileHistoryLinks();
+ if (c == null && links == null) {
+ history.getStyle().setDisplay(Display.NONE);
+ }
+ if (c != null) {
+ webLinkPanel.add(toAnchor(c.toFileHistory(new Branch.NameKey(value.getProjectName(),
+ RefNames.REFS_CONFIG), "project.config"), c.getLinkName()));
+ }
+
+ if (links != null) {
+ for (WebLinkInfoCommon link : links) {
+ webLinkPanel.add(toAnchor(link));
+ }
+ }
+ }
+ }
+
+ private Anchor toAnchor(String href, String name) {
+ Anchor a = new Anchor();
+ a.setHref(href);
+ a.setText(name);
+ return a;
+ }
+
+ private static Anchor toAnchor(WebLinkInfoCommon info) {
+ Anchor a = new Anchor();
+ a.setHref(info.url);
+ if (info.target != null && !info.target.isEmpty()) {
+ a.setTarget(info.target);
+ }
+ if (info.imageUrl != null && !info.imageUrl.isEmpty()) {
+ Image img = new Image();
+ img.setAltText(info.name);
+ img.setUrl(info.imageUrl);
+ img.setTitle(info.name);
+ a.getElement().appendChild(img.getElement());
+ } else {
+ a.setText("(" + info.name + ")");
+ }
+ return a;
+ }
+
private class Source extends EditorSource<AccessSectionEditor> {
private final FlowPanel container;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
index 0db4779..ebe6caf 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
@@ -39,9 +39,12 @@
.historyTitle {
font-weight: bold;
}
- .gitwebLink {
+ .webLinkPanel a {
display: inline;
}
+ .webLinkPanel>a {
+ margin-left:2px;
+ }
.addContainer {
margin-top: 5px;
@@ -62,7 +65,9 @@
</div>
<div ui:field='history' class='{style.history}'>
<span class='{style.historyTitle}'><ui:msg>History:</ui:msg></span>
- <g:Anchor ui:field='gitweb' styleName='{style.gitwebLink}'></g:Anchor>
+ <td>
+ <g:FlowPanel ui:field="webLinkPanel" styleName='{style.webLinkPanel}'/>
+ </td>
</div>
<g:FlowPanel ui:field='localContainer'/>
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 3615610..aee238d 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
@@ -15,6 +15,7 @@
package com.google.gerrit.httpd.rpc.project;
import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.Maps;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GroupDescription;
@@ -23,11 +24,13 @@
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.common.data.WebLinkInfoCommon;
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.config.AllProjectsName;
@@ -64,6 +67,7 @@
private final Project.NameKey projectName;
private ProjectControl pc;
+ private WebLinks webLinks;
@Inject
ProjectAccessFactory(final GroupBackend groupBackend,
@@ -72,6 +76,7 @@
final GroupControl.Factory groupControlFactory,
final MetaDataUpdate.Server metaDataUpdateFactory,
final AllProjectsName allProjectsName,
+ final WebLinks webLinks,
@Assisted final Project.NameKey name) {
this.groupBackend = groupBackend;
@@ -80,6 +85,7 @@
this.groupControlFactory = groupControlFactory;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.allProjectsName = allProjectsName;
+ this.webLinks = webLinks;
this.projectName = name;
}
@@ -209,9 +215,17 @@
detail.setConfigVisible(pc.isOwner() || metaConfigControl.isVisible());
detail.setGroupInfo(buildGroupInfo(local));
detail.setLabelTypes(pc.getLabelTypes());
+ detail.setFileHistoryLinks(getConfigFileLogLinks(projectName.get()));
return detail;
}
+ private List<WebLinkInfoCommon> getConfigFileLogLinks(String projectName) {
+ FluentIterable<WebLinkInfoCommon> links =
+ webLinks.getFileHistoryLinksCommon(projectName, RefNames.REFS_CONFIG,
+ ProjectConfig.PROJECT_CONFIG);
+ return links.isEmpty() ? null : links.toList();
+ }
+
private Map<AccountGroup.UUID, GroupInfo> buildGroupInfo(List<AccessSection> local) {
Map<AccountGroup.UUID, GroupInfo> infos = new HashMap<>();
for (AccessSection section : local) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java b/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java
index 1403e60..351de5e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/WebLinks.java
@@ -18,11 +18,13 @@
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
+import com.google.gerrit.common.data.WebLinkInfoCommon;
import com.google.gerrit.extensions.common.DiffWebLinkInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.webui.BranchWebLink;
import com.google.gerrit.extensions.webui.DiffWebLink;
+import com.google.gerrit.extensions.webui.FileHistoryWebLink;
import com.google.gerrit.extensions.webui.FileWebLink;
import com.google.gerrit.extensions.webui.PatchSetWebLink;
import com.google.gerrit.extensions.webui.ProjectWebLink;
@@ -53,9 +55,26 @@
return true;
}
};
+ private static final Predicate<WebLinkInfoCommon> INVALID_WEBLINK_COMMON =
+ new Predicate<WebLinkInfoCommon>() {
+
+ @Override
+ public boolean apply(WebLinkInfoCommon link) {
+ if (link == null) {
+ return false;
+ } else if (Strings.isNullOrEmpty(link.name)
+ || Strings.isNullOrEmpty(link.url)) {
+ log.warn(String.format("%s is missing name and/or url", link
+ .getClass().getName()));
+ return false;
+ }
+ return true;
+ }
+ };
private final DynamicSet<PatchSetWebLink> patchSetLinks;
private final DynamicSet<FileWebLink> fileLinks;
+ private final DynamicSet<FileHistoryWebLink> fileHistoryLinks;
private final DynamicSet<DiffWebLink> diffLinks;
private final DynamicSet<ProjectWebLink> projectLinks;
private final DynamicSet<BranchWebLink> branchLinks;
@@ -63,11 +82,14 @@
@Inject
public WebLinks(DynamicSet<PatchSetWebLink> patchSetLinks,
DynamicSet<FileWebLink> fileLinks,
+ DynamicSet<FileHistoryWebLink> fileLogLinks,
DynamicSet<DiffWebLink> diffLinks,
DynamicSet<ProjectWebLink> projectLinks,
- DynamicSet<BranchWebLink> branchLinks) {
+ DynamicSet<BranchWebLink> branchLinks
+ ) {
this.patchSetLinks = patchSetLinks;
this.fileLinks = fileLinks;
+ this.fileHistoryLinks = fileLogLinks;
this.diffLinks = diffLinks;
this.projectLinks = projectLinks;
this.branchLinks = branchLinks;
@@ -111,6 +133,46 @@
/**
*
* @param project Project name.
+ * @param revision SHA1 of revision.
+ * @param file File name.
+ * @return Links for file history
+ */
+ public FluentIterable<WebLinkInfo> getFileHistoryLinks(final String project,
+ final String revision, final String file) {
+ return filterLinks(fileHistoryLinks, new Function<WebLink, WebLinkInfo>() {
+
+ @Override
+ public WebLinkInfo apply(WebLink webLink) {
+ return ((FileHistoryWebLink) webLink).getFileHistoryWebLink(project,
+ revision, file);
+ }
+ });
+ }
+
+ public FluentIterable<WebLinkInfoCommon> getFileHistoryLinksCommon(
+ final String project, final String revision, final String file) {
+ return FluentIterable
+ .from(fileHistoryLinks)
+ .transform(new Function<WebLink, WebLinkInfoCommon>() {
+ @Override
+ public WebLinkInfoCommon apply(WebLink webLink) {
+ WebLinkInfo info =
+ ((FileHistoryWebLink) webLink).getFileHistoryWebLink(project,
+ revision, file);
+ WebLinkInfoCommon commonInfo = new WebLinkInfoCommon();
+ commonInfo.name = info.name;
+ commonInfo.imageUrl = info.imageUrl;
+ commonInfo.url = info.url;
+ commonInfo.target = info.target;
+ return commonInfo;
+ }
+ })
+ .filter(INVALID_WEBLINK_COMMON);
+ }
+
+ /**
+ *
+ * @param project Project name.
* @param patchSetIdA Patch set ID of side A, <code>null</code> if no base
* patch set was selected.
* @param revisionA SHA1 of revision of side A.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 478febe..a452561 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -37,6 +37,7 @@
import com.google.gerrit.extensions.systemstatus.MessageOfTheDay;
import com.google.gerrit.extensions.webui.BranchWebLink;
import com.google.gerrit.extensions.webui.DiffWebLink;
+import com.google.gerrit.extensions.webui.FileHistoryWebLink;
import com.google.gerrit.extensions.webui.FileWebLink;
import com.google.gerrit.extensions.webui.PatchSetWebLink;
import com.google.gerrit.extensions.webui.ProjectWebLink;
@@ -285,6 +286,7 @@
DynamicMap.mapOf(binder(), ProjectConfigEntry.class);
DynamicSet.setOf(binder(), PatchSetWebLink.class);
DynamicSet.setOf(binder(), FileWebLink.class);
+ DynamicSet.setOf(binder(), FileHistoryWebLink.class);
DynamicSet.setOf(binder(), DiffWebLink.class);
DynamicSet.setOf(binder(), ProjectWebLink.class);
DynamicSet.setOf(binder(), BranchWebLink.class);
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index ac84984..7306135 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit ac84984d90e09be358754e707a192ddce2ea6d63
+Subproject commit 730613516a733fa33f684cbe03fe22ecf811216e