Add WebLink extension point for file history

Change-Id: Ib70f1beeb03dfbf7ebb0ccef0b6f46157b9fac3a
This commit is contained in:
Sven Selberg 2015-06-03 16:21:40 +02:00
parent f707c8a98d
commit 21c8686867
9 changed files with 208 additions and 15 deletions

View File

@ -32,6 +32,7 @@ public class ProjectAccess {
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 class ProjectAccess {
public void setGroupInfo(Map<AccountGroup.UUID, GroupInfo> m) {
groupInfo = m;
}
public void setFileHistoryLinks(List<WebLinkInfoCommon> links) {
fileHistoryLinks = links;
}
public List<WebLinkInfoCommon> getFileHistoryLinks() {
return fileHistoryLinks;
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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<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;

View File

@ -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.
</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'/>

View File

@ -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<ProjectAccess> {
private final Project.NameKey projectName;
private ProjectControl pc;
private WebLinks webLinks;
@Inject
ProjectAccessFactory(final GroupBackend groupBackend,
@ -72,6 +76,7 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
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<ProjectAccess> {
this.groupControlFactory = groupControlFactory;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.allProjectsName = allProjectsName;
this.webLinks = webLinks;
this.projectName = name;
}
@ -209,9 +215,17 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
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) {

View File

@ -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<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 @@ public class WebLinks {
@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;
@ -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<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.

View File

@ -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);

@ -1 +1 @@
Subproject commit ac84984d90e09be358754e707a192ddce2ea6d63
Subproject commit 730613516a733fa33f684cbe03fe22ecf811216e