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)