xref: /OpenGrok/opengrok-web/src/main/webapp/list.jsp (revision d6df19e1b22784c78f567cf74c42f18e3901b900)
1<%--
2CDDL HEADER START
3
4The contents of this file are subject to the terms of the
5Common Development and Distribution License (the "License").
6You may not use this file except in compliance with the License.
7
8See LICENSE.txt included in this distribution for the specific
9language governing permissions and limitations under the License.
10
11When distributing Covered Code, include this CDDL HEADER in each
12file and include the License file at LICENSE.txt.
13If applicable, add the following below this CDDL HEADER, with the
14fields enclosed by brackets "[]" replaced with your own identifying
15information: Portions Copyright [yyyy] [name of copyright owner]
16
17CDDL HEADER END
18
19Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
20Portions Copyright 2011 Jens Elkner.
21Portions Copyright (c) 2017-2020, Chris Fraire <cfraire@me.com>.
22
23--%>
24<%@page errorPage="error.jsp" import="
25java.io.BufferedInputStream,
26java.io.File,
27java.io.FileInputStream,
28java.io.InputStreamReader,
29java.io.IOException,
30java.io.Reader,
31java.net.URLEncoder,
32java.nio.charset.StandardCharsets,
33java.util.List,
34java.util.Locale,
35java.util.logging.Level,
36java.util.logging.Logger,
37java.util.Set,
38java.util.TreeSet,
39org.opengrok.indexer.analysis.AnalyzerGuru,
40org.opengrok.indexer.analysis.Definitions,
41org.opengrok.indexer.analysis.AbstractAnalyzer,
42org.opengrok.indexer.analysis.AnalyzerFactory,
43org.opengrok.indexer.analysis.NullableNumLinesLOC,
44org.opengrok.indexer.history.Annotation,
45org.opengrok.indexer.index.IndexDatabase,
46org.opengrok.indexer.logger.LoggerFactory,
47org.opengrok.indexer.search.DirectoryEntry,
48org.opengrok.indexer.search.DirectoryExtraReader,
49org.opengrok.indexer.util.FileExtraZipper,
50org.opengrok.indexer.util.ForbiddenSymlinkException,
51org.opengrok.indexer.util.IOUtils,
52org.opengrok.web.DirectoryListing,
53org.opengrok.indexer.web.SearchHelper"
54%>
55<%@ page import="static org.opengrok.web.PageConfig.DUMMY_REVISION" %>
56<%@ page import="org.opengrok.indexer.web.SortOrder" %>
57<%@ page import="jakarta.servlet.http.Cookie" %>
58<%
59{
60    // need to set it here since requesting parameters
61    if (request.getCharacterEncoding() == null) {
62        request.setCharacterEncoding("UTF-8");
63    }
64
65    PageConfig cfg = PageConfig.get(request);
66    cfg.checkSourceRootExistence();
67
68    String rev = cfg.getRequestedRevision();
69    if (!cfg.isDir() && rev.length() == 0) {
70        /*
71         * Get the latest revision and redirect so that the revision number
72         * appears in the URL.
73         */
74        String latestRevision = cfg.getLatestRevision();
75        if (latestRevision != null) {
76            cfg.evaluateMatchOffset();
77            String location = cfg.getRevisionLocation(latestRevision);
78            response.sendRedirect(location);
79            return;
80        }
81        if (!cfg.getEnv().isGenerateHtml()) {
82            cfg.evaluateMatchOffset();
83            /*
84             * Economy mode is on and failed to get the last revision
85             * (presumably running with history turned off).  Use dummy
86             * revision string so that xref can be generated from the resource
87             * file directly.
88             */
89            String location = cfg.getRevisionLocation(DUMMY_REVISION);
90            response.sendRedirect(location);
91            return;
92        }
93
94        if (cfg.evaluateMatchOffset()) {
95            /*
96             * If after calling, a match offset has been translated to a
97             * fragment identifier (specifying a line#), then redirect to self.
98             * This block will not be activated again the second time.
99             */
100            String location = cfg.getRevisionLocation(""); // empty
101            response.sendRedirect(location);
102            return;
103        }
104    }
105
106    Annotation annotation = cfg.getAnnotation();
107    if (annotation != null) {
108        int r = annotation.getWidestRevision();
109        int a = annotation.getWidestAuthor();
110        cfg.addHeaderData("<style type=\"text/css\">"
111            + ".blame .r { width: " + (r == 0 ? 6 : Math.ceil(r * 0.7)) + "em; } "
112            + ".blame .a { width: " + (a == 0 ? 6 : Math.ceil(a * 0.7)) + "em; } "
113            + "</style>");
114    }
115}
116%><%@include
117
118file="mast.jsp"
119
120%><script type="text/javascript">/* <![CDATA[ */
121document.pageReady.push(function() { pageReadyList();});
122/* ]]> */</script>
123<%
124/* ---------------------- list.jsp start --------------------- */
125{
126    final Logger LOGGER = LoggerFactory.getLogger(getClass());
127
128    PageConfig cfg = PageConfig.get(request);
129    String rev = cfg.getRequestedRevision();
130    Project project = cfg.getProject();
131
132    String navigateWindowEnabled = project != null ? Boolean.toString(
133            project.isNavigateWindowEnabled()) : "false";
134    File resourceFile = cfg.getResourceFile();
135    String path = cfg.getPath();
136    String basename = resourceFile.getName();
137    String rawPath = request.getContextPath() + Prefix.DOWNLOAD_P + path;
138    Reader r = null;
139    if (cfg.isDir()) {
140        // valid resource is requested
141        // mast.jsp assures, that resourceFile is valid and not /
142        // see cfg.resourceNotAvailable()
143        String cookieValue = cfg.getRequestedProjectsAsString();
144        String projectName = null;
145        if (project != null) {
146            projectName = project.getName();
147            Set<String>  projects = cfg.getRequestedProjects();
148            if (!projects.contains(projectName)) {
149                projects.add(projectName);
150                // update cookie
151                cookieValue = cookieValue.length() == 0 ? projectName :
152                        projectName + ',' + cookieValue;
153                Cookie cookie = new Cookie(PageConfig.OPEN_GROK_PROJECT, URLEncoder.encode(cookieValue, StandardCharsets.UTF_8));
154                // TODO hmmm, projects.jspf doesn't set a path
155                cookie.setPath(request.getContextPath() + '/');
156                response.addCookie(cookie);
157            }
158        }
159        // requesting a directory listing
160        DirectoryListing dl = new DirectoryListing(cfg.getEftarReader());
161        List<String> files = cfg.getResourceFileList();
162        if (!files.isEmpty()) {
163            List<NullableNumLinesLOC> extras = null;
164            SearchHelper searchHelper = cfg.prepareInternalSearch(SortOrder.RELEVANCY);
165            /*
166             * N.b. searchHelper.destroy() is called via
167             * WebappListener.requestDestroyed() on presence of the following
168             * REQUEST_ATTR.
169             */
170            request.setAttribute(SearchHelper.REQUEST_ATTR, searchHelper);
171            if (project != null) {
172                searchHelper.prepareExec(project);
173            } else {
174                //noinspection Convert2Diamond
175                searchHelper.prepareExec(new TreeSet<String>());
176            }
177
178            if (searchHelper.getSearcher() != null) {
179                DirectoryExtraReader extraReader = new DirectoryExtraReader();
180                String primePath = path;
181                try {
182                    primePath = searchHelper.getPrimeRelativePath(projectName, path);
183                } catch (IOException | ForbiddenSymlinkException ex) {
184                    LOGGER.log(Level.WARNING, String.format(
185                            "Error getting prime relative for %s", path), ex);
186                }
187                extras = extraReader.search(searchHelper.getSearcher(), primePath);
188            }
189
190            FileExtraZipper zipper = new FileExtraZipper();
191            List<DirectoryEntry> entries = zipper.zip(resourceFile, files, extras);
192
193            List<String> readMes = dl.extraListTo(Util.uriEncodePath(request.getContextPath()),
194                    resourceFile, out, path, entries);
195            File[] catfiles = cfg.findDataFiles(readMes);
196            for (int i=0; i < catfiles.length; i++) {
197                if (catfiles[i] == null) {
198                    continue;
199                }
200%>
201<%
202    String lcName = readMes.get(i).toLowerCase(Locale.ROOT);
203    if (lcName.endsWith(".md") || lcName.endsWith(".markdown")) {
204    %><div id="src<%=i%>" data-markdown>
205        <div class="markdown-heading">
206            <h3><%= readMes.get(i) %></h3>
207        </div>
208        <div class="markdown-content"
209             data-markdown-download="<%= request.getContextPath() + Prefix.DOWNLOAD_P + Util.uriEncodePath(cfg.getPath() + readMes.get(i)) %>">
210        </div>
211        <pre data-markdown-original><%
212            Util.dump(out, catfiles[i], catfiles[i].getName().endsWith(".gz"));
213        %></pre>
214    </div>
215<% } else { %>
216    <h3><%= readMes.get(i) %></h3>
217    <div id="src<%=i%>">
218        <pre><%
219            Util.dump(out, catfiles[i], catfiles[i].getName().endsWith(".gz"));
220        %></pre>
221    </div>
222<%
223    }
224
225            }
226        }
227    } else if (rev.length() != 0) {
228        // requesting a revision
229        File xrefFile;
230        if (cfg.isLatestRevision(rev) && (xrefFile = cfg.findDataFile()) != null) {
231            if (cfg.annotate()) {
232                // annotate
233                BufferedInputStream bin = new BufferedInputStream(new FileInputStream(resourceFile));
234                try {
235                    AnalyzerFactory a = AnalyzerGuru.find(basename);
236                    AbstractAnalyzer.Genre g = AnalyzerGuru.getGenre(a);
237                    if (g == null) {
238                        a = AnalyzerGuru.find(bin);
239                        g = AnalyzerGuru.getGenre(a);
240                    }
241                    if (g == AbstractAnalyzer.Genre.IMAGE) {
242%>
243<div id="src">
244    <img src="<%= rawPath %>" alt="Image from Source Repository"/>
245</div><%
246                    } else if ( g == AbstractAnalyzer.Genre.HTML) {
247                        /*
248                         * For backward compatibility, read the OpenGrok-produced
249                         * document using the system default charset.
250                         */
251                        r = new InputStreamReader(bin);
252                        // dumpXref() is also useful here for translating links.
253                        Util.dumpXref(out, r, request.getContextPath());
254                    } else if (g == AbstractAnalyzer.Genre.PLAIN) {
255%>
256<div id="src" data-navigate-window-enabled="<%= navigateWindowEnabled %>">
257    <pre><%
258                        // We're generating xref for the latest revision, so we can
259                        // find the definitions in the index.
260                        Definitions defs = IndexDatabase.getDefinitions(resourceFile);
261                        Annotation annotation = cfg.getAnnotation();
262                        // Data under source root is read with UTF-8 as a default.
263                        r = IOUtils.createBOMStrippedReader(bin,
264                            StandardCharsets.UTF_8.name());
265                        AnalyzerGuru.writeDumpedXref(request.getContextPath(), a,
266                                r, out, defs, annotation, project);
267    %></pre>
268</div><%
269                    } else {
270%>
271Click <a href="<%= rawPath %>">download <%= basename %></a><%
272                    }
273                } finally {
274                    if (r != null) {
275                        IOUtils.close(r);
276                        bin = null;
277                    }
278                    if (bin != null) {
279                        IOUtils.close(bin);
280                        bin = null;
281                    }
282                }
283
284            } else {
285%>
286<div id="src" data-navigate-window-enabled="<%= navigateWindowEnabled %>">
287    <pre><%
288                    boolean compressed = xrefFile.getName().endsWith(".gz");
289                    Util.dumpXref(out, xrefFile, compressed,
290                            request.getContextPath());
291    %></pre>
292</div>
293<%
294            }
295        } else {
296%>
297<%@
298
299include file="xref.jspf"
300
301%>
302<%
303        }
304    } else {
305        // Requesting cross referenced file with no known revision.
306        File xrefFile = cfg.findDataFile();
307        if (xrefFile != null) {
308%>
309<div id="src" data-navigate-window-enabled="<%= navigateWindowEnabled %>">
310    <pre><%
311            boolean compressed = xrefFile.getName().endsWith(".gz");
312            Util.dumpXref(out, xrefFile, compressed, request.getContextPath());
313    %></pre>
314</div>
315<%
316        } else {
317            // Failed to get xref, generate on the fly.
318%>
319<%@
320
321include file="xref.jspf"
322
323%>
324<%
325        }
326    }
327}
328/* ---------------------- list.jsp end --------------------- */
329%><%@
330
331include file="foot.jspf"
332
333%>
334