19 package org.sleuthkit.autopsy.timeline.actions;
21 import java.awt.Desktop;
22 import java.awt.image.BufferedImage;
23 import java.io.IOException;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.text.SimpleDateFormat;
28 import java.util.Date;
29 import java.util.function.Supplier;
30 import java.util.logging.Level;
31 import javafx.embed.swing.SwingFXUtils;
32 import javafx.scene.Node;
33 import javafx.scene.control.Alert;
34 import javafx.scene.control.ButtonBar;
35 import javafx.scene.control.ButtonType;
36 import javafx.scene.control.Control;
37 import javafx.scene.control.TextInputDialog;
38 import javafx.scene.image.Image;
39 import javafx.scene.image.ImageView;
40 import javax.swing.JOptionPane;
41 import org.apache.commons.lang3.StringUtils;
42 import org.controlsfx.control.HyperlinkLabel;
43 import org.controlsfx.control.action.Action;
44 import org.controlsfx.validation.ValidationResult;
45 import org.controlsfx.validation.ValidationSupport;
46 import org.controlsfx.validation.Validator;
47 import org.openide.util.NbBundle;
48 import org.openide.windows.WindowManager;
65 private static final Image
SNAP_SHOT =
new Image(
"org/sleuthkit/autopsy/timeline/images/image.png", 16, 16,
true,
true);
66 private static final ButtonType
OPEN =
new ButtonType(Bundle.OpenReportAction_DisplayName(), ButtonBar.ButtonData.NO);
67 private static final ButtonType
OK =
new ButtonType(ButtonType.OK.getText(), ButtonBar.ButtonData.CANCEL_CLOSE);
78 "Timeline.ModuleName=Timeline",
79 "SaveSnapShotAsReport.action.dialogs.title=Timeline",
80 "SaveSnapShotAsReport.action.name.text=Snapshot Report",
81 "SaveSnapShotAsReport.action.longText=Save a screen capture of the current view of the timeline as a report.",
82 "# {0} - report file path",
83 "SaveSnapShotAsReport.ReportSavedAt=Report saved at [{0}]",
84 "SaveSnapShotAsReport.Success=Success",
85 "SaveSnapShotAsReport.FailedToAddReport=Failed to add snaphot to case as a report.",
86 "# {0} - report path",
87 "SaveSnapShotAsReport.ErrorWritingReport=Error writing report to disk at {0}.",
88 "# {0} - generated default report name",
89 "SaveSnapShotAsReport.reportName.prompt=leave empty for default report name: {0}.",
90 "SaveSnapShotAsReport.reportName.header=Enter a report name for the Timeline Snapshot Report.",
91 "SaveSnapShotAsReport.duplicateReportNameError.text=A report with that name already exists."
94 super(Bundle.SaveSnapShotAsReport_action_name_text());
95 setLongText(Bundle.SaveSnapShotAsReport_action_longText());
96 setGraphic(
new ImageView(SNAP_SHOT));
100 setEventHandler(actionEvent -> {
102 Date generationDate =
new Date();
104 BufferedImage snapshot = SwingFXUtils.fromFXImage(nodeSupplier.get().snapshot(null, null), null);
107 TextInputDialog textInputDialog =
new TextInputDialog();
109 textInputDialog.setTitle(Bundle.SaveSnapShotAsReport_action_dialogs_title());
110 textInputDialog.getEditor().setPromptText(Bundle.SaveSnapShotAsReport_reportName_prompt(defaultReportName));
111 textInputDialog.setHeaderText(Bundle.SaveSnapShotAsReport_reportName_header());
114 textInputDialog.getEditor().setStyle(
"-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);");
121 ValidationSupport validationSupport =
new ValidationSupport();
122 validationSupport.registerValidator(textInputDialog.getEditor(),
false,
new Validator<String>() {
124 public ValidationResult apply(Control textField, String enteredReportName) {
125 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
126 boolean exists = Files.exists(Paths.get(currentCase.
getReportDirectory(), reportName));
127 return ValidationResult.fromErrorIf(textField, Bundle.SaveSnapShotAsReport_duplicateReportNameError_text(), exists);
130 textInputDialog.getDialogPane().lookupButton(ButtonType.OK).disableProperty().bind(validationSupport.invalidProperty());
133 textInputDialog.showAndWait().ifPresent(enteredReportName -> {
135 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
136 Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName,
"Timeline Snapshot");
137 Path reportMainFilePath;
145 generationDate, snapshot).writeReport();
146 }
catch (IOException ex) {
147 LOGGER.log(Level.SEVERE,
"Error writing report to disk at " + reportFolderPath, ex);
148 new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_ErrorWritingReport(reportFolderPath)).show();
156 LOGGER.log(Level.WARNING,
"Failed to add " + reportMainFilePath.toString() +
" to case as a report", ex);
157 new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_FailedToAddReport()).show();
162 final Alert alert =
new Alert(Alert.AlertType.INFORMATION, null, OPEN, OK);
163 alert.setTitle(Bundle.SaveSnapShotAsReport_action_dialogs_title());
164 alert.setHeaderText(Bundle.SaveSnapShotAsReport_Success());
168 HyperlinkLabel hyperlinkLabel =
new HyperlinkLabel(Bundle.SaveSnapShotAsReport_ReportSavedAt(reportMainFilePath.toString()));
169 hyperlinkLabel.setOnAction(openReportAction);
170 alert.getDialogPane().setContent(hyperlinkLabel);
172 alert.showAndWait().filter(OPEN::equals).ifPresent(buttonType -> openReportAction.handle(null));
181 "OpenReportAction.DisplayName=Open Report",
182 "OpenReportAction.NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
183 "OpenReportAction.MessageBoxTitle=Open Report Failure",
184 "OpenReportAction.NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
185 "OpenReportAction.MissingReportFileMessage=The report file no longer exists.",
186 "OpenReportAction.ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied."})
190 super(Bundle.OpenReportAction_DisplayName());
191 setEventHandler(actionEvent -> {
193 Desktop.getDesktop().open(reportHTMLFIle.toFile());
194 }
catch (IOException ex) {
195 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
196 Bundle.OpenReportAction_NoAssociatedEditorMessage(),
197 Bundle.OpenReportAction_MessageBoxTitle(),
198 JOptionPane.ERROR_MESSAGE);
199 }
catch (UnsupportedOperationException ex) {
200 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
201 Bundle.OpenReportAction_NoOpenInEditorSupportMessage(),
202 Bundle.OpenReportAction_MessageBoxTitle(),
203 JOptionPane.ERROR_MESSAGE);
204 }
catch (IllegalArgumentException ex) {
205 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
206 Bundle.OpenReportAction_MissingReportFileMessage(),
207 Bundle.OpenReportAction_MessageBoxTitle(),
208 JOptionPane.ERROR_MESSAGE);
209 }
catch (SecurityException ex) {
210 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
211 Bundle.OpenReportAction_ReportFileOpenPermissionDeniedMessage(),
212 Bundle.OpenReportAction_MessageBoxTitle(),
213 JOptionPane.ERROR_MESSAGE);
static final Logger LOGGER
FilteredEventsModel getEventsModel()
synchronized ZoomState getZoomState()
static final Image SNAP_SHOT
static final ButtonType OPEN
String getReportDirectory()
void addReport(String localPath, String srcModuleName, String reportName)
SaveSnapshotAsReport(TimeLineController controller, Supplier< Node > nodeSupplier)
static String escapeFileName(String fileName)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
static void setDialogIcons(Dialog<?> dialog)
static final ButtonType OK