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