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);
79 "Timeline.ModuleName=Timeline",
80 "SaveSnapShotAsReport.action.dialogs.title=Timeline",
81 "SaveSnapShotAsReport.action.name.text=Snapshot Report",
82 "SaveSnapShotAsReport.action.longText=Save a screen capture of the current view of the timeline as a report.",
83 "# {0} - report file path",
84 "SaveSnapShotAsReport.ReportSavedAt=Report saved at [{0}]",
85 "SaveSnapShotAsReport.Success=Success",
86 "SaveSnapShotAsReport.FailedToAddReport=Failed to add snaphot to case as a report.",
87 "# {0} - report path",
88 "SaveSnapShotAsReport.ErrorWritingReport=Error writing report to disk at {0}.",
89 "# {0} - generated default report name",
90 "SaveSnapShotAsReport.reportName.prompt=leave empty for default report name: {0}.",
91 "SaveSnapShotAsReport.reportName.header=Enter a report name for the Timeline Snapshot Report.",
92 "SaveSnapShotAsReport.duplicateReportNameError.text=A report with that name already exists."
95 super(Bundle.SaveSnapShotAsReport_action_name_text());
96 setLongText(Bundle.SaveSnapShotAsReport_action_longText());
97 setGraphic(
new ImageView(SNAP_SHOT));
102 setEventHandler(actionEvent -> {
104 Date generationDate =
new Date();
106 BufferedImage snapshot = SwingFXUtils.fromFXImage(nodeSupplier.get().snapshot(null, null), null);
109 TextInputDialog textInputDialog =
new TextInputDialog();
111 textInputDialog.setTitle(Bundle.SaveSnapShotAsReport_action_dialogs_title());
112 textInputDialog.getEditor().setPromptText(Bundle.SaveSnapShotAsReport_reportName_prompt(defaultReportName));
113 textInputDialog.setHeaderText(Bundle.SaveSnapShotAsReport_reportName_header());
116 textInputDialog.getEditor().setStyle(
"-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);");
123 ValidationSupport validationSupport =
new ValidationSupport();
124 validationSupport.registerValidator(textInputDialog.getEditor(),
false,
new Validator<String>() {
126 public ValidationResult apply(Control textField, String enteredReportName) {
127 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
128 boolean exists = Files.exists(Paths.get(currentCase.
getReportDirectory(), reportName));
129 return ValidationResult.fromErrorIf(textField, Bundle.SaveSnapShotAsReport_duplicateReportNameError_text(), exists);
132 textInputDialog.getDialogPane().lookupButton(ButtonType.OK).disableProperty().bind(validationSupport.invalidProperty());
135 textInputDialog.showAndWait().ifPresent(enteredReportName -> {
137 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
138 Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName,
"Timeline Snapshot");
139 Path reportMainFilePath;
147 generationDate, snapshot).writeReport();
148 }
catch (IOException ex) {
149 LOGGER.log(Level.SEVERE,
"Error writing report to disk at " + reportFolderPath, ex);
150 new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_ErrorWritingReport(reportFolderPath)).show();
158 LOGGER.log(Level.WARNING,
"Failed to add " + reportMainFilePath.toString() +
" to case as a report", ex);
159 new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_FailedToAddReport()).show();
164 final Alert alert =
new Alert(Alert.AlertType.INFORMATION, null, OPEN, OK);
165 alert.setTitle(Bundle.SaveSnapShotAsReport_action_dialogs_title());
166 alert.setHeaderText(Bundle.SaveSnapShotAsReport_Success());
170 HyperlinkLabel hyperlinkLabel =
new HyperlinkLabel(Bundle.SaveSnapShotAsReport_ReportSavedAt(reportMainFilePath.toString()));
171 hyperlinkLabel.setOnAction(openReportAction);
172 alert.getDialogPane().setContent(hyperlinkLabel);
174 alert.showAndWait().filter(OPEN::equals).ifPresent(buttonType -> openReportAction.handle(null));
183 "OpenReportAction.DisplayName=Open Report",
184 "OpenReportAction.NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
185 "OpenReportAction.MessageBoxTitle=Open Report Failure",
186 "OpenReportAction.NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
187 "OpenReportAction.MissingReportFileMessage=The report file no longer exists.",
188 "OpenReportAction.ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied."})
192 super(Bundle.OpenReportAction_DisplayName());
193 setEventHandler(actionEvent -> {
195 Desktop.getDesktop().open(reportHTMLFIle.toFile());
196 }
catch (IOException ex) {
197 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
198 Bundle.OpenReportAction_NoAssociatedEditorMessage(),
199 Bundle.OpenReportAction_MessageBoxTitle(),
200 JOptionPane.ERROR_MESSAGE);
201 }
catch (UnsupportedOperationException ex) {
202 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
203 Bundle.OpenReportAction_NoOpenInEditorSupportMessage(),
204 Bundle.OpenReportAction_MessageBoxTitle(),
205 JOptionPane.ERROR_MESSAGE);
206 }
catch (IllegalArgumentException ex) {
207 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
208 Bundle.OpenReportAction_MissingReportFileMessage(),
209 Bundle.OpenReportAction_MessageBoxTitle(),
210 JOptionPane.ERROR_MESSAGE);
211 }
catch (SecurityException ex) {
212 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
213 Bundle.OpenReportAction_ReportFileOpenPermissionDeniedMessage(),
214 Bundle.OpenReportAction_MessageBoxTitle(),
215 JOptionPane.ERROR_MESSAGE);
static final Logger LOGGER
FilteredEventsModel getEventsModel()
static final Image SNAP_SHOT
static Case getOpenCase()
static final ButtonType OPEN
String getReportDirectory()
void addReport(String localPath, String srcModuleName, String reportName)
SaveSnapshotAsReport(TimeLineController controller, Supplier< Node > nodeSupplier)
final TimeLineController controller
synchronized ZoomParams getZoomParamaters()
static String escapeFileName(String fileName)
synchronized static Logger getLogger(String name)
static void setDialogIcons(Dialog<?> dialog)
static final ButtonType OK