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