Files
gerrit/java/com/google/gerrit/server/config/GitwebConfig.java
Gal Paikin bdcba6961d Expose the entire commit message instead of only the subject
Some callers are interested in the entire commit message, or more
specifically in the footers of the commit message. Hence, we expose the
entire commit message to give the callers more flexibility.

Original change that exposed the subject in I420161ca4.

Change-Id: I40341c4daee118a5b80958e7221abed835d52a05
2021-01-07 14:57:28 +00:00

377 lines
13 KiB
Java

// 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.
package com.google.gerrit.server.config;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.base.Strings.nullToEmpty;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GitwebType;
import com.google.gerrit.common.data.ParameterizedString;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.extensions.webui.BranchWebLink;
import com.google.gerrit.extensions.webui.FileHistoryWebLink;
import com.google.gerrit.extensions.webui.FileWebLink;
import com.google.gerrit.extensions.webui.ParentWebLink;
import com.google.gerrit.extensions.webui.PatchSetWebLink;
import com.google.gerrit.extensions.webui.ProjectWebLink;
import com.google.gerrit.extensions.webui.TagWebLink;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.jgit.lib.Config;
public class GitwebConfig {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public static boolean isDisabled(Config cfg) {
return isEmptyString(cfg, "gitweb", null, "url")
|| isEmptyString(cfg, "gitweb", null, "cgi")
|| "disabled".equals(cfg.getString("gitweb", null, "type"));
}
public static class LegacyModule extends AbstractModule {
private final Config cfg;
public LegacyModule(Config cfg) {
this.cfg = cfg;
}
@Override
protected void configure() {
GitwebType type = typeFromConfig(cfg);
if (type != null) {
bind(GitwebType.class).toInstance(type);
if (!isNullOrEmpty(type.getBranch())) {
DynamicSet.bind(binder(), BranchWebLink.class).to(GitwebLinks.class);
}
if (!isNullOrEmpty(type.getTag())) {
DynamicSet.bind(binder(), TagWebLink.class).to(GitwebLinks.class);
}
if (!isNullOrEmpty(type.getFile()) || !isNullOrEmpty(type.getRootTree())) {
DynamicSet.bind(binder(), FileWebLink.class).to(GitwebLinks.class);
}
if (!isNullOrEmpty(type.getFileHistory())) {
DynamicSet.bind(binder(), FileHistoryWebLink.class).to(GitwebLinks.class);
}
if (!isNullOrEmpty(type.getRevision())) {
DynamicSet.bind(binder(), PatchSetWebLink.class).to(GitwebLinks.class);
DynamicSet.bind(binder(), ParentWebLink.class).to(GitwebLinks.class);
}
if (!isNullOrEmpty(type.getProject())) {
DynamicSet.bind(binder(), ProjectWebLink.class).to(GitwebLinks.class);
}
}
}
}
private static boolean isEmptyString(Config cfg, String section, String subsection, String name) {
// This is currently the only way to check for the empty string in a JGit
// config. Fun!
String[] values = cfg.getStringList(section, subsection, name);
return values.length > 0 && isNullOrEmpty(values[0]);
}
private static GitwebType typeFromConfig(Config cfg) {
GitwebType defaultType = defaultType(cfg.getString("gitweb", null, "type"));
if (defaultType == null) {
return null;
}
GitwebType type = new GitwebType();
type.setLinkName(
firstNonNull(cfg.getString("gitweb", null, "linkname"), defaultType.getLinkName()));
type.setBranch(firstNonNull(cfg.getString("gitweb", null, "branch"), defaultType.getBranch()));
type.setTag(firstNonNull(cfg.getString("gitweb", null, "tag"), defaultType.getTag()));
type.setProject(
firstNonNull(cfg.getString("gitweb", null, "project"), defaultType.getProject()));
type.setRevision(
firstNonNull(cfg.getString("gitweb", null, "revision"), defaultType.getRevision()));
type.setRootTree(
firstNonNull(cfg.getString("gitweb", null, "roottree"), defaultType.getRootTree()));
type.setFile(firstNonNull(cfg.getString("gitweb", null, "file"), defaultType.getFile()));
type.setFileHistory(
firstNonNull(cfg.getString("gitweb", null, "filehistory"), defaultType.getFileHistory()));
type.setUrlEncode(cfg.getBoolean("gitweb", null, "urlencode", defaultType.getUrlEncode()));
String pathSeparator = cfg.getString("gitweb", null, "pathSeparator");
if (pathSeparator != null) {
if (pathSeparator.length() == 1) {
char c = pathSeparator.charAt(0);
if (isValidPathSeparator(c)) {
type.setPathSeparator(firstNonNull(c, defaultType.getPathSeparator()));
} else {
logger.atWarning().log("Invalid gitweb.pathSeparator: %s", c);
}
} else {
logger.atWarning().log("gitweb.pathSeparator is not a single character: %s", pathSeparator);
}
}
return type;
}
private static GitwebType defaultType(String typeName) {
GitwebType type = new GitwebType();
switch (nullToEmpty(typeName)) {
case "gitweb":
type.setLinkName("gitweb");
type.setProject("?p=${project}.git;a=summary");
type.setRevision("?p=${project}.git;a=commit;h=${commit}");
type.setBranch("?p=${project}.git;a=shortlog;h=${branch}");
type.setTag("?p=${project}.git;a=tag;h=${tag}");
type.setRootTree("?p=${project}.git;a=tree;hb=${commit}");
type.setFile("?p=${project}.git;hb=${commit};f=${file}");
type.setFileHistory("?p=${project}.git;a=history;hb=${branch};f=${file}");
break;
case "cgit":
type.setLinkName("cgit");
type.setProject("${project}.git/summary");
type.setRevision("${project}.git/commit/?id=${commit}");
type.setBranch("${project}.git/log/?h=${branch}");
type.setTag("${project}.git/tag/?h=${tag}");
type.setRootTree("${project}.git/tree/?h=${commit}");
type.setFile("${project}.git/tree/${file}?h=${commit}");
type.setFileHistory("${project}.git/log/${file}?h=${branch}");
break;
case "custom":
// For a custom type with no explicit link name, just reuse "gitweb".
type.setLinkName("gitweb");
type.setProject("");
type.setRevision("");
type.setBranch("");
type.setTag("");
type.setRootTree("");
type.setFile("");
type.setFileHistory("");
break;
case "":
case "disabled":
default:
return null;
}
return type;
}
private final String url;
private final GitwebType type;
@Inject
GitwebConfig(
GitwebCgiConfig cgiConfig,
@GerritServerConfig Config cfg,
@Nullable @CanonicalWebUrl String gerritUrl)
throws MalformedURLException {
if (isDisabled(cfg)) {
type = null;
url = null;
} else {
String cfgUrl = cfg.getString("gitweb", null, "url");
type = typeFromConfig(cfg);
if (type == null) {
url = null;
} else if (cgiConfig.getGitwebCgi() == null) {
// Use an externally managed gitweb instance, and not an internal one.
url = cfgUrl;
} else {
String baseGerritUrl;
if (gerritUrl != null) {
URL u = new URL(gerritUrl);
baseGerritUrl = u.getPath();
} else {
baseGerritUrl = "/";
}
url = firstNonNull(cfgUrl, baseGerritUrl + "gitweb");
}
}
}
/** @return GitwebType for gitweb viewer. */
@Nullable
public GitwebType getGitwebType() {
return type;
}
/**
* @return URL of the entry point into gitweb. This URL may be relative to our context if gitweb
* is hosted by ourselves; or absolute if its hosted elsewhere; or null if gitweb has not been
* configured.
*/
public String getUrl() {
return url;
}
/**
* Determines if a given character can be used unencoded in an URL as a replacement for the path
* separator '/'.
*
* <p>Reasoning: http://www.ietf.org/rfc/rfc1738.txt § 2.2:
*
* <p>... only alphanumerics, the special characters "$-_.+!*'(),", and reserved characters used
* for their reserved purposes may be used unencoded within a URL.
*
* <p>The following characters might occur in file names, however:
*
* <p>alphanumeric characters,
*
* <p>"$-_.+!',"
*/
static boolean isValidPathSeparator(char c) {
switch (c) {
case '*':
case '(':
case ')':
return true;
default:
return false;
}
}
@Singleton
static class GitwebLinks
implements BranchWebLink,
FileHistoryWebLink,
FileWebLink,
PatchSetWebLink,
ParentWebLink,
ProjectWebLink,
TagWebLink {
private final String url;
private final GitwebType type;
private final ParameterizedString branch;
private final ParameterizedString file;
private final ParameterizedString fileHistory;
private final ParameterizedString project;
private final ParameterizedString revision;
private final ParameterizedString tag;
@Inject
GitwebLinks(GitwebConfig config, GitwebType type) {
this.url = config.getUrl();
this.type = type;
this.branch = parse(type.getBranch());
this.file = parse(firstNonNull(emptyToNull(type.getFile()), nullToEmpty(type.getRootTree())));
this.fileHistory = parse(type.getFileHistory());
this.project = parse(type.getProject());
this.revision = parse(type.getRevision());
this.tag = parse(type.getTag());
}
@Override
public WebLinkInfo getBranchWebLink(String projectName, String branchName) {
if (branch != null) {
return link(
branch
.replace("project", encode(projectName))
.replace("branch", encode(branchName))
.toString());
}
return null;
}
@Override
public WebLinkInfo getTagWebLink(String projectName, String tagName) {
if (tag != null) {
return link(
tag.replace("project", encode(projectName)).replace("tag", encode(tagName)).toString());
}
return null;
}
@Override
public WebLinkInfo getFileHistoryWebLink(String projectName, String revision, String fileName) {
if (fileHistory != null) {
return link(
fileHistory
.replace("project", encode(projectName))
.replace("branch", encode(revision))
.replace("file", encode(fileName))
.toString());
}
return null;
}
@Override
public WebLinkInfo getFileWebLink(String projectName, String revision, String fileName) {
if (file != null) {
return link(
file.replace("project", encode(projectName))
.replace("commit", encode(revision))
.replace("file", encode(fileName))
.toString());
}
return null;
}
@Override
public WebLinkInfo getPatchSetWebLink(
String projectName, String commit, String commitMessage, String branchName) {
if (revision != null) {
// commitMessage and branchName are not needed, hence not used.
return link(
revision
.replace("project", encode(projectName))
.replace("commit", encode(commit))
.toString());
}
return null;
}
@Override
public WebLinkInfo getParentWebLink(
String projectName, String commit, String commitMessage, String branchName) {
// For Gitweb treat parent revision links the same as patch set links
return getPatchSetWebLink(projectName, commit, commitMessage, branchName);
}
@Override
public WebLinkInfo getProjectWeblink(String projectName) {
if (project != null) {
return link(project.replace("project", encode(projectName)).toString());
}
return null;
}
private String encode(String val) {
if (type.getUrlEncode()) {
return Url.encode(type.replacePathSeparator(val));
}
return val;
}
private WebLinkInfo link(String rest) {
return new WebLinkInfo(type.getLinkName(), null, url + rest, null);
}
private static ParameterizedString parse(String pattern) {
if (!isNullOrEmpty(pattern)) {
return new ParameterizedString(pattern);
}
return null;
}
}
}