19 package org.sleuthkit.autopsy.timeline.actions;
21 import java.awt.Desktop;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.Writer;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.time.LocalDateTime;
31 import java.util.function.Consumer;
32 import java.util.logging.Level;
33 import javafx.embed.swing.SwingFXUtils;
34 import javafx.event.ActionEvent;
35 import javafx.scene.Node;
36 import javafx.scene.control.Alert;
37 import javafx.scene.control.ButtonBar;
38 import javafx.scene.control.ButtonType;
39 import javafx.scene.image.Image;
40 import javafx.scene.image.ImageView;
41 import javafx.stage.Modality;
42 import javafx.stage.StageStyle;
43 import javax.imageio.ImageIO;
44 import javax.swing.JOptionPane;
45 import org.controlsfx.control.HyperlinkLabel;
46 import org.controlsfx.control.action.Action;
47 import org.openide.util.NbBundle;
61 private static final Image
SNAP_SHOT =
new Image(
"org/sleuthkit/autopsy/timeline/images/image.png", 16, 16,
true,
true);
62 private static final String
HTML_EXT =
".html";
64 private static final ButtonType
open =
new ButtonType(Bundle.OpenReportAction_DisplayName(), ButtonBar.ButtonData.NO);
65 private static final ButtonType
ok =
new ButtonType(ButtonType.OK.getText(), ButtonBar.ButtonData.CANCEL_CLOSE);
67 @NbBundle.Messages({
"SaveSnapshot.action.name.text=Snapshot Report",
68 "SaveSnapshot.action.longText=Save a screen capture of the visualization as a report.",
69 "SaveSnapshot.fileChoose.title.text=Save snapshot to",
70 "# {0} - report file path",
71 "SaveSnapShotAsReport.ReportSavedAt=Report saved at [{0}]",
72 "Timeline.ModuleName=Timeline",
"SaveSnapShotAsReport.Success=Success",
73 "# {0} - uniqueness identifier, local date time at report creation time",
74 "SaveSnapsHotAsReport.ReportName=timeline-report-{0}",
75 "SaveSnapShotAsReport.FailedToAddReport=Failed to add snaphot as a report. See log for details",
76 "# {0} - report name",
77 "SaveSnapShotAsReport.ErrorWritingReport=Error writing report {0} to disk. See log for details",})
79 super(Bundle.SaveSnapshot_action_name_text());
80 setLongText(Bundle.SaveSnapshot_action_longText());
81 setGraphic(
new ImageView(SNAP_SHOT));
82 setEventHandler(
new Consumer<ActionEvent>() {
85 public void accept(ActionEvent actioneEvent) {
87 String reportName = Bundle.SaveSnapsHotAsReport_ReportName(escapedLocalDateTime);
89 File reportHTMLFIle = reportPath.resolve(reportName + HTML_EXT).toFile();
95 Files.createDirectories(reportPath);
96 try (Writer htmlWriter =
new FileWriter(reportHTMLFIle)) {
101 ImageIO.write(SwingFXUtils.fromFXImage(node.snapshot(null, null), null),
"png",
102 reportPath.resolve(reportName + REPORT_IMAGE_EXTENSION).toFile());
105 try (InputStream resource = this.getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/timeline/index.css")) {
106 Files.copy(resource, reportPath.resolve(
"index.css"));
112 }
catch (TskCoreException ex) {
113 LOGGER.log(Level.WARNING,
"failed to add html wrapper as a report", ex);
114 new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_FailedToAddReport()).showAndWait();
118 final Alert alert =
new Alert(Alert.AlertType.INFORMATION, null, open, ok);
119 alert.setTitle(Bundle.SaveSnapshot_action_name_text());
120 alert.setHeaderText(Bundle.SaveSnapShotAsReport_Success());
121 alert.initStyle(StageStyle.UTILITY);
122 alert.initOwner(node.getScene().getWindow());
123 alert.initModality(Modality.APPLICATION_MODAL);
127 HyperlinkLabel hyperlinkLabel =
new HyperlinkLabel(Bundle.SaveSnapShotAsReport_ReportSavedAt(reportHTMLFIle.getPath()));
128 hyperlinkLabel.setOnAction(openReportAction);
129 alert.getDialogPane().setContent(hyperlinkLabel);
131 alert.showAndWait().ifPresent(buttonType -> {
132 if (buttonType == open) {
133 openReportAction.handle(null);
137 }
catch (IOException e) {
138 LOGGER.log(Level.SEVERE,
"Error writing report " + reportPath +
" to disk", e);
139 new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_ErrorWritingReport(reportPath)).showAndWait();
148 htmlWriter.write(
"<html>\n<head>\n\t<title>timeline snapshot</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n</head>\n<body>\n");
149 htmlWriter.write(
"<div id=\"content\">\n<h1>" + reportName +
"</h1>\n");
151 htmlWriter.write(
"<img src = \"" + reportName + REPORT_IMAGE_EXTENSION +
"\" alt = \"snaphot\">");
153 htmlWriter.write(
"<table>\n");
155 writeTableRow(htmlWriter,
"Time Range", zoomParams.getTimeRange().toString());
156 writeTableRow(htmlWriter,
"Description Level of Detail", zoomParams.getDescriptionLOD().getDisplayName());
157 writeTableRow(htmlWriter,
"Event Type Zoom Level", zoomParams.getTypeZoomLevel().getDisplayName());
158 writeTableRow(htmlWriter,
"Filters", zoomParams.getFilter().getHTMLReportString());
160 htmlWriter.write(
"</table>\n");
161 htmlWriter.write(
"</div>\n</body>\n</html>");
172 private static void writeTableRow(
final Writer htmlWriter,
final String key,
final String value)
throws IOException {
173 htmlWriter.write(
"<tr><td>" + key +
": </td><td>" + value +
"</td></tr>\n");
176 @NbBundle.Messages({
"OpenReportAction.DisplayName=Open Report",
177 "OpenReportAction.NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
178 "OpenReportAction.MessageBoxTitle=Open Report Failure",
179 "OpenReportAction.NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
180 "OpenReportAction.MissingReportFileMessage=The report file no longer exists.",
181 "OpenReportAction.ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied."})
185 super(Bundle.OpenReportAction_DisplayName());
186 setEventHandler(actionEvent -> {
188 Desktop.getDesktop().open(reportHTMLFIle);
189 }
catch (IOException ex) {
190 JOptionPane.showMessageDialog(null,
191 Bundle.OpenReportAction_NoAssociatedEditorMessage(),
192 Bundle.OpenReportAction_MessageBoxTitle(),
193 JOptionPane.ERROR_MESSAGE);
194 }
catch (UnsupportedOperationException ex) {
195 JOptionPane.showMessageDialog(null,
196 Bundle.OpenReportAction_NoOpenInEditorSupportMessage(),
197 Bundle.OpenReportAction_MessageBoxTitle(),
198 JOptionPane.ERROR_MESSAGE);
199 }
catch (IllegalArgumentException ex) {
200 JOptionPane.showMessageDialog(null,
201 Bundle.OpenReportAction_MissingReportFileMessage(),
202 Bundle.OpenReportAction_MessageBoxTitle(),
203 JOptionPane.ERROR_MESSAGE);
204 }
catch (SecurityException ex) {
205 JOptionPane.showMessageDialog(null,
206 Bundle.OpenReportAction_ReportFileOpenPermissionDeniedMessage(),
207 Bundle.OpenReportAction_MessageBoxTitle(),
208 JOptionPane.ERROR_MESSAGE);
static final Logger LOGGER
static void writeTableRow(final Writer htmlWriter, final String key, final String value)
static final String HTML_EXT
static void writeHTMLFile(String reportName, final Writer htmlWriter, ZoomParams zoomParams)
FilteredEventsModel getEventsModel()
SaveSnapshotAsReport(TimeLineController controller, Node node)
static final Image SNAP_SHOT
String getReportDirectory()
synchronized ReadOnlyObjectProperty< ZoomParams > zoomParametersProperty()
void addReport(String localPath, String srcModuleName, String reportName)
static final ButtonType open
static final ButtonType ok
static String escapeFileName(String fileName)
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
static final String REPORT_IMAGE_EXTENSION