blob: a491976e8767f29bdfe406014540b84c572e9372 [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;
18
Dave Borowitz9de65952012-08-13 16:09:45 -070019import com.google.common.base.Strings;
Dave Borowitz5db0eed2012-12-28 15:08:39 -080020import com.google.common.collect.ImmutableList;
Shawn Pearce45e83752015-02-20 17:59:05 -080021import com.google.common.collect.ImmutableMap;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080022import com.google.common.collect.Maps;
Jakub Vrana9d219fb2019-04-02 14:26:05 +020023import com.google.common.html.types.SafeHtml;
Dave Borowitz2b2f34b2014-04-29 16:47:20 -070024import com.google.gitiles.DateFormatter.Format;
Masaya Suzuki5cecb862019-03-25 17:35:44 -070025import com.google.gitiles.GitilesRequestFailureException.FailureReason;
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;
Dave Borowitz3b744b12016-08-19 16:11:10 -040028import java.io.IOException;
29import java.io.OutputStream;
30import java.io.Writer;
31import java.util.List;
32import java.util.Map;
David Pursehouse7a7f5472016-10-14 09:59:20 +090033import java.util.Optional;
Matthias Sohnc156c962023-09-30 22:15:23 +020034import javax.annotation.Nullable;
Dave Borowitz3b744b12016-08-19 16:11:10 -040035import javax.servlet.http.HttpServletRequest;
36import javax.servlet.http.HttpServletResponse;
Shawn Pearce45e83752015-02-20 17:59:05 -080037import org.eclipse.jgit.errors.IncorrectObjectTypeException;
Dave Borowitz9de65952012-08-13 16:09:45 -070038import org.eclipse.jgit.http.server.ServletUtils;
Shawn Pearce45e83752015-02-20 17:59:05 -080039import org.eclipse.jgit.lib.Config;
Dave Borowitz9de65952012-08-13 16:09:45 -070040import org.eclipse.jgit.lib.Constants;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080041import org.eclipse.jgit.lib.ObjectId;
42import org.eclipse.jgit.lib.Repository;
43import org.eclipse.jgit.revwalk.RevCommit;
44import org.eclipse.jgit.revwalk.RevObject;
Shawn Pearce45e83752015-02-20 17:59:05 -080045import org.eclipse.jgit.revwalk.RevTree;
Dave Borowitz14ce8282012-12-20 14:08:25 -080046import org.eclipse.jgit.revwalk.RevWalk;
Dave Borowitz9de65952012-08-13 16:09:45 -070047
Dave Borowitz9de65952012-08-13 16:09:45 -070048/** Serves the index page for a repository, if accessed directly by a browser. */
49public class RepositoryIndexServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080050 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070051
Dave Borowitz209d0aa2012-12-28 14:28:53 -080052 static final int REF_LIMIT = 10;
53 private static final int LOG_LIMIT = 20;
Shawn Pearce45e83752015-02-20 17:59:05 -080054 private static final int LOG_WITH_README_LIMIT = 5;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080055
Dave Borowitz14ce8282012-12-20 14:08:25 -080056 private final TimeCache timeCache;
57
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020058 public RepositoryIndexServlet(
59 GitilesAccess.Factory accessFactory, Renderer renderer, TimeCache timeCache) {
Dave Borowitz8d6d6872014-03-16 15:18:14 -070060 super(renderer, accessFactory);
Dave Borowitz14ce8282012-12-20 14:08:25 -080061 this.timeCache = checkNotNull(timeCache, "timeCache");
Dave Borowitz9de65952012-08-13 16:09:45 -070062 }
63
64 @Override
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020065 protected void doHead(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearce10e68e62016-01-02 09:37:58 -080066 // If the repository didn't exist a prior filter would have 404 replied.
67 Optional<FormatType> format = getFormat(req);
68 if (!format.isPresent()) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070069 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_RESPONSE_FORMAT);
Shawn Pearce10e68e62016-01-02 09:37:58 -080070 }
71 switch (format.get()) {
72 case HTML:
73 case JSON:
74 res.setStatus(HttpServletResponse.SC_OK);
75 res.setContentType(format.get().getMimeType());
76 break;
77 case TEXT:
David Pursehousecb91aaf2016-06-15 22:05:24 +090078 case DEFAULT:
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020079 default:
Masaya Suzuki5cecb862019-03-25 17:35:44 -070080 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_RESPONSE_FORMAT);
Shawn Pearce10e68e62016-01-02 09:37:58 -080081 }
82 }
83
84 @Override
Kevin Graney77dbea92014-07-08 14:07:40 -040085 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -070086 GitilesView view = ViewFilter.getView(req);
87 Repository repo = ServletUtils.getRepository(req);
88 GitilesAccess access = getAccess(req);
89 RepositoryDescription desc = access.getRepositoryDescription();
90
Shawn Pearceb5ad0a02015-05-24 20:33:17 -070091 try (RevWalk walk = new RevWalk(repo)) {
92 Paginator paginator = null;
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -070093 Map<String, Object> data = Maps.newHashMapWithExpectedSize(7);
94 List<Map<String, Object>> tags = RefServlet.getTagsSoyData(req, timeCache, walk, REF_LIMIT);
95 ObjectId headId = repo.resolve(Constants.HEAD);
96 if (headId != null) {
97 RevObject head = walk.parseAny(headId);
Shawn Pearce45e83752015-02-20 17:59:05 -080098 int limit = LOG_LIMIT;
Shawn Pearcec68ad0b2016-05-28 16:52:47 -070099 Map<String, Object> readme = renderReadme(req, walk, view, access.getConfig(), head);
Shawn Pearce45e83752015-02-20 17:59:05 -0800100 if (readme != null) {
101 data.putAll(readme);
102 limit = LOG_WITH_README_LIMIT;
103 }
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700104 // TODO(dborowitz): Handle non-commit or missing HEAD?
105 if (head.getType() == Constants.OBJ_COMMIT) {
106 walk.reset();
107 walk.markStart((RevCommit) head);
Shawn Pearce45e83752015-02-20 17:59:05 -0800108 paginator = new Paginator(walk, limit, null);
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700109 }
110 }
111 if (!data.containsKey("entries")) {
112 data.put("entries", ImmutableList.of());
113 }
114 List<Map<String, Object>> branches = RefServlet.getBranchesSoyData(req, REF_LIMIT);
115
116 data.put("cloneUrl", desc.cloneUrl);
117 data.put("mirroredFromUrl", Strings.nullToEmpty(desc.mirroredFromUrl));
118 data.put("description", Strings.nullToEmpty(desc.description));
119 data.put("branches", trim(branches));
120 if (branches.size() > REF_LIMIT) {
121 data.put("moreBranchesUrl", GitilesView.refs().copyFrom(view).toUrl());
122 }
123 data.put("tags", trim(tags));
124 data.put("hasLog", paginator != null);
125 if (tags.size() > REF_LIMIT) {
126 data.put("moreTagsUrl", GitilesView.refs().copyFrom(view).toUrl());
127 }
128 GitilesConfig.putVariant(getAccess(req).getConfig(), "logEntry", "logEntryVariant", data);
129
130 if (paginator != null) {
131 DateFormatter df = new DateFormatter(access, Format.DEFAULT);
Dave Borowitze360d5c2015-09-16 16:53:30 -0400132 try (OutputStream out =
Sven Selbergd99004c2022-01-31 10:24:08 +0100133 startRenderStreamingHtml(
134 req, res, "com.google.gitiles.templates.RepositoryIndex.repositoryIndex", data)) {
Shawn Pearce4c2eb852014-08-26 15:35:33 -0700135 Writer w = newWriter(out, res);
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700136 new LogSoyData(req, access, "oneline")
Dave Borowitz571b7a02018-02-09 15:18:10 -0500137 .renderStreaming(
138 paginator, "HEAD", renderer, w, df, LogSoyData.FooterBehavior.LOG_HEAD);
Shawn Pearce4c2eb852014-08-26 15:35:33 -0700139 w.flush();
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700140 }
141 } else {
Sven Selbergd99004c2022-01-31 10:24:08 +0100142 renderHtml(req, res, "com.google.gitiles.templates.RepositoryIndex.repositoryIndex", data);
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700143 }
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700144 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700145 }
146
Kevin Graney77dbea92014-07-08 14:07:40 -0400147 @Override
148 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
149 GitilesAccess access = getAccess(req);
150 RepositoryDescription desc = access.getRepositoryDescription();
151 renderJson(req, res, desc, new TypeToken<RepositoryDescription>() {}.getType());
152 }
153
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800154 private static <T> List<T> trim(List<T> list) {
155 return list.size() > REF_LIMIT ? list.subList(0, REF_LIMIT) : list;
Dave Borowitz14ce8282012-12-20 14:08:25 -0800156 }
Shawn Pearce45e83752015-02-20 17:59:05 -0800157
Matthias Sohnc156c962023-09-30 22:15:23 +0200158 private static @Nullable Map<String, Object> renderReadme(
Shawn Pearcec68ad0b2016-05-28 16:52:47 -0700159 HttpServletRequest req, RevWalk walk, GitilesView view, Config cfg, RevObject head)
160 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),
Shawn Pearcec68ad0b2016-05-28 16:52:47 -0700173 rootTree,
174 req.getRequestURI());
Shawn Pearce45e83752015-02-20 17:59:05 -0800175 readme.scanTree(rootTree);
176 if (readme.isPresent()) {
Jakub Vrana9d219fb2019-04-02 14:26:05 +0200177 SafeHtml html = readme.render();
Shawn Pearce08e38f22016-05-24 16:40:18 -0700178 if (html != null) {
179 return ImmutableMap.<String, Object>of("readmeHtml", html);
180 }
Shawn Pearce45e83752015-02-20 17:59:05 -0800181 }
182 return null;
183 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700184}