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)