cleanup SaveSnapshotAsReport and templates

This commit is contained in:
jmillman 2016-04-27 10:58:42 -04:00
parent 7f68b8bf50
commit a0df66f37e
4 changed files with 103 additions and 94 deletions

View File

@ -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(),

View File

@ -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>

View File

@ -1,5 +1,3 @@
<html> <html>
<head> <head>
<title>Report Navigation</title> <title>Report Navigation</title>

View File

@ -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>