From 21c86868673fbd629a98673741b2e2014b0a17e7 Mon Sep 17 00:00:00 2001 From: Sven Selberg Date: Wed, 3 Jun 2015 16:21:40 +0200 Subject: [PATCH] Add WebLink extension point for file history Change-Id: Ib70f1beeb03dfbf7ebb0ccef0b6f46157b9fac3a --- .../gerrit/common/data/ProjectAccess.java | 9 +++ .../gerrit/common/data/WebLinkInfoCommon.java | 24 +++++++ .../extensions/webui/FileHistoryWebLink.java | 37 +++++++++++ .../client/admin/ProjectAccessEditor.java | 62 ++++++++++++++---- .../client/admin/ProjectAccessEditor.ui.xml | 9 ++- .../rpc/project/ProjectAccessFactory.java | 14 ++++ .../com/google/gerrit/server/WebLinks.java | 64 ++++++++++++++++++- .../server/config/GerritGlobalModule.java | 2 + plugins/cookbook-plugin | 2 +- 9 files changed, 208 insertions(+), 15 deletions(-) create mode 100644 gerrit-common/src/main/java/com/google/gerrit/common/data/WebLinkInfoCommon.java create mode 100644 gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/FileHistoryWebLink.java 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 7041dccd57..a83f46c4d1 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 @@ public class ProjectAccess { protected LabelTypes labelTypes; protected Map capabilities; protected Map groupInfo; + protected List fileHistoryLinks; public ProjectAccess() { } @@ -132,4 +133,12 @@ public class ProjectAccess { public void setGroupInfo(Map m) { groupInfo = m; } + + public void setFileHistoryLinks(List links) { + fileHistoryLinks = links; + } + + public List 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 0000000000..dd0a70a6d2 --- /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 0000000000..f9f9e58059 --- /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. + * + *

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.

+ * + * @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 13b3a54610..177faff0de 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.Hyperlink; 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.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.Image; import java.util.ArrayList; import java.util.List; @@ -65,7 +67,7 @@ public class ProjectAccessEditor extends Composite implements DivElement history; @UiField - Anchor gitweb; + FlowPanel webLinkPanel; @UiField FlowPanel localContainer; @@ -120,16 +122,7 @@ public class ProjectAccessEditor extends Composite implements } 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 @@ public class ProjectAccessEditor extends Composite implements addSection.setVisible(editing); } + private void setUpWebLinks() { + if (!value.isConfigVisible()) { + history.getStyle().setDisplay(Display.NONE); + } else { + GitwebInfo c = Gerrit.info().gitweb(); + List 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 { 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 0db477900d..ebe6caff21 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 @@ limitations under the License. .historyTitle { font-weight: bold; } - .gitwebLink { + .webLinkPanel a { display: inline; } + .webLinkPanel>a { + margin-left:2px; + } .addContainer { margin-top: 5px; @@ -62,7 +65,9 @@ limitations under the License.

History: - + + +
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 361561068b..aee238da7f 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.Permission; 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 @@ class ProjectAccessFactory extends Handler { private final Project.NameKey projectName; private ProjectControl pc; + private WebLinks webLinks; @Inject ProjectAccessFactory(final GroupBackend groupBackend, @@ -72,6 +76,7 @@ class ProjectAccessFactory extends Handler { 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 @@ class ProjectAccessFactory extends Handler { this.groupControlFactory = groupControlFactory; this.metaDataUpdateFactory = metaDataUpdateFactory; this.allProjectsName = allProjectsName; + this.webLinks = webLinks; this.projectName = name; } @@ -209,9 +215,17 @@ class ProjectAccessFactory extends Handler { detail.setConfigVisible(pc.isOwner() || metaConfigControl.isVisible()); detail.setGroupInfo(buildGroupInfo(local)); detail.setLabelTypes(pc.getLabelTypes()); + detail.setFileHistoryLinks(getConfigFileLogLinks(projectName.get())); return detail; } + private List getConfigFileLogLinks(String projectName) { + FluentIterable links = + webLinks.getFileHistoryLinksCommon(projectName, RefNames.REFS_CONFIG, + ProjectConfig.PROJECT_CONFIG); + return links.isEmpty() ? null : links.toList(); + } + private Map buildGroupInfo(List local) { Map 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 1403e60d96..351de5e41b 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.Function; 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 @@ public class WebLinks { return true; } }; + private static final Predicate INVALID_WEBLINK_COMMON = + new Predicate() { + + @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 patchSetLinks; private final DynamicSet fileLinks; + private final DynamicSet fileHistoryLinks; private final DynamicSet diffLinks; private final DynamicSet projectLinks; private final DynamicSet branchLinks; @@ -63,11 +82,14 @@ public class WebLinks { @Inject public WebLinks(DynamicSet patchSetLinks, DynamicSet fileLinks, + DynamicSet fileLogLinks, DynamicSet diffLinks, DynamicSet projectLinks, - DynamicSet branchLinks) { + DynamicSet branchLinks + ) { this.patchSetLinks = patchSetLinks; this.fileLinks = fileLinks; + this.fileHistoryLinks = fileLogLinks; this.diffLinks = diffLinks; this.projectLinks = projectLinks; this.branchLinks = branchLinks; @@ -108,6 +130,46 @@ public class WebLinks { }); } + /** + * + * @param project Project name. + * @param revision SHA1 of revision. + * @param file File name. + * @return Links for file history + */ + public FluentIterable getFileHistoryLinks(final String project, + final String revision, final String file) { + return filterLinks(fileHistoryLinks, new Function() { + + @Override + public WebLinkInfo apply(WebLink webLink) { + return ((FileHistoryWebLink) webLink).getFileHistoryWebLink(project, + revision, file); + } + }); + } + + public FluentIterable getFileHistoryLinksCommon( + final String project, final String revision, final String file) { + return FluentIterable + .from(fileHistoryLinks) + .transform(new Function() { + @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. 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 478febe8a0..a452561f6a 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.registration.DynamicSet; 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 @@ public class GerritGlobalModule extends FactoryModule { 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 ac84984d90..730613516a 160000 --- a/plugins/cookbook-plugin +++ b/plugins/cookbook-plugin @@ -1 +1 @@ -Subproject commit ac84984d90e09be358754e707a192ddce2ea6d63 +Subproject commit 730613516a733fa33f684cbe03fe22ecf811216e