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);
 
  136         "AddManualEvent.createArtifactFailed=Failed to create artifact for event.",
 
  137         "AddManualEvent.postArtifactFailed=Failed to post artifact to blackboard."})
 
  143             String source = MANUAL_CREATION + 
": " + sleuthkitCase.getCurrentExaminer().getLoginName();
 
  145             BlackboardArtifact artifact = sleuthkitCase.newBlackboardArtifact(TSK_TL_EVENT, eventInfo.
datasource.getId());
 
  146             artifact.addAttributes(asList(
 
  147                     new BlackboardAttribute(
 
  148                             TSK_TL_EVENT_TYPE, source,
 
  149                             TimelineEventType.USER_CREATED.getTypeID()),
 
  150                     new BlackboardAttribute(
 
  151                             TSK_DESCRIPTION, source,
 
  153                     new BlackboardAttribute(
 
  154                             TSK_DATETIME, source,
 
  158                 sleuthkitCase.getBlackboard().postArtifact(artifact, source);
 
  159             } 
catch (Blackboard.BlackboardException ex) {
 
  160                 logger.log(Level.SEVERE, 
"Error posting artifact to the blackboard.", ex); 
 
  161                 new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_postArtifactFailed(), ButtonType.OK).showAndWait();
 
  163         } 
catch (TskCoreException ex) {
 
  164             logger.log(Level.SEVERE, 
"Error creatig new artifact.", ex); 
 
  165             new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_createArtifactFailed(), ButtonType.OK).showAndWait();
 
  175         private final JFXPanel jfxPanel = 
new JFXPanel();
 
  178             super(owner, Bundle.AddManualEvent_text(), Dialog.ModalityType.DOCUMENT_MODAL);
 
  179             setIconImages(owner.getIconImages());
 
  184             Platform.runLater(() -> {
 
  188                 ((ButtonBase) customPane.lookupButton(ButtonType.CANCEL)).setOnAction(event -> dispose());
 
  190                 ((ButtonBase) customPane.lookupButton(ButtonType.OK)).setOnAction(event -> {
 
  192                     if (manualEventInfo != null) {
 
  193                         addEvent(controller, manualEventInfo);
 
  198                 jfxPanel.setScene(
new Scene(customPane));
 
  200                 SwingUtilities.invokeLater(() -> {
 
  203                     setLocationRelativeTo(owner);
 
  224             private final ValidationSupport validationSupport = 
new ValidationSupport();
 
  228                 this.controller = controller;
 
  230                 if (epochMillis == null) {
 
  231                     timePicker.setLocalDateTime(LocalDateTime.now());
 
  238             @NbBundle.Messages({
"# {0} - datasource name", 
"# {1} - datasource id",
 
  239                 "AddManualEvent.EventCreationDialogPane.dataSourceStringConverter.template={0} (ID: {1})",
 
  240                 "AddManualEvent.EventCreationDialogPane.initialize.dataSourcesError=Error getting datasources in case."})
 
  242                 assert descriptionTextField != null : 
"fx:id=\"descriptionTextField\" was not injected: check your FXML file 'EventCreationDialog.fxml'.";
 
  244                 timeZoneChooser.getItems().setAll(timeZoneList);
 
  246                 TextFields.bindAutoCompletion(timeZoneChooser.getEditor(), timeZoneList);
 
  248                 dataSourceChooser.setConverter(
new StringConverter<DataSource>() {
 
  250                     public String toString(DataSource dataSource) {
 
  251                         return Bundle.AddManualEvent_EventCreationDialogPane_dataSourceStringConverter_template(dataSource.getName(), dataSource.getId());
 
  255                     public DataSource fromString(String 
string) {
 
  256                         throw new UnsupportedOperationException(); 
 
  261                     dataSourceChooser.getSelectionModel().select(0);
 
  262                 } 
catch (TskCoreException ex) {
 
  263                     logger.log(Level.SEVERE, 
"Error getting datasources in case.", ex);
 
  264                     SwingUtilities.invokeLater(() -> 
MessageNotifyUtil.
Message.
error(Bundle.AddManualEvent_EventCreationDialogPane_initialize_dataSourcesError()));
 
  272                 "AddManualEvent.validation.description=Description is required.",
 
  273                 "AddManualEvent.validation.datetime=Invalid datetime",
 
  274                 "AddManualEvent.validation.timezone=Invalid time zone",})
 
  276                 validationSupport.registerValidator(descriptionTextField, 
false,
 
  277                         Validator.createEmptyValidator(Bundle.AddManualEvent_validation_description()));
 
  278                 validationSupport.registerValidator(timePicker, 
false,
 
  279                         Validator.createPredicateValidator(Objects::nonNull, Bundle.AddManualEvent_validation_description()));
 
  280                 validationSupport.registerValidator(timeZoneChooser, 
false,
 
  281                         Validator.createPredicateValidator((String zone) -> timeZoneList.contains(zone.trim()), Bundle.AddManualEvent_validation_timezone()));
 
  283                 validationSupport.initInitialDecoration();
 
  286                 lookupButton(ButtonType.OK).disableProperty().bind(validationSupport.invalidProperty());
 
  297                 String zone = StringUtils.substringAfter(timeZoneChooser.getValue(), 
")").trim(); 
 
  298                 long toEpochSecond = timePicker.getLocalDateTime().atZone(ZoneId.of(zone)).toEpochSecond();
 
  299                 return new ManualEventInfo(dataSourceChooser.getValue(), descriptionTextField.getText(), toEpochSecond);
 
  314             this.datasource = datasource;
 
  315             this.description = description;
 
JEventCreationDialog(TimeLineController controller, Long epochMillis, java.awt.Window owner)
 
TextField descriptionTextField
 
EventCreationDialogPane(TimeLineController controller, Long epochMillis)
 
SleuthkitCase getSleuthkitCase()
 
AddManualEvent(TimeLineController controller, Long epochMillis)
 
FilteredEventsModel getEventsModel()
 
final TimeLineController controller
 
ManualEventInfo(DataSource datasource, String description, long time)
 
static List< String > createTimeZoneList()
 
ManualEventInfo getManualEventInfo()
 
static TimeZone getTimeZone()
 
LocalDateTimeTextField timePicker
 
static ZoneId getTimeZoneID()
 
static String createTimeZoneString(TimeZone timeZone)
 
SleuthkitCase getSleuthkitCase()
 
ComboBox< String > timeZoneChooser
 
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)