1<%-- 2$Id$ 3 4CDDL HEADER START 5 6The contents of this file are subject to the terms of the 7Common Development and Distribution License (the "License"). 8You may not use this file except in compliance with the License. 9 10See LICENSE.txt included in this distribution for the specific 11language governing permissions and limitations under the License. 12 13When distributing Covered Code, include this CDDL HEADER in each 14file and include the License file at LICENSE.txt. 15If applicable, add the following below this CDDL HEADER, with the 16fields enclosed by brackets "[]" replaced with your own identifying 17information: Portions Copyright [yyyy] [name of copyright owner] 18 19CDDL HEADER END 20 21Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. 22Portions Copyright 2011 Jens Elkner. 23Portions Copyright (c) 2018-2020, Chris Fraire <cfraire@me.com>. 24--%> 25<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 26<%@page errorPage="error.jsp" import=" 27java.io.IOException, 28java.io.File, 29java.text.Format, 30java.text.SimpleDateFormat, 31java.util.Date, 32java.util.logging.Level, 33java.util.logging.Logger, 34java.util.Objects, 35java.util.Set, 36java.util.regex.Pattern, 37 38org.opengrok.indexer.configuration.RuntimeEnvironment, 39org.opengrok.indexer.history.History, 40org.opengrok.indexer.history.HistoryEntry, 41org.opengrok.indexer.history.HistoryGuru, 42org.opengrok.indexer.logger.LoggerFactory, 43org.opengrok.indexer.util.ForbiddenSymlinkException, 44org.opengrok.indexer.web.QueryParameters, 45org.opengrok.indexer.web.SearchHelper, 46org.opengrok.indexer.web.Util" 47%> 48<%@ page import="jakarta.servlet.http.HttpServletResponse" %> 49<%@ page import="org.opengrok.indexer.web.SortOrder" %> 50<%/* ---------------------- history.jsp start --------------------- */ 51{ 52 final Logger LOGGER = LoggerFactory.getLogger(getClass()); 53 54 PageConfig cfg = PageConfig.get(request); 55 cfg.checkSourceRootExistence(); 56 57 // Need to set the title before including httpheader.jspf 58 cfg.setTitle(cfg.getHistoryTitle()); 59 60 String path = cfg.getPath(); 61 62 if (path.length() > 0) { 63 String primePath = path; 64 Project project = cfg.getProject(); 65 if (project != null) { 66 SearchHelper searchHelper = cfg.prepareInternalSearch(SortOrder.RELEVANCY); 67 /* 68 * N.b. searchHelper.destroy() is called via 69 * WebappListener.requestDestroyed() on presence of the following 70 * REQUEST_ATTR. 71 */ 72 request.setAttribute(SearchHelper.REQUEST_ATTR, searchHelper); 73 searchHelper.prepareExec(project); 74 75 try { 76 primePath = searchHelper.getPrimeRelativePath(project.getName(), path); 77 } catch (IOException | ForbiddenSymlinkException ex) { 78 LOGGER.log(Level.WARNING, String.format( 79 "Error getting prime relative for %s", path), ex); 80 } 81 } 82 83 File file = cfg.getResourceFile(primePath); 84 History hist; 85 try { 86 hist = HistoryGuru.getInstance().getHistoryUI(file); 87 } catch (Exception e) { 88 // should not happen 89 response.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage()); 90 return; 91 } 92 93 if (hist == null) { 94 /* 95 * The history is not available even for a renamed file. 96 * Send 404 Not Found. 97 */ 98 response.sendError(HttpServletResponse.SC_NOT_FOUND); 99 return; 100 } 101 request.setAttribute("history.jsp-hist", hist); 102 } 103} 104%> 105<%@ 106 107include file="httpheader.jspf" 108 109%> 110<% 111{ 112 if ((request.getAttribute("history.jsp-hist")) != null) { 113%> 114<body> 115<script type="text/javascript">/* <![CDATA[ */ 116 document.rev = function() { return getParameter("<%= QueryParameters.REVISION_PARAM %>"); }; 117 document.annotate = <%= PageConfig.get(request).annotate() %>; 118 document.domReady.push(function() { domReadyMast(); }); 119 document.pageReady.push(function() { pageReadyMast(); }); 120/* ]]> */</script> 121<div id="page"> 122 <header id="whole_header"> 123<% 124 } 125} 126{ 127 if (request.getAttribute("history.jsp-hist") != null) { 128%> 129 <%@include file="pageheader.jspf" %> 130<% 131 } 132} 133{ 134 PageConfig cfg = PageConfig.get(request); 135 String context = request.getContextPath(); 136 String path = cfg.getPath(); 137 138 History hist; 139 if ((hist = (History) request.getAttribute("history.jsp-hist")) != null) { 140 141 int start = cfg.getSearchStart(); 142 int max = cfg.getSearchMaxItems(); 143 long totalHits = hist.getHistoryEntries().size(); 144 long thispage = Math.min(totalHits - start, max); 145 146 // We have a lots of results to show: create a slider for them 147 request.setAttribute("history.jsp-slider", Util.createSlider(start, max, totalHits, request)); 148%> 149 <div id="Masthead">History log of 150 <%= Util.breadcrumbPath(context + Prefix.XREF_P, path,'/',"",true,cfg.isDir()) %> 151 (Results <span class="bold"> <%= totalHits != 0 ? start + 1 : 0 %> – <%= thispage + start 152 %></span> of <span class="bold"><%= totalHits %></span>) 153 </div> 154<% 155 } 156} 157{ 158 if (request.getAttribute("history.jsp-hist") != null) { 159%> 160 <%@ 161 162include file="minisearch.jspf" 163 164%> 165<% 166 } 167} 168{ 169 PageConfig cfg = PageConfig.get(request); 170 String context = request.getContextPath(); 171 String path = cfg.getPath(); 172 History hist; 173 if ((hist = (History) request.getAttribute("history.jsp-hist")) != null) { 174 RuntimeEnvironment env = cfg.getEnv(); 175 String uriEncodedName = cfg.getUriEncodedPath(); 176 Project project = cfg.getProject(); 177 178 boolean striked = false; 179 String userPage = env.getUserPage(); 180 String userPageSuffix = env.getUserPageSuffix(); 181 String bugPage = project != null ? project.getBugPage() : env.getBugPage(); 182 String bugRegex = project != null ? project.getBugPattern() : env.getBugPattern(); 183 Pattern bugPattern = null; 184 if (bugRegex != null) { 185 bugPattern = Pattern.compile(bugRegex); 186 } 187 String reviewPage = project != null ? project.getReviewPage() : env.getReviewPage(); 188 String reviewRegex = project != null ? project.getReviewPattern() : env.getReviewPattern(); 189 Pattern reviewPattern = null; 190 if (reviewRegex != null) { 191 reviewPattern = Pattern.compile(reviewRegex); 192 } 193 194 Format df = new SimpleDateFormat("dd-MMM-yyyy"); 195 196 int revision2 = Math.max(cfg.getIntParam(QueryParameters.REVISION_2_PARAM, -1), 0); 197 int revision1 = cfg.getIntParam(QueryParameters.REVISION_1_PARAM, -1) < revision2 ? 198 revision2 + 1 : cfg.getIntParam(QueryParameters.REVISION_1_PARAM, -1); 199 revision2 = revision2 >= hist.getHistoryEntries().size() ? hist.getHistoryEntries().size() - 1 : revision2; 200 201 int start = cfg.getSearchStart(); 202 int max = cfg.getSearchMaxItems(); 203%> 204<script type="text/javascript">/* <![CDATA[ */ 205document.domReady.push(function() {domReadyHistory();}); 206/* ]]> */</script> 207<!--[if IE]> 208<style type="text/css"> 209 table#revisions tbody tr td p { 210 word-break: break-all; 211 } 212</style> 213<![endif]--> 214<form action="<%= context + Prefix.DIFF_P + uriEncodedName %>"> 215<table class="src" id="revisions" aria-label="table of revisions"> 216 <thead> 217 <tr> 218 <th>Revision <% 219 if (hist.hasTags()) { 220 %><a href="#" onclick="toggle_revtags(); return false;"> 221 <span class="revtags-hidden"> 222 (<<< Hide revision tags)</span> 223 <span class="revtags"> 224 (Show revision tags >>>)</span></a><% 225 } 226 %></th><% 227 if (!cfg.isDir()) { 228 %> 229 <th><input type="submit" value=" Compare "/> 230 <% if (hist.getHistoryEntries().size() > revision1 && revision1 >= 0) { %> 231 <input type="hidden" id="input_r1" name="<%= QueryParameters.REVISION_1_PARAM %>" 232 value="<%= path + '@' + hist.getHistoryEntries().get(revision1).getRevision() %>"/> 233 <% } %> 234 <% if (hist.getHistoryEntries().size() > revision2 && revision2 >= 0) { %> 235 <input type="hidden" id="input_r2" name="<%= QueryParameters.REVISION_2_PARAM %>" 236 value="<%= path + '@' + hist.getHistoryEntries().get(revision2).getRevision() %>"/> 237 <% } %> 238 </th><% 239 } 240 %> 241 <th>Date</th> 242 <th>Author</th> 243 <th>Comments <% 244 if (hist.hasFileList()) { 245 %><a href="#" onclick="toggle_filelist(); return false;"> 246 <div class="filelist-hidden"> 247 (<<< Hide modified files)</div> 248 <div class="filelist"> 249 (Show modified files >>>)</div></a><% 250 } 251 %> 252 </th> 253 </tr> 254 </thead> 255 <tbody> 256 <% 257 int count=0; 258 for (HistoryEntry entry : hist.getHistoryEntries(max, start)) { 259 String rev = entry.getRevision(); 260 if (rev == null || rev.length() == 0) { 261 rev = ""; 262 } 263 String tags = hist.getTags().get(rev); 264 265 if (tags != null) { 266 int colspan; 267 if (cfg.isDir()) 268 colspan = 4; 269 else 270 colspan = 5; 271 %> 272 <tr class="revtags-hidden"> 273 <td colspan="<%= colspan %>" class="revtags"> 274 <span class="bold">Revision tags:</span> <%= tags %> 275 </td> 276 </tr><tr style="display: none;"></tr><% 277 } 278 %> 279 <tr><% 280 if (cfg.isDir()) { 281 %> 282 <td><%= rev %></td><% 283 } else { 284 if (entry.isActive()) { 285 StringBuffer urlBuffer = request.getRequestURL(); 286 if (request.getQueryString() != null) { 287 urlBuffer.append('?').append(request.getQueryString()); 288 } 289 urlBuffer.append('#').append(rev); 290 %> 291 <td><a href="<%= urlBuffer %>" 292 title="link to revision line">#</a> 293 <a href="<%= context + Prefix.XREF_P + uriEncodedName + "?" + 294 QueryParameters.REVISION_PARAM_EQ + Util.uriEncode(rev) %>"><%= rev %> 295 </a></td> 296 <td><% 297 %><input type="radio" 298 aria-label="From" 299 data-revision-1="<%= (start + count) %>" 300 data-revision-2="<%= revision2 %>" 301 data-diff-revision="<%= QueryParameters.REVISION_1_PARAM %>" 302 data-revision-path="<%= path + '@' + hist.getHistoryEntries().get(start + count).getRevision()%>" 303 <% 304 if (count + start > revision1 || (count + start > revision2 && count + start <= revision1 - 1)) { 305 // revision1 enabled 306 } else if (count + start == revision1 ) { 307 // revision1 selected 308 %> checked="checked"<% 309 } else if( count + start <= revision2 ) { 310 // revision1 disabled 311 %> disabled="disabled" <% 312 } 313 %>/><% 314 315 %><input type="radio" 316 aria-label="To" 317 data-revision-1="<%= revision1 %>" 318 data-revision-2="<%= (start + count) %>" 319 data-diff-revision="<%= QueryParameters.REVISION_2_PARAM %>" 320 data-revision-path="<%= path + '@' + hist.getHistoryEntries().get(start + count).getRevision() %>" 321 <% 322 if( count + start < revision2 || (count + start > revision2 && count + start <= revision1 - 1) ) { 323 // revision2 enabled 324 } else if( count + start == revision2 ) { 325 // revision2 selected 326 %> checked="checked" <% 327 } else if (count + start >= revision1 ) { 328 // revision2 disabled 329 %> disabled="disabled" <% 330 } 331 %>/><% 332 %></td><% 333 } else { 334 striked = true; 335 %> 336 <td><del><%= rev %></del></td> 337 <td></td><% 338 } 339 } 340 %> 341 <td><% 342 Date date = entry.getDate(); 343 if (date != null) { 344 %><%= df.format(date) %><% 345 } 346 %></td> 347 <td><% 348 String author = entry.getAuthor(); 349 if (author == null) { 350 %>(no author)<% 351 } else if (userPage != null && userPage.length() > 0) { 352 String alink = Util.getEmail(author); 353 %><a href="<%= userPage + Util.htmlize(alink) + userPageSuffix 354 %>"><%= Util.htmlize(author)%></a><% 355 } else { 356 %><%= Util.htmlize(author) %><% 357 } 358 %></td> 359 <td><a name="<%= rev %>"></a><% 360 // revision message collapse threshold minimum of 10 361 int summaryLength = Math.max(10, cfg.getRevisionMessageCollapseThreshold()); 362 String cout = Util.htmlize(entry.getMessage()); 363 364 if (bugPage != null && bugPage.length() > 0 && bugPattern != null) { 365 cout = Util.linkifyPattern(cout, bugPattern, "$1", Util.completeUrl(bugPage + "$1", request)); 366 } 367 if (reviewPage != null && reviewPage.length() > 0 && reviewPattern != null) { 368 cout = Util.linkifyPattern(cout, reviewPattern, "$1", Util.completeUrl(reviewPage + "$1", request)); 369 } 370 371 boolean showSummary = false; 372 String coutSummary = entry.getMessage(); 373 if (coutSummary.length() > summaryLength) { 374 showSummary = true; 375 coutSummary = coutSummary.substring(0, summaryLength - 1); 376 coutSummary = Util.htmlize(coutSummary); 377 if (bugPage != null && bugPage.length() > 0) { 378 coutSummary = Util.linkifyPattern(coutSummary, bugPattern, "$1", Util.completeUrl(bugPage + "$1", request)); 379 } 380 if (reviewPage != null && reviewPage.length() > 0) { 381 coutSummary = Util.linkifyPattern(coutSummary, reviewPattern, "$1", Util.completeUrl(reviewPage + "$1", request)); 382 } 383 } 384 385 if (showSummary) { 386 %> 387 <p class="rev-message-summary"><%= coutSummary %></p> 388 <p class="rev-message-full rev-message-hidden"><%= cout %></p> 389 <p class="rev-message-toggle" data-toggle-state="less"><a class="rev-toggle-a" href="#">show more ... </a></p> 390 <% 391 } 392 else { 393 %><p class="rev-message-full"><%= cout %></p><% 394 } 395 396 Set<String> files = entry.getFiles(); 397 if (files != null) { 398 %><div class="filelist-hidden"><br/><% 399 for (String ifile : files) { 400 String jfile = Util.stripPathPrefix(path, ifile); 401 if (Objects.equals(rev, "")) { 402 %> 403<a class="h" href="<%= context + Prefix.XREF_P + ifile %>"><%= jfile %></a><br/><% 404 } else { 405 %> 406<a class="h" href="<%= context + Prefix.XREF_P + ifile %>?<%= QueryParameters.REVISION_PARAM_EQ %> 407<%= rev %>"><%= jfile %></a><br/><% 408 } 409 } 410 %></div><% 411 } 412 %></td> 413 </tr><% 414 count++; 415 } 416 %> 417 </tbody> 418 <tfoot> 419 <tr> 420 <td colspan="5"> 421<% 422 String slider; 423 if ((slider = (String) request.getAttribute("history.jsp-slider")) != null) { 424 // NOTE: shouldn't happen that it doesn't have this attribute 425 %><p class="slider"><%= slider %></p><% 426 } 427%> 428 </td> 429 </tr> 430 </tfoot> 431</table> 432 433</form><% 434 if (striked) { 435%><p><span class="bold">Note:</span> No associated file changes are available for 436revisions with strike-through numbers (eg. <del>1.45</del>)</p><% 437 } 438%> 439<p class="rssbadge"><a href="<%=context + Prefix.RSS_P + uriEncodedName 440%>" title="RSS XML Feed of latest changes"><span id="rssi"></span></a></p> 441 442<% 443 444 } 445} 446/* ---------------------- history.jsp end --------------------- */ 447%><%@ 448 449include file="foot.jspf" 450 451%> 452