mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 10:17:41 +00:00
cleanup SaveSnapshotAsReport and templates
This commit is contained in:
parent
7f68b8bf50
commit
a0df66f37e
@ -22,8 +22,6 @@ import com.github.mustachejava.DefaultMustacheFactory;
|
|||||||
import com.github.mustachejava.Mustache;
|
import com.github.mustachejava.Mustache;
|
||||||
import com.github.mustachejava.MustacheFactory;
|
import com.github.mustachejava.MustacheFactory;
|
||||||
import java.awt.Desktop;
|
import java.awt.Desktop;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
@ -33,15 +31,16 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.embed.swing.SwingFXUtils;
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.ButtonBar;
|
import javafx.scene.control.ButtonBar;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.TextInputDialog;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
@ -70,8 +69,6 @@ public class SaveSnapshotAsReport extends Action {
|
|||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(SaveSnapshotAsReport.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(SaveSnapshotAsReport.class.getName());
|
||||||
private static final Image SNAP_SHOT = new Image("org/sleuthkit/autopsy/timeline/images/image.png", 16, 16, true, true); //
|
private static final Image SNAP_SHOT = new Image("org/sleuthkit/autopsy/timeline/images/image.png", 16, 16, true, true); //
|
||||||
private static final String HTML_EXT = ".html"; //
|
|
||||||
private static final String REPORT_IMAGE_EXTENSION = ".png"; //
|
|
||||||
private static final ButtonType OPEN = new ButtonType(Bundle.OpenReportAction_DisplayName(), ButtonBar.ButtonData.NO);
|
private static final ButtonType OPEN = new ButtonType(Bundle.OpenReportAction_DisplayName(), ButtonBar.ButtonData.NO);
|
||||||
private static final ButtonType OK = new ButtonType(ButtonType.OK.getText(), ButtonBar.ButtonData.CANCEL_CLOSE);
|
private static final ButtonType OK = new ButtonType(ButtonType.OK.getText(), ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||||
|
|
||||||
@ -79,7 +76,6 @@ public class SaveSnapshotAsReport extends Action {
|
|||||||
|
|
||||||
private final TimeLineController controller;
|
private final TimeLineController controller;
|
||||||
private final Case currentCase;
|
private final Case currentCase;
|
||||||
private final ReportBranding reportBranding;
|
|
||||||
|
|
||||||
@NbBundle.Messages({"SaveSnapshot.action.name.text=Snapshot Report",
|
@NbBundle.Messages({"SaveSnapshot.action.name.text=Snapshot Report",
|
||||||
"SaveSnapshot.action.longText=Save a screen capture of the visualization as a report.",
|
"SaveSnapshot.action.longText=Save a screen capture of the visualization as a report.",
|
||||||
@ -99,58 +95,39 @@ public class SaveSnapshotAsReport extends Action {
|
|||||||
|
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
this.currentCase = controller.getAutopsyCase();
|
this.currentCase = controller.getAutopsyCase();
|
||||||
this.reportBranding = new ReportBranding();
|
|
||||||
|
|
||||||
setEventHandler(actionEvent -> {
|
setEventHandler(actionEvent -> {
|
||||||
String escapedLocalDateTime = FileUtil.escapeFileName(LocalDateTime.now().toString());
|
ReportBranding reportBranding = new ReportBranding();
|
||||||
String reportName = Bundle.SaveSnapsHotAsReport_ReportName(escapedLocalDateTime);
|
Date generationDate = new Date();
|
||||||
Path reportPath = Paths.get(Case.getCurrentCase().getReportDirectory(), reportName).toAbsolutePath();
|
|
||||||
File reportHTMLFIle = reportPath.resolve(reportName + HTML_EXT).toFile();
|
TextInputDialog textInputDialog = new TextInputDialog();
|
||||||
|
|
||||||
|
textInputDialog.setTitle("Timeline");
|
||||||
|
textInputDialog.setHeaderText("Enter a report name.");
|
||||||
|
Optional<String> showAndWait = textInputDialog.showAndWait();
|
||||||
|
|
||||||
|
String reportName = showAndWait.orElseGet(() -> FileUtil.escapeFileName(currentCase.getName() + " " + new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss").format(generationDate)));
|
||||||
|
Path reportFolderPath = Paths.get(currentCase.getReportDirectory(), reportName, "Timeline Snapshot");
|
||||||
|
Path reportIndexFilePath = reportFolderPath.resolve("index.html");
|
||||||
|
|
||||||
ZoomParams zoomParams = controller.getEventsModel().zoomParametersProperty().get();
|
ZoomParams zoomParams = controller.getEventsModel().zoomParametersProperty().get();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(reportPath);
|
//ensure directory exists and write html files
|
||||||
|
Files.createDirectories(reportFolderPath);
|
||||||
writeSummary(reportPath);
|
writeSummary(reportFolderPath, generationDate, reportBranding);
|
||||||
//ensure directory exists and write html file
|
writeIndexHTML(reportFolderPath);
|
||||||
|
writeSnapShotHTMLFile(reportFolderPath, reportName, zoomParams);
|
||||||
try (Writer htmlWriter = new FileWriter(reportHTMLFIle)) {
|
|
||||||
writeSnapShotHTMLFile(reportPath, reportName, zoomParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
//pull generator and agency logo from branding, and the remaining resources from the core jar
|
|
||||||
String generatorLogoPath = reportBranding.getGeneratorLogoPath();
|
|
||||||
if (StringUtils.isNotBlank(generatorLogoPath)) {
|
|
||||||
Files.copy(Paths.get(generatorLogoPath), Files.newOutputStream(reportPath.resolve("generator_logo.png")));
|
|
||||||
}
|
|
||||||
|
|
||||||
String agencyLogoPath = reportBranding.getAgencyLogoPath();
|
|
||||||
if (StringUtils.isNotBlank(agencyLogoPath)) {
|
|
||||||
Files.copy(Paths.get(agencyLogoPath), Files.newOutputStream(reportPath.resolve("agency_logo.png")));
|
|
||||||
}
|
|
||||||
|
|
||||||
//copy favicon
|
|
||||||
try (InputStream faviconStream = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico")) {
|
|
||||||
Files.copy(faviconStream, reportPath.resolve("favicon.ico"));
|
|
||||||
}
|
|
||||||
|
|
||||||
//take snapshot and save in report directory
|
//take snapshot and save in report directory
|
||||||
ImageIO.write(SwingFXUtils.fromFXImage(node.snapshot(null, null), null), "png", //
|
ImageIO.write(SwingFXUtils.fromFXImage(node.snapshot(null, null), null), "png",
|
||||||
reportPath.resolve(reportName + REPORT_IMAGE_EXTENSION).toFile()); //
|
reportFolderPath.resolve("snapshot.png").toFile());
|
||||||
|
|
||||||
//copy report css
|
copyResources(reportFolderPath, reportBranding);
|
||||||
try (InputStream resource = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/index.css")) { //
|
|
||||||
Files.copy(resource, reportPath.resolve("index.css")); //
|
|
||||||
}
|
|
||||||
//copy report css
|
|
||||||
try (InputStream resource = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/summary.css")) { //
|
|
||||||
Files.copy(resource, reportPath.resolve("summary.css")); //
|
|
||||||
}
|
|
||||||
|
|
||||||
//add html file as report to case
|
//add html file as report to case
|
||||||
try {
|
try {
|
||||||
Case.getCurrentCase().addReport(reportHTMLFIle.getPath(), Bundle.Timeline_ModuleName(), reportName + HTML_EXT); //
|
Case.getCurrentCase().addReport(reportIndexFilePath.toString(), Bundle.Timeline_ModuleName(), reportName); //
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.WARNING, "failed to add html wrapper as a report", ex); //
|
LOGGER.log(Level.WARNING, "failed to add html wrapper as a report", ex); //
|
||||||
new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_FailedToAddReport()).showAndWait();
|
new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_FailedToAddReport()).showAndWait();
|
||||||
@ -165,8 +142,8 @@ public class SaveSnapshotAsReport extends Action {
|
|||||||
alert.initModality(Modality.APPLICATION_MODAL);
|
alert.initModality(Modality.APPLICATION_MODAL);
|
||||||
|
|
||||||
//make action to open report, and hyperlinklable to invoke action
|
//make action to open report, and hyperlinklable to invoke action
|
||||||
final OpenReportAction openReportAction = new OpenReportAction(reportHTMLFIle);
|
final OpenReportAction openReportAction = new OpenReportAction(reportIndexFilePath);
|
||||||
HyperlinkLabel hyperlinkLabel = new HyperlinkLabel(Bundle.SaveSnapShotAsReport_ReportSavedAt(reportHTMLFIle.getPath()));
|
HyperlinkLabel hyperlinkLabel = new HyperlinkLabel(Bundle.SaveSnapShotAsReport_ReportSavedAt(reportIndexFilePath.toString()));
|
||||||
hyperlinkLabel.setOnAction(openReportAction);
|
hyperlinkLabel.setOnAction(openReportAction);
|
||||||
alert.getDialogPane().setContent(hyperlinkLabel);
|
alert.getDialogPane().setContent(hyperlinkLabel);
|
||||||
|
|
||||||
@ -175,47 +152,47 @@ public class SaveSnapshotAsReport extends Action {
|
|||||||
.ifPresent(buttonType -> openReportAction.handle(null));
|
.ifPresent(buttonType -> openReportAction.handle(null));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.log(Level.SEVERE, "Error writing report " + reportPath + " to disk", e); //
|
LOGGER.log(Level.SEVERE, "Error writing report " + reportFolderPath + " to disk", e); //
|
||||||
new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_ErrorWritingReport(reportPath)).showAndWait();
|
new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_ErrorWritingReport(reportFolderPath)).showAndWait();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeSnapShotHTMLFile(Path reportPath, String reportTitle, ZoomParams zoomParams) throws IOException {
|
private static void writeSnapShotHTMLFile(Path reportPath, String reportTitle, ZoomParams zoomParams) throws IOException {
|
||||||
|
|
||||||
HashMap<String, Object> scopes = new HashMap<>();
|
HashMap<String, Object> context = new HashMap<>();
|
||||||
|
|
||||||
scopes.put("reportTitle", reportTitle);
|
|
||||||
scopes.put("snapshotFile", reportTitle + REPORT_IMAGE_EXTENSION);
|
|
||||||
|
|
||||||
|
context.put("reportTitle", reportTitle);
|
||||||
Interval timeRange = zoomParams.getTimeRange();
|
Interval timeRange = zoomParams.getTimeRange();
|
||||||
|
context.put("startTime", timeRange.getStart().toString(DateTimeFormat.fullDateTime()));
|
||||||
scopes.put("startTime", timeRange.getStart().toString(DateTimeFormat.fullDateTime()));
|
context.put("endTime", timeRange.getEnd().toString(DateTimeFormat.fullDateTime()));
|
||||||
scopes.put("endTime", timeRange.getEnd().toString(DateTimeFormat.fullDateTime()));
|
context.put("zoomParams", zoomParams);
|
||||||
|
|
||||||
scopes.put("zoomParams", zoomParams);
|
|
||||||
|
|
||||||
try (Writer writer = Files.newBufferedWriter(reportPath.resolve("snapshot.html"), Charset.forName("UTF-8"))) {
|
try (Writer writer = Files.newBufferedWriter(reportPath.resolve("snapshot.html"), Charset.forName("UTF-8"))) {
|
||||||
Mustache summaryMustache = mf.compile(new InputStreamReader(SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/snapshot_template.html")), "Snapshot");
|
Mustache summaryMustache = mf.compile(new InputStreamReader(SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/snapshot_template.html")), "Snapshot");
|
||||||
summaryMustache.execute(writer, scopes);
|
summaryMustache.execute(writer, context);
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeIndexHTML(Path reportPath) throws IOException {
|
||||||
|
|
||||||
|
HashMap<String, Object> context = new HashMap<>();
|
||||||
|
|
||||||
|
context.put("currentCase", currentCase);
|
||||||
|
|
||||||
|
try (Writer writer = Files.newBufferedWriter(reportPath.resolve("index.html"), Charset.forName("UTF-8"))) {
|
||||||
|
Mustache summaryMustache = mf.compile(new InputStreamReader(SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/index_template.html")), "Index");
|
||||||
|
summaryMustache.execute(writer, context);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param htmlWriter the value of htmlWriter
|
|
||||||
* @param key the value of Key
|
|
||||||
* @param value the value of value
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
private static void writeTableRow(final Writer htmlWriter, final String key, final String value) throws IOException {
|
|
||||||
htmlWriter.write("<tr><td>" + key + ": </td><td>" + value + "</td></tr>\n"); //
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the summary of the current case for this report.
|
* Write the summary of the current case for this report.
|
||||||
|
*
|
||||||
|
* @param reportPath the value of reportPath
|
||||||
|
* @param generationDate the value of generationDate
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"ReportHTML.writeSum.caseName=Case:",
|
"ReportHTML.writeSum.caseName=Case:",
|
||||||
@ -225,24 +202,57 @@ public class SaveSnapshotAsReport extends Action {
|
|||||||
"ReportHTML.writeSum.examiner=Examiner:",
|
"ReportHTML.writeSum.examiner=Examiner:",
|
||||||
"ReportHTML.writeSum.timezone=Timezone:",
|
"ReportHTML.writeSum.timezone=Timezone:",
|
||||||
"ReportHTML.writeSum.path=Path:"})
|
"ReportHTML.writeSum.path=Path:"})
|
||||||
private void writeSummary(Path reportPath) throws IOException {
|
private void writeSummary(Path reportPath, final Date generationDate, ReportBranding reportBranding) throws IOException {
|
||||||
HashMap<String, Object> scopes = new HashMap<>();
|
HashMap<String, Object> context = new HashMap<>();
|
||||||
|
|
||||||
scopes.put("reportBranding", reportBranding);
|
context.put("reportBranding", reportBranding);
|
||||||
|
context.put("generationDateTime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(generationDate));
|
||||||
scopes.put("generationDateTime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()));
|
context.put("ingestRunning", IngestManager.getInstance().isIngestRunning());
|
||||||
scopes.put("ingestRunning", IngestManager.getInstance().isIngestRunning());
|
context.put("currentCase", currentCase);
|
||||||
|
|
||||||
scopes.put("currentCase", currentCase);
|
|
||||||
|
|
||||||
try (Writer writer = Files.newBufferedWriter(reportPath.resolve("summary.html"), Charset.forName("UTF-8"))) {
|
try (Writer writer = Files.newBufferedWriter(reportPath.resolve("summary.html"), Charset.forName("UTF-8"))) {
|
||||||
|
|
||||||
Mustache summaryMustache = mf.compile(new InputStreamReader(SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/summary_template.html")), "Summary");
|
Mustache summaryMustache = mf.compile(new InputStreamReader(SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/summary_template.html")), "Summary");
|
||||||
summaryMustache.execute(writer, scopes);
|
summaryMustache.execute(writer, context);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copyResources(Path reportPath, ReportBranding reportBranding) throws IOException {
|
||||||
|
//copy navigation html
|
||||||
|
try (InputStream navStream = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/navigation_template.html")) {
|
||||||
|
Files.copy(navStream, reportPath.resolve("nav.html"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//pull generator and agency logo from branding
|
||||||
|
String generatorLogoPath = reportBranding.getGeneratorLogoPath();
|
||||||
|
if (StringUtils.isNotBlank(generatorLogoPath)) {
|
||||||
|
Files.copy(Paths.get(generatorLogoPath), Files.newOutputStream(reportPath.resolve("generator_logo.png")));
|
||||||
|
}
|
||||||
|
|
||||||
|
String agencyLogoPath = reportBranding.getAgencyLogoPath();
|
||||||
|
if (StringUtils.isNotBlank(agencyLogoPath)) {
|
||||||
|
Files.copy(Paths.get(agencyLogoPath), Files.newOutputStream(reportPath.resolve("agency_logo.png")));
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy favicon
|
||||||
|
try (InputStream faviconStream = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico")) {
|
||||||
|
Files.copy(faviconStream, reportPath.resolve("favicon.ico"));
|
||||||
|
}
|
||||||
|
try (InputStream faviconStream = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/summary.png")) {
|
||||||
|
Files.copy(faviconStream, reportPath.resolve("summary.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy report css
|
||||||
|
try (InputStream resource = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/index.css")) { //
|
||||||
|
Files.copy(resource, reportPath.resolve("index.css")); //
|
||||||
|
}
|
||||||
|
//copy summary css
|
||||||
|
try (InputStream resource = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/summary.css")) { //
|
||||||
|
Files.copy(resource, reportPath.resolve("summary.css")); //
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NbBundle.Messages({"OpenReportAction.DisplayName=Open Report",
|
@NbBundle.Messages({"OpenReportAction.DisplayName=Open Report",
|
||||||
"OpenReportAction.NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
|
"OpenReportAction.NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
|
||||||
"OpenReportAction.MessageBoxTitle=Open Report Failure",
|
"OpenReportAction.MessageBoxTitle=Open Report Failure",
|
||||||
@ -251,11 +261,11 @@ public class SaveSnapshotAsReport extends Action {
|
|||||||
"OpenReportAction.ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied."})
|
"OpenReportAction.ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied."})
|
||||||
private class OpenReportAction extends Action {
|
private class OpenReportAction extends Action {
|
||||||
|
|
||||||
OpenReportAction(File reportHTMLFIle) {
|
OpenReportAction(Path reportHTMLFIle) {
|
||||||
super(Bundle.OpenReportAction_DisplayName());
|
super(Bundle.OpenReportAction_DisplayName());
|
||||||
setEventHandler(actionEvent -> {
|
setEventHandler(actionEvent -> {
|
||||||
try {
|
try {
|
||||||
Desktop.getDesktop().open(reportHTMLFIle);
|
Desktop.getDesktop().open(reportHTMLFIle.toFile());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
JOptionPane.showMessageDialog(null,
|
JOptionPane.showMessageDialog(null,
|
||||||
Bundle.OpenReportAction_NoAssociatedEditorMessage(),
|
Bundle.OpenReportAction_NoAssociatedEditorMessage(),
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Autopsy Report for case test7</title>
|
<title>Autopsy Report for {{curentCase.getName}}</title>
|
||||||
<link rel="icon" type="image/ico" href="favicon.ico" />
|
<link rel="icon" type="image/ico" href="favicon.ico" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
</head>
|
</head>
|
||||||
<frameset cols="350px,*">
|
<frameset cols="350px,*">
|
||||||
<frame src="nav.html" name="nav">
|
<frame src="nav.html" name="nav"/>
|
||||||
<frame src="summary.html" name="content">
|
<frame src="summary.html" name="content"/>
|
||||||
<noframes>Your browser is not compatible with our frame setup.<br />
|
<noframes>Your browser is not compatible with our frame setup.<br />
|
||||||
Please see <a href="nav.html">the navigation page</a> for artifact links,<br />
|
Please see <a href="nav.html">the navigation page</a> for links,<br />
|
||||||
and <a href="summary.html">the summary page</a> for a case summary.</noframes>
|
and <a href="summary.html">the summary page</a> for a case summary.</noframes>
|
||||||
</frameset>
|
</frameset>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Report Navigation</title>
|
<title>Report Navigation</title>
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<h1>{{reportTitle}}</h1>
|
<h1>{{reportTitle}}</h1>
|
||||||
<img src = "{{snapshotFile}}" alt = "snaphot"><table>
|
<img src = "snapshot.png" alt = "snaphot"/>
|
||||||
|
<table>
|
||||||
<tr><td>Time Range: </td><td>{{startTime}}
|
<tr><td>Time Range: </td><td>{{startTime}}
|
||||||
to
|
to
|
||||||
{{endTime}}</td></tr>
|
{{endTime}}</td></tr>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user