19 package org.sleuthkit.autopsy.timeline.actions;
 
   21 import java.awt.Dialog;
 
   22 import java.time.Instant;
 
   23 import java.time.LocalDateTime;
 
   24 import java.time.ZoneId;
 
   25 import static java.util.Arrays.asList;
 
   26 import java.util.List;
 
   27 import java.util.Objects;
 
   28 import java.util.logging.Level;
 
   29 import javafx.application.Platform;
 
   30 import javafx.embed.swing.JFXPanel;
 
   31 import javafx.fxml.FXML;
 
   32 import javafx.scene.Scene;
 
   33 import javafx.scene.control.Alert;
 
   34 import javafx.scene.control.ButtonBase;
 
   35 import javafx.scene.control.ButtonType;
 
   36 import javafx.scene.control.ChoiceBox;
 
   37 import javafx.scene.control.ComboBox;
 
   38 import javafx.scene.control.DialogPane;
 
   39 import javafx.scene.control.TextField;
 
   40 import javafx.scene.image.Image;
 
   41 import javafx.scene.image.ImageView;
 
   42 import javafx.util.StringConverter;
 
   43 import javax.swing.JDialog;
 
   44 import javax.swing.SwingUtilities;
 
   45 import jfxtras.scene.control.LocalDateTimeTextField;
 
   46 import org.apache.commons.lang3.StringUtils;
 
   47 import org.controlsfx.control.action.Action;
 
   48 import org.controlsfx.control.textfield.TextFields;
 
   49 import org.controlsfx.tools.ValueExtractor;
 
   50 import org.controlsfx.validation.ValidationSupport;
 
   51 import org.controlsfx.validation.Validator;
 
   52 import org.openide.util.NbBundle;
 
   60 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT;
 
   62 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
 
   63 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION;
 
   64 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TL_EVENT_TYPE;
 
   75     "AddManualEvent.text=Add Event",
 
   76     "AddManualEvent.longText=Manually add an event to the timeline."})
 
   80     private static final String MANUAL_CREATION = 
"Manual Creation"; 
 
   81     private static final Image ADD_EVENT_IMAGE = 
new Image(
"/org/sleuthkit/autopsy/timeline/images/add.png", 16, 16, 
true, 
true, 
true); 
 
   88         ValueExtractor.addObservableValueExtractor(LocalDateTimeTextField.class::isInstance,
 
   89                 control -> ((LocalDateTimeTextField) control).localDateTimeProperty());
 
  102         this(controller, null);
 
  115         super(Bundle.AddManualEvent_text());
 
  116         setGraphic(
new ImageView(ADD_EVENT_IMAGE));
 
  117         setLongText(Bundle.AddManualEvent_longText());
 
  119         setEventHandler(actionEvent -> SwingUtilities.invokeLater(() -> {
 
  120             JEventCreationDialog dialog = new JEventCreationDialog(controller, epochMillis, SwingUtilities.windowForComponent(controller.getTopComponent()));
 
  121             dialog.setVisible(true);
 
  137         "AddManualEvent.createArtifactFailed=Failed to create artifact for event.",
 
  138         "AddManualEvent.postArtifactFailed=Failed to post artifact to blackboard."})
 
  144             String source = MANUAL_CREATION + 
": " + sleuthkitCase.getCurrentExaminer().getLoginName();
 
  146             BlackboardArtifact artifact = sleuthkitCase.newBlackboardArtifact(TSK_TL_EVENT, eventInfo.
datasource.getId());
 
  147             artifact.addAttributes(asList(
 
  148                     new BlackboardAttribute(
 
  149                             TSK_TL_EVENT_TYPE, source,
 
  150                             TimelineEventType.USER_CREATED.getTypeID()),
 
  151                     new BlackboardAttribute(
 
  152                             TSK_DESCRIPTION, source,
 
  154                     new BlackboardAttribute(
 
  155                             TSK_DATETIME, source,
 
  159                 sleuthkitCase.getBlackboard().postArtifact(artifact, source);
 
  160             } 
catch (Blackboard.BlackboardException ex) {
 
  161                 logger.log(Level.SEVERE, 
"Error posting artifact to the blackboard.", ex); 
 
  162                 new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_postArtifactFailed(), ButtonType.OK).showAndWait();
 
  164         } 
catch (TskCoreException ex) {
 
  165             logger.log(Level.SEVERE, 
"Error creatig new artifact.", ex); 
 
  166             new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_createArtifactFailed(), ButtonType.OK).showAndWait();
 
  176         private final JFXPanel jfxPanel = 
new JFXPanel();
 
  179             super(owner, Bundle.AddManualEvent_text(), Dialog.ModalityType.DOCUMENT_MODAL);
 
  180             setIconImages(owner.getIconImages());
 
  185             Platform.runLater(() -> {
 
  189                 ((ButtonBase) customPane.lookupButton(ButtonType.CANCEL)).setOnAction(event -> dispose());
 
  191                 ((ButtonBase) customPane.lookupButton(ButtonType.OK)).setOnAction(event -> {
 
  193                     if (manualEventInfo != null) {
 
  194                         addEvent(controller, manualEventInfo);
 
  199                 jfxPanel.setScene(
new Scene(customPane));
 
  201                 SwingUtilities.invokeLater(() -> {
 
  204                     setLocationRelativeTo(owner);
 
  225             private final ValidationSupport validationSupport = 
new ValidationSupport();
 
  229                 this.controller = controller;
 
  231                 if (epochMillis == null) {
 
  232                     timePicker.setLocalDateTime(LocalDateTime.now());
 
  239             @NbBundle.Messages({
"# {0} - datasource name", 
"# {1} - datasource id",
 
  240                 "AddManualEvent.EventCreationDialogPane.dataSourceStringConverter.template={0} (ID: {1})",
 
  241                 "AddManualEvent.EventCreationDialogPane.initialize.dataSourcesError=Error getting datasources in case."})
 
  243                 assert descriptionTextField != null : 
"fx:id=\"descriptionTextField\" was not injected: check your FXML file 'EventCreationDialog.fxml'.";
 
  245                 timeZoneChooser.getItems().setAll(timeZoneList);
 
  247                 TextFields.bindAutoCompletion(timeZoneChooser.getEditor(), timeZoneList);
 
  249                 dataSourceChooser.setConverter(
new StringConverter<DataSource>() {
 
  251                     public String toString(DataSource dataSource) {
 
  252                         return Bundle.AddManualEvent_EventCreationDialogPane_dataSourceStringConverter_template(dataSource.getName(), dataSource.getId());
 
  256                     public DataSource fromString(String 
string) {
 
  257                         throw new UnsupportedOperationException(); 
 
  262                     dataSourceChooser.getSelectionModel().select(0);
 
  263                 } 
catch (TskCoreException ex) {
 
  264                     logger.log(Level.SEVERE, 
"Error getting datasources in case.", ex);
 
  265                     SwingUtilities.invokeLater(() -> 
MessageNotifyUtil.
Message.
error(Bundle.AddManualEvent_EventCreationDialogPane_initialize_dataSourcesError()));
 
  273                 "AddManualEvent.validation.description=Description is required.",
 
  274                 "AddManualEvent.validation.datetime=Invalid datetime",
 
  275                 "AddManualEvent.validation.timezone=Invalid time zone",})
 
  277                 validationSupport.registerValidator(descriptionTextField, 
false,
 
  278                         Validator.createEmptyValidator(Bundle.AddManualEvent_validation_description()));
 
  279                 validationSupport.registerValidator(timePicker, 
false,
 
  280                         Validator.createPredicateValidator(Objects::nonNull, Bundle.AddManualEvent_validation_description()));
 
  281                 validationSupport.registerValidator(timeZoneChooser, 
false,
 
  282                         Validator.createPredicateValidator((String zone) -> timeZoneList.contains(zone.trim()), Bundle.AddManualEvent_validation_timezone()));
 
  284                 validationSupport.initInitialDecoration();
 
  287                 lookupButton(ButtonType.OK).disableProperty().bind(validationSupport.invalidProperty());
 
  298                 String zone = StringUtils.substringAfter(timeZoneChooser.getValue(), 
")").trim(); 
 
  299                 long toEpochSecond = timePicker.getLocalDateTime().atZone(ZoneId.of(zone)).toEpochSecond();
 
  300                 return new ManualEventInfo(dataSourceChooser.getValue(), descriptionTextField.getText(), toEpochSecond);
 
  315             this.datasource = datasource;
 
  316             this.description = description;
 
JEventCreationDialog(TimeLineController controller, Long epochMillis, java.awt.Window owner)
 
TextField descriptionTextField
 
EventCreationDialogPane(TimeLineController controller, Long epochMillis)
 
AddManualEvent(TimeLineController controller, Long epochMillis)
 
final TimeLineController controller
 
ManualEventInfo(DataSource datasource, String description, long time)
 
static List< String > createTimeZoneList()
 
ManualEventInfo getManualEventInfo()
 
static TimeZone getTimeZone()
 
LocalDateTimeTextField timePicker
 
SleuthkitCase getSleuthkitCase()
 
static ZoneId getTimeZoneID()
 
static String createTimeZoneString(TimeZone timeZone)
 
SleuthkitCase getSleuthkitCase()
 
ComboBox< String > timeZoneChooser
 
EventsModel getEventsModel()
 
ChoiceBox< DataSource > dataSourceChooser
 
synchronized static Logger getLogger(String name)
 
AddManualEvent(TimeLineController controller)
 
static void construct(Node node, String fxmlFileName)
 
void addEvent(TimeLineController controller, ManualEventInfo eventInfo)
 
final DataSource datasource
 
static void error(String message)