| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 1 | // 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 | |
| 15 | package com.google.gitiles; |
| 16 | |
| 17 | import static com.google.common.base.Preconditions.checkNotNull; |
| Shawn Pearce | 10e68e6 | 2016-01-02 09:37:58 -0800 | [diff] [blame] | 18 | import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 19 | |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 20 | import com.google.common.base.Strings; |
| Dave Borowitz | 5db0eed | 2012-12-28 15:08:39 -0800 | [diff] [blame] | 21 | import com.google.common.collect.ImmutableList; |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 22 | import com.google.common.collect.ImmutableMap; |
| Dave Borowitz | 209d0aa | 2012-12-28 14:28:53 -0800 | [diff] [blame] | 23 | import com.google.common.collect.Maps; |
| Dave Borowitz | 2b2f34b | 2014-04-29 16:47:20 -0700 | [diff] [blame] | 24 | import com.google.gitiles.DateFormatter.Format; |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 25 | import com.google.gitiles.doc.MarkdownConfig; |
| Kevin Graney | 77dbea9 | 2014-07-08 14:07:40 -0400 | [diff] [blame] | 26 | import com.google.gson.reflect.TypeToken; |
| Shawn Pearce | 08e38f2 | 2016-05-24 16:40:18 -0700 | [diff] [blame] | 27 | import com.google.template.soy.data.SanitizedContent; |
| Dave Borowitz | 3b744b1 | 2016-08-19 16:11:10 -0400 | [diff] [blame] | 28 | import java.io.IOException; |
| 29 | import java.io.OutputStream; |
| 30 | import java.io.Writer; |
| 31 | import java.util.List; |
| 32 | import java.util.Map; |
| David Pursehouse | 7a7f547 | 2016-10-14 09:59:20 +0900 | [diff] [blame] | 33 | import java.util.Optional; |
| Dave Borowitz | 3b744b1 | 2016-08-19 16:11:10 -0400 | [diff] [blame] | 34 | import javax.servlet.http.HttpServletRequest; |
| 35 | import javax.servlet.http.HttpServletResponse; |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 36 | import org.eclipse.jgit.errors.IncorrectObjectTypeException; |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 37 | import org.eclipse.jgit.http.server.ServletUtils; |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 38 | import org.eclipse.jgit.lib.Config; |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 39 | import org.eclipse.jgit.lib.Constants; |
| Dave Borowitz | 209d0aa | 2012-12-28 14:28:53 -0800 | [diff] [blame] | 40 | import org.eclipse.jgit.lib.ObjectId; |
| 41 | import org.eclipse.jgit.lib.Repository; |
| 42 | import org.eclipse.jgit.revwalk.RevCommit; |
| 43 | import org.eclipse.jgit.revwalk.RevObject; |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 44 | import org.eclipse.jgit.revwalk.RevTree; |
| Dave Borowitz | 14ce828 | 2012-12-20 14:08:25 -0800 | [diff] [blame] | 45 | import org.eclipse.jgit.revwalk.RevWalk; |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 46 | |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 47 | /** Serves the index page for a repository, if accessed directly by a browser. */ |
| 48 | public class RepositoryIndexServlet extends BaseServlet { |
| Chad Horohoe | ad23f14 | 2012-11-12 09:45:39 -0800 | [diff] [blame] | 49 | private static final long serialVersionUID = 1L; |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 50 | |
| Dave Borowitz | 209d0aa | 2012-12-28 14:28:53 -0800 | [diff] [blame] | 51 | static final int REF_LIMIT = 10; |
| 52 | private static final int LOG_LIMIT = 20; |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 53 | private static final int LOG_WITH_README_LIMIT = 5; |
| Dave Borowitz | 209d0aa | 2012-12-28 14:28:53 -0800 | [diff] [blame] | 54 | |
| Dave Borowitz | 14ce828 | 2012-12-20 14:08:25 -0800 | [diff] [blame] | 55 | private final TimeCache timeCache; |
| 56 | |
| Han-Wen Nienhuys | c0200f6 | 2016-05-02 17:34:51 +0200 | [diff] [blame] | 57 | public RepositoryIndexServlet( |
| 58 | GitilesAccess.Factory accessFactory, Renderer renderer, TimeCache timeCache) { |
| Dave Borowitz | 8d6d687 | 2014-03-16 15:18:14 -0700 | [diff] [blame] | 59 | super(renderer, accessFactory); |
| Dave Borowitz | 14ce828 | 2012-12-20 14:08:25 -0800 | [diff] [blame] | 60 | this.timeCache = checkNotNull(timeCache, "timeCache"); |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | @Override |
| Han-Wen Nienhuys | c0200f6 | 2016-05-02 17:34:51 +0200 | [diff] [blame] | 64 | protected void doHead(HttpServletRequest req, HttpServletResponse res) throws IOException { |
| Shawn Pearce | 10e68e6 | 2016-01-02 09:37:58 -0800 | [diff] [blame] | 65 | // If the repository didn't exist a prior filter would have 404 replied. |
| 66 | Optional<FormatType> format = getFormat(req); |
| 67 | if (!format.isPresent()) { |
| 68 | res.sendError(SC_BAD_REQUEST); |
| 69 | return; |
| 70 | } |
| 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 Pursehouse | cb91aaf | 2016-06-15 22:05:24 +0900 | [diff] [blame] | 78 | case DEFAULT: |
| Han-Wen Nienhuys | c0200f6 | 2016-05-02 17:34:51 +0200 | [diff] [blame] | 79 | default: |
| Shawn Pearce | 10e68e6 | 2016-01-02 09:37:58 -0800 | [diff] [blame] | 80 | res.sendError(SC_BAD_REQUEST); |
| 81 | break; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | @Override |
| Kevin Graney | 77dbea9 | 2014-07-08 14:07:40 -0400 | [diff] [blame] | 86 | protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException { |
| Dave Borowitz | f6dcf7a | 2014-07-30 10:26:58 -0700 | [diff] [blame] | 87 | GitilesView view = ViewFilter.getView(req); |
| 88 | Repository repo = ServletUtils.getRepository(req); |
| 89 | GitilesAccess access = getAccess(req); |
| 90 | RepositoryDescription desc = access.getRepositoryDescription(); |
| 91 | |
| Shawn Pearce | b5ad0a0 | 2015-05-24 20:33:17 -0700 | [diff] [blame] | 92 | try (RevWalk walk = new RevWalk(repo)) { |
| 93 | Paginator paginator = null; |
| Dave Borowitz | f6dcf7a | 2014-07-30 10:26:58 -0700 | [diff] [blame] | 94 | Map<String, Object> data = Maps.newHashMapWithExpectedSize(7); |
| 95 | List<Map<String, Object>> tags = RefServlet.getTagsSoyData(req, timeCache, walk, REF_LIMIT); |
| 96 | ObjectId headId = repo.resolve(Constants.HEAD); |
| 97 | if (headId != null) { |
| 98 | RevObject head = walk.parseAny(headId); |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 99 | int limit = LOG_LIMIT; |
| Shawn Pearce | c68ad0b | 2016-05-28 16:52:47 -0700 | [diff] [blame] | 100 | Map<String, Object> readme = renderReadme(req, walk, view, access.getConfig(), head); |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 101 | if (readme != null) { |
| 102 | data.putAll(readme); |
| 103 | limit = LOG_WITH_README_LIMIT; |
| 104 | } |
| Dave Borowitz | f6dcf7a | 2014-07-30 10:26:58 -0700 | [diff] [blame] | 105 | // TODO(dborowitz): Handle non-commit or missing HEAD? |
| 106 | if (head.getType() == Constants.OBJ_COMMIT) { |
| 107 | walk.reset(); |
| 108 | walk.markStart((RevCommit) head); |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 109 | paginator = new Paginator(walk, limit, null); |
| Dave Borowitz | f6dcf7a | 2014-07-30 10:26:58 -0700 | [diff] [blame] | 110 | } |
| 111 | } |
| 112 | if (!data.containsKey("entries")) { |
| 113 | data.put("entries", ImmutableList.of()); |
| 114 | } |
| 115 | List<Map<String, Object>> branches = RefServlet.getBranchesSoyData(req, REF_LIMIT); |
| 116 | |
| 117 | data.put("cloneUrl", desc.cloneUrl); |
| 118 | data.put("mirroredFromUrl", Strings.nullToEmpty(desc.mirroredFromUrl)); |
| 119 | data.put("description", Strings.nullToEmpty(desc.description)); |
| 120 | data.put("branches", trim(branches)); |
| 121 | if (branches.size() > REF_LIMIT) { |
| 122 | data.put("moreBranchesUrl", GitilesView.refs().copyFrom(view).toUrl()); |
| 123 | } |
| 124 | data.put("tags", trim(tags)); |
| 125 | data.put("hasLog", paginator != null); |
| 126 | if (tags.size() > REF_LIMIT) { |
| 127 | data.put("moreTagsUrl", GitilesView.refs().copyFrom(view).toUrl()); |
| 128 | } |
| 129 | GitilesConfig.putVariant(getAccess(req).getConfig(), "logEntry", "logEntryVariant", data); |
| 130 | |
| 131 | if (paginator != null) { |
| 132 | DateFormatter df = new DateFormatter(access, Format.DEFAULT); |
| Dave Borowitz | e360d5c | 2015-09-16 16:53:30 -0400 | [diff] [blame] | 133 | try (OutputStream out = |
| 134 | startRenderStreamingHtml(req, res, "gitiles.repositoryIndex", data)) { |
| Shawn Pearce | 4c2eb85 | 2014-08-26 15:35:33 -0700 | [diff] [blame] | 135 | Writer w = newWriter(out, res); |
| Dave Borowitz | f6dcf7a | 2014-07-30 10:26:58 -0700 | [diff] [blame] | 136 | new LogSoyData(req, access, "oneline") |
| 137 | .renderStreaming(paginator, "HEAD", renderer, w, df); |
| Shawn Pearce | 4c2eb85 | 2014-08-26 15:35:33 -0700 | [diff] [blame] | 138 | w.flush(); |
| Dave Borowitz | f6dcf7a | 2014-07-30 10:26:58 -0700 | [diff] [blame] | 139 | } |
| 140 | } else { |
| 141 | renderHtml(req, res, "gitiles.repositoryIndex", data); |
| 142 | } |
| Dave Borowitz | f6dcf7a | 2014-07-30 10:26:58 -0700 | [diff] [blame] | 143 | } |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 144 | } |
| 145 | |
| Kevin Graney | 77dbea9 | 2014-07-08 14:07:40 -0400 | [diff] [blame] | 146 | @Override |
| 147 | protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException { |
| 148 | GitilesAccess access = getAccess(req); |
| 149 | RepositoryDescription desc = access.getRepositoryDescription(); |
| 150 | renderJson(req, res, desc, new TypeToken<RepositoryDescription>() {}.getType()); |
| 151 | } |
| 152 | |
| Dave Borowitz | 209d0aa | 2012-12-28 14:28:53 -0800 | [diff] [blame] | 153 | private static <T> List<T> trim(List<T> list) { |
| 154 | return list.size() > REF_LIMIT ? list.subList(0, REF_LIMIT) : list; |
| Dave Borowitz | 14ce828 | 2012-12-20 14:08:25 -0800 | [diff] [blame] | 155 | } |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 156 | |
| Han-Wen Nienhuys | c0200f6 | 2016-05-02 17:34:51 +0200 | [diff] [blame] | 157 | private static Map<String, Object> renderReadme( |
| Shawn Pearce | c68ad0b | 2016-05-28 16:52:47 -0700 | [diff] [blame] | 158 | HttpServletRequest req, RevWalk walk, GitilesView view, Config cfg, RevObject head) |
| 159 | throws IOException { |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 160 | RevTree rootTree; |
| 161 | try { |
| 162 | rootTree = walk.parseTree(head); |
| 163 | } catch (IncorrectObjectTypeException notTreeish) { |
| 164 | return null; |
| 165 | } |
| 166 | |
| Han-Wen Nienhuys | c0200f6 | 2016-05-02 17:34:51 +0200 | [diff] [blame] | 167 | ReadmeHelper readme = |
| 168 | new ReadmeHelper( |
| 169 | walk.getObjectReader(), |
| 170 | GitilesView.path().copyFrom(view).setRevision(Revision.HEAD).setPathPart("/").build(), |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 171 | MarkdownConfig.get(cfg), |
| Shawn Pearce | c68ad0b | 2016-05-28 16:52:47 -0700 | [diff] [blame] | 172 | rootTree, |
| 173 | req.getRequestURI()); |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 174 | readme.scanTree(rootTree); |
| 175 | if (readme.isPresent()) { |
| Shawn Pearce | 08e38f2 | 2016-05-24 16:40:18 -0700 | [diff] [blame] | 176 | SanitizedContent html = readme.render(); |
| 177 | if (html != null) { |
| 178 | return ImmutableMap.<String, Object>of("readmeHtml", html); |
| 179 | } |
| Shawn Pearce | 45e8375 | 2015-02-20 17:59:05 -0800 | [diff] [blame] | 180 | } |
| 181 | return null; |
| 182 | } |
| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 183 | } |