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 java.util.Arrays;
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();
145 List<BlackboardAttribute> attributes = Arrays.asList(
146 new BlackboardAttribute(
147 TSK_TL_EVENT_TYPE, source,
148 TimelineEventType.USER_CREATED.getTypeID()),
149 new BlackboardAttribute(
150 TSK_DESCRIPTION, source,
152 new BlackboardAttribute(
153 TSK_DATETIME, source,
157 BlackboardArtifact artifact = eventInfo.
datasource.newDataArtifact(
new BlackboardArtifact.Type(TSK_TL_EVENT), attributes, null);
160 sleuthkitCase.getBlackboard().postArtifact(artifact, source, null);
161 }
catch (Blackboard.BlackboardException ex) {
162 logger.log(Level.SEVERE,
"Error posting artifact to the blackboard.", ex);
163 new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_postArtifactFailed(), ButtonType.OK).showAndWait();
165 }
catch (TskCoreException ex) {
166 logger.log(Level.SEVERE,
"Error creatig new artifact.", ex);
167 new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_createArtifactFailed(), ButtonType.OK).showAndWait();
177 private final JFXPanel jfxPanel =
new JFXPanel();
180 super(owner, Bundle.AddManualEvent_text(), Dialog.ModalityType.DOCUMENT_MODAL);
181 setIconImages(owner.getIconImages());
186 Platform.runLater(() -> {
190 ((ButtonBase) customPane.lookupButton(ButtonType.CANCEL)).setOnAction(event -> dispose());
192 ((ButtonBase) customPane.lookupButton(ButtonType.OK)).setOnAction(event -> {
194 if (manualEventInfo != null) {
195 addEvent(controller, manualEventInfo);
200 jfxPanel.setScene(
new Scene(customPane));
202 SwingUtilities.invokeLater(() -> {
205 setLocationRelativeTo(owner);
226 private final ValidationSupport validationSupport =
new ValidationSupport();
230 this.controller = controller;
232 if (epochMillis == null) {
233 timePicker.setLocalDateTime(LocalDateTime.now());
240 @NbBundle.Messages({
"# {0} - datasource name",
"# {1} - datasource id",
241 "AddManualEvent.EventCreationDialogPane.dataSourceStringConverter.template={0} (ID: {1})",
242 "AddManualEvent.EventCreationDialogPane.initialize.dataSourcesError=Error getting datasources in case."})
244 assert descriptionTextField != null :
"fx:id=\"descriptionTextField\" was not injected: check your FXML file 'EventCreationDialog.fxml'.";
246 timeZoneChooser.getItems().setAll(timeZoneList);
248 TextFields.bindAutoCompletion(timeZoneChooser.getEditor(), timeZoneList);
250 dataSourceChooser.setConverter(
new StringConverter<DataSource>() {
252 public String toString(DataSource dataSource) {
253 return Bundle.AddManualEvent_EventCreationDialogPane_dataSourceStringConverter_template(dataSource.getName(), dataSource.getId());
257 public DataSource fromString(String
string) {
258 throw new UnsupportedOperationException();
263 dataSourceChooser.getSelectionModel().select(0);
264 }
catch (TskCoreException ex) {
265 logger.log(Level.SEVERE,
"Error getting datasources in case.", ex);
266 SwingUtilities.invokeLater(() ->
MessageNotifyUtil.
Message.
error(Bundle.AddManualEvent_EventCreationDialogPane_initialize_dataSourcesError()));
274 "AddManualEvent.validation.description=Description is required.",
275 "AddManualEvent.validation.datetime=Invalid datetime",
276 "AddManualEvent.validation.timezone=Invalid time zone",})
278 validationSupport.registerValidator(descriptionTextField,
false,
279 Validator.createEmptyValidator(Bundle.AddManualEvent_validation_description()));
280 validationSupport.registerValidator(timePicker,
false,
281 Validator.createPredicateValidator(Objects::nonNull, Bundle.AddManualEvent_validation_description()));
282 validationSupport.registerValidator(timeZoneChooser,
false,
283 Validator.createPredicateValidator((String zone) -> timeZoneList.contains(zone.trim()), Bundle.AddManualEvent_validation_timezone()));
285 validationSupport.initInitialDecoration();
288 lookupButton(ButtonType.OK).disableProperty().bind(validationSupport.invalidProperty());
299 String zone = StringUtils.substringAfter(timeZoneChooser.getValue(),
")").trim();
300 long toEpochSecond = timePicker.getLocalDateTime().atZone(ZoneId.of(zone)).toEpochSecond();
301 return new ManualEventInfo(dataSourceChooser.getValue(), descriptionTextField.getText(), toEpochSecond);
316 this.datasource = datasource;
317 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)