blob: a6758a9a2a35672ab47518422cdeedcfbd6b319e [file] [log] [blame]
Dave Borowitz9de65952012-08-13 16:09:45 -07001// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.gitiles;
16
17import static com.google.common.base.Preconditions.checkNotNull;
Shawn Pearce10e68e62016-01-02 09:37:58 -080018import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
Dave Borowitz9de65952012-08-13 16:09:45 -070019
Shawn Pearce10e68e62016-01-02 09:37:58 -080020import com.google.common.base.Optional;
Dave Borowitz9de65952012-08-13 16:09:45 -070021import com.google.common.base.Strings;
Dave Borowitz5db0eed2012-12-28 15:08:39 -080022import com.google.common.collect.ImmutableList;
Shawn Pearce45e83752015-02-20 17:59:05 -080023import com.google.common.collect.ImmutableMap;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080024import com.google.common.collect.Maps;
Dave Borowitz2b2f34b2014-04-29 16:47:20 -070025import com.google.gitiles.DateFormatter.Format;
Shawn Pearce47fd6562016-05-28 14:15:15 -070026import com.google.gitiles.doc.MarkdownConfig;
Kevin Graney77dbea92014-07-08 14:07:40 -040027import com.google.gson.reflect.TypeToken;
Shawn Pearce08e38f22016-05-24 16:40:18 -070028import com.google.template.soy.data.SanitizedContent;
Dave Borowitz9de65952012-08-13 16:09:45 -070029
Shawn Pearce45e83752015-02-20 17:59:05 -080030import org.eclipse.jgit.errors.IncorrectObjectTypeException;
Dave Borowitz9de65952012-08-13 16:09:45 -070031import org.eclipse.jgit.http.server.ServletUtils;
Shawn Pearce45e83752015-02-20 17:59:05 -080032import org.eclipse.jgit.lib.Config;
Dave Borowitz9de65952012-08-13 16:09:45 -070033import org.eclipse.jgit.lib.Constants;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080034import org.eclipse.jgit.lib.ObjectId;
35import org.eclipse.jgit.lib.Repository;
36import org.eclipse.jgit.revwalk.RevCommit;
37import org.eclipse.jgit.revwalk.RevObject;
Shawn Pearce45e83752015-02-20 17:59:05 -080038import org.eclipse.jgit.revwalk.RevTree;
Dave Borowitz14ce8282012-12-20 14:08:25 -080039import org.eclipse.jgit.revwalk.RevWalk;
Dave Borowitz9de65952012-08-13 16:09:45 -070040
41import java.io.IOException;
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -070042import java.io.OutputStream;
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -070043import java.io.Writer;
Dave Borowitz9de65952012-08-13 16:09:45 -070044import java.util.List;
45import java.util.Map;
46
47import javax.servlet.http.HttpServletRequest;
48import javax.servlet.http.HttpServletResponse;
49
50/** Serves the index page for a repository, if accessed directly by a browser. */
51public class RepositoryIndexServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080052 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070053
Dave Borowitz209d0aa2012-12-28 14:28:53 -080054 static final int REF_LIMIT = 10;
55 private static final int LOG_LIMIT = 20;
Shawn Pearce45e83752015-02-20 17:59:05 -080056 private static final int LOG_WITH_README_LIMIT = 5;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080057
Dave Borowitz14ce8282012-12-20 14:08:25 -080058 private final TimeCache timeCache;
59
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020060 public RepositoryIndexServlet(
61 GitilesAccess.Factory accessFactory, Renderer renderer, TimeCache timeCache) {
Dave Borowitz8d6d6872014-03-16 15:18:14 -070062 super(renderer, accessFactory);
Dave Borowitz14ce8282012-12-20 14:08:25 -080063 this.timeCache = checkNotNull(timeCache, "timeCache");
Dave Borowitz9de65952012-08-13 16:09:45 -070064 }
65
66 @Override
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020067 protected void doHead(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearce10e68e62016-01-02 09:37:58 -080068 // If the repository didn't exist a prior filter would have 404 replied.
69 Optional<FormatType> format = getFormat(req);
70 if (!format.isPresent()) {
71 res.sendError(SC_BAD_REQUEST);
72 return;
73 }
74 switch (format.get()) {
75 case HTML:
76 case JSON:
77 res.setStatus(HttpServletResponse.SC_OK);
78 res.setContentType(format.get().getMimeType());
79 break;
80 case TEXT:
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020081 default:
Shawn Pearce10e68e62016-01-02 09:37:58 -080082 res.sendError(SC_BAD_REQUEST);
83 break;
84 }
85 }
86
87 @Override
Kevin Graney77dbea92014-07-08 14:07:40 -040088 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -070089 GitilesView view = ViewFilter.getView(req);
90 Repository repo = ServletUtils.getRepository(req);
91 GitilesAccess access = getAccess(req);
92 RepositoryDescription desc = access.getRepositoryDescription();
93
Shawn Pearceb5ad0a02015-05-24 20:33:17 -070094 try (RevWalk walk = new RevWalk(repo)) {
95 Paginator paginator = null;
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -070096 Map<String, Object> data = Maps.newHashMapWithExpectedSize(7);
97 List<Map<String, Object>> tags = RefServlet.getTagsSoyData(req, timeCache, walk, REF_LIMIT);
98 ObjectId headId = repo.resolve(Constants.HEAD);
99 if (headId != null) {
100 RevObject head = walk.parseAny(headId);
Shawn Pearce45e83752015-02-20 17:59:05 -0800101 int limit = LOG_LIMIT;
102 Map<String, Object> readme = renderReadme(walk, view, access.getConfig(), head);
103 if (readme != null) {
104 data.putAll(readme);
105 limit = LOG_WITH_README_LIMIT;
106 }
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700107 // TODO(dborowitz): Handle non-commit or missing HEAD?
108 if (head.getType() == Constants.OBJ_COMMIT) {
109 walk.reset();
110 walk.markStart((RevCommit) head);
Shawn Pearce45e83752015-02-20 17:59:05 -0800111 paginator = new Paginator(walk, limit, null);
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700112 }
113 }
114 if (!data.containsKey("entries")) {
115 data.put("entries", ImmutableList.of());
116 }
117 List<Map<String, Object>> branches = RefServlet.getBranchesSoyData(req, REF_LIMIT);
118
119 data.put("cloneUrl", desc.cloneUrl);
120 data.put("mirroredFromUrl", Strings.nullToEmpty(desc.mirroredFromUrl));
121 data.put("description", Strings.nullToEmpty(desc.description));
122 data.put("branches", trim(branches));
123 if (branches.size() > REF_LIMIT) {
124 data.put("moreBranchesUrl", GitilesView.refs().copyFrom(view).toUrl());
125 }
126 data.put("tags", trim(tags));
127 data.put("hasLog", paginator != null);
128 if (tags.size() > REF_LIMIT) {
129 data.put("moreTagsUrl", GitilesView.refs().copyFrom(view).toUrl());
130 }
131 GitilesConfig.putVariant(getAccess(req).getConfig(), "logEntry", "logEntryVariant", data);
132
133 if (paginator != null) {
134 DateFormatter df = new DateFormatter(access, Format.DEFAULT);
Dave Borowitze360d5c2015-09-16 16:53:30 -0400135 try (OutputStream out =
136 startRenderStreamingHtml(req, res, "gitiles.repositoryIndex", data)) {
Shawn Pearce4c2eb852014-08-26 15:35:33 -0700137 Writer w = newWriter(out, res);
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700138 new LogSoyData(req, access, "oneline")
139 .renderStreaming(paginator, "HEAD", renderer, w, df);
Shawn Pearce4c2eb852014-08-26 15:35:33 -0700140 w.flush();
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700141 }
142 } else {
143 renderHtml(req, res, "gitiles.repositoryIndex", data);
144 }
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700145 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700146 }
147
Kevin Graney77dbea92014-07-08 14:07:40 -0400148 @Override
149 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
150 GitilesAccess access = getAccess(req);
151 RepositoryDescription desc = access.getRepositoryDescription();
152 renderJson(req, res, desc, new TypeToken<RepositoryDescription>() {}.getType());
153 }
154
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800155 private static <T> List<T> trim(List<T> list) {
156 return list.size() > REF_LIMIT ? list.subList(0, REF_LIMIT) : list;
Dave Borowitz14ce8282012-12-20 14:08:25 -0800157 }
Shawn Pearce45e83752015-02-20 17:59:05 -0800158
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200159 private static Map<String, Object> renderReadme(
160 RevWalk walk, GitilesView view, Config cfg, RevObject head) throws IOException {
Shawn Pearce45e83752015-02-20 17:59:05 -0800161 RevTree rootTree;
162 try {
163 rootTree = walk.parseTree(head);
164 } catch (IncorrectObjectTypeException notTreeish) {
165 return null;
166 }
167
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200168 ReadmeHelper readme =
169 new ReadmeHelper(
170 walk.getObjectReader(),
171 GitilesView.path().copyFrom(view).setRevision(Revision.HEAD).setPathPart("/").build(),
Shawn Pearce47fd6562016-05-28 14:15:15 -0700172 MarkdownConfig.get(cfg),
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200173 rootTree);
Shawn Pearce45e83752015-02-20 17:59:05 -0800174 readme.scanTree(rootTree);
175 if (readme.isPresent()) {
Shawn Pearce08e38f22016-05-24 16:40:18 -0700176 SanitizedContent html = readme.render();
177 if (html != null) {
178 return ImmutableMap.<String, Object>of("readmeHtml", html);
179 }
Shawn Pearce45e83752015-02-20 17:59:05 -0800180 }
181 return null;
182 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700183}