timeline time range fix

This commit is contained in:
Greg DiCristofaro 2020-11-23 13:32:49 -05:00
parent 27c6283959
commit e015b93eb5
3 changed files with 86 additions and 53 deletions

View File

@ -31,6 +31,7 @@ import org.apache.commons.collections.CollectionUtils;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineDataSourceUtils;
@ -64,7 +65,7 @@ import org.sleuthkit.datamodel.TskCoreException;
"TimlinePanel_last30DaysChart_fileEvts_title=File Events",
"TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events",})
public class TimelinePanel extends BaseDataSourceSummaryPanel {
private static final Logger logger = Logger.getLogger(TimelinePanel.class.getName());
private static final long serialVersionUID = 1L;
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy");
@ -86,7 +87,6 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title());
private final LoadableLabel latestLabel = new LoadableLabel(Bundle.TimelinePanel_latestLabel_title());
private final BarChartPanel last30DaysChart = new BarChartPanel(Bundle.TimlinePanel_last30DaysChart_title(), "", "");
private final OpenTimelineAction openTimelineAction = new OpenTimelineAction();
private final TimelineDataSourceUtils timelineUtils = TimelineDataSourceUtils.getInstance();
// all loadable components on this tab
@ -94,7 +94,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
// actions to load data for this tab
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
public TimelinePanel() {
this(new TimelineSummary());
}
@ -109,7 +109,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
(dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT),
(result) -> handleResult(result))
);
initComponents();
}
@ -125,7 +125,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
private static String formatDate(Date date, DateFormat formatter) {
return date == null ? null : formatter.format(date);
}
private static final Color FILE_EVT_COLOR = new Color(228, 22, 28);
private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100);
@ -145,25 +145,25 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
// Create a bar chart item for each recent days activity item
List<BarChartItem> fileEvtCounts = new ArrayList<>();
List<BarChartItem> artifactEvtCounts = new ArrayList<>();
for (int i = 0; i < recentDaysActivity.size(); i++) {
DailyActivityAmount curItem = recentDaysActivity.get(i);
long fileAmt = curItem.getFileActivityCount();
long artifactAmt = curItem.getArtifactActivityCount() * 100;
String formattedDate = (i == 0 || i == recentDaysActivity.size() - 1)
? formatDate(curItem.getDay(), CHART_FORMAT) : "";
OrderedKey thisKey = new OrderedKey(formattedDate, i);
fileEvtCounts.add(new BarChartItem(thisKey, fileAmt));
artifactEvtCounts.add(new BarChartItem(thisKey, artifactAmt));
}
return Arrays.asList(
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts),
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts));
}
private final Object timelineBtnLock = new Object();
private TimelineSummaryData curTimelineData = null;
@ -179,11 +179,11 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity())));
if (result != null
&& result.getResultType() == DataFetchResult.ResultType.SUCCESS
&& result.getData() != null) {
synchronized (this.timelineBtnLock) {
this.curTimelineData = result.getData();
this.viewInTimelineBtn.setEnabled(true);
@ -208,7 +208,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
if (curTimelineData == null) {
return;
}
dataSource = curTimelineData.getDataSource();
if (CollectionUtils.isNotEmpty(curTimelineData.getMostRecentDaysActivity())) {
minDate = curTimelineData.getMostRecentDaysActivity().get(0).getDay();
@ -219,38 +219,59 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
}
}
}
openFilteredChart(dataSource, minDate, maxDate);
}
/**
* Action that occurs when 'View in Timeline' button is pressed.
*
* @param dataSource The data source to filter to.
* @param minDate The min date for the zoom of the window.
* @param maxDate The max date for the zoom of the window.
*/
private void openFilteredChart(DataSource dataSource, Date minDate, Date maxDate) {
OpenTimelineAction openTimelineAction = CallableSystemAction.get(OpenTimelineAction.class);
if (openTimelineAction == null) {
logger.log(Level.WARNING, "No OpenTimelineAction provided by CallableSystemAction; taking no redirect action.");
}
// notify dialog (if in dialog) should close.
TimelinePanel.this.notifyParentClose();
// open the timeline filtered to data source and zoomed in on interval
openTimelineAction.performAction();
Interval timeSpan = null;
try {
TimeLineController controller = TimeLineModule.getController();
final TimeLineController controller = TimeLineModule.getController();
if (dataSource != null) {
controller.pushFilters(timelineUtils.getDataSourceFilterState(dataSource));
}
if (minDate != null && maxDate != null) {
Interval timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate));
controller.pushTimeRange(timeSpan);
timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate));
}
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, "Unable to open Timeline view", ex);
logger.log(Level.WARNING, "Unable to view time range in Timeline view", ex);
}
try {
openTimelineAction.showTimeline(timeSpan);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "An unexpected exception occurred while opening the timeline.", ex);
}
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
onNewDataSource(dataFetchComponents, loadableComponents, dataSource);
}
@Override
public void close() {
ingestRunningLabel.unregister();

View File

@ -24,6 +24,7 @@ import javafx.application.Platform;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import org.joda.time.Interval;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
@ -46,13 +47,10 @@ import org.sleuthkit.datamodel.TskCoreException;
* An Action that opens the Timeline window. Has methods to open the window in
* various specific states (e.g., showing a specific artifact in the List View)
*/
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline")
@ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false)
@ActionReferences(value = {
@ActionReference(path = "Menu/Tools", position = 104)
,
@ActionReference(path = "Menu/Tools", position = 104),
@ActionReference(path = "Toolbars/Case", position = 104)})
public final class OpenTimelineAction extends CallableSystemAction {
@ -64,7 +62,6 @@ public final class OpenTimelineAction extends CallableSystemAction {
private final JButton toolbarButton = new JButton(getName(),
new ImageIcon(getClass().getResource("images/btn_icon_timeline_colorized_26.png"))); //NON-NLS
public OpenTimelineAction() {
toolbarButton.addActionListener(actionEvent -> performAction());
menuItem = super.getMenuPresenter();
@ -74,9 +71,10 @@ public final class OpenTimelineAction extends CallableSystemAction {
@Override
public boolean isEnabled() {
/**
* We used to also check if Case.getCurrentOpenCase().hasData() was true. We
* disabled that check because if it is executed while a data source is
* being added, it blocks the edt. We still do that in ImageGallery.
* We used to also check if Case.getCurrentOpenCase().hasData() was
* true. We disabled that check because if it is executed while a data
* source is being added, it blocks the edt. We still do that in
* ImageGallery.
*/
return super.isEnabled() && Case.isCaseOpen() && Installer.isJavaFxInited();
}
@ -103,7 +101,7 @@ public final class OpenTimelineAction extends CallableSystemAction {
@NbBundle.Messages({
"OpenTimelineAction.settingsErrorMessage=Failed to initialize timeline settings.",
"OpenTimeLineAction.msgdlg.text=Could not create timeline, there are no data sources."})
synchronized private void showTimeline(AbstractFile file, BlackboardArtifact artifact) throws TskCoreException {
synchronized private void showTimeline(AbstractFile file, BlackboardArtifact artifact, Interval timeSpan) throws TskCoreException {
try {
Case currentCase = Case.getCurrentCaseThrows();
if (currentCase.hasData() == false) {
@ -112,6 +110,15 @@ public final class OpenTimelineAction extends CallableSystemAction {
return;
}
TimeLineController controller = TimeLineModule.getController();
// if file or artifact not specified, specify the time range as either
// a) full range if timeSpan is null or b) the timeSpan
if (file == null && artifact == null) {
if (timeSpan == null) {
controller.showFullRange();
} else {
controller.pushTimeRange(timeSpan);
}
}
controller.showTimeLine(file, artifact);
} catch (NoCurrentCaseException e) {
//there is no case... Do nothing.
@ -123,7 +130,18 @@ public final class OpenTimelineAction extends CallableSystemAction {
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void showTimeline() throws TskCoreException {
showTimeline(null, null);
showTimeline(null, null, null);
}
/**
* Open timeline with the given timeSpan time range.
*
* @param timeSpan The time range to display.
* @throws TskCoreException
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void showTimeline(Interval timeSpan) throws TskCoreException {
showTimeline(null, null, timeSpan);
}
/**
@ -135,7 +153,7 @@ public final class OpenTimelineAction extends CallableSystemAction {
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void showFileInTimeline(AbstractFile file) throws TskCoreException {
showTimeline(file, null);
showTimeline(file, null, null);
}
/**
@ -146,7 +164,7 @@ public final class OpenTimelineAction extends CallableSystemAction {
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void showArtifactInTimeline(BlackboardArtifact artifact) throws TskCoreException {
showTimeline(null, artifact);
showTimeline(null, artifact, null);
}
@Override

View File

@ -62,7 +62,6 @@ import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
@ -348,7 +347,7 @@ public class TimeLineController {
/**
* Show the entire range of the timeline.
*/
private boolean showFullRange() throws TskCoreException {
boolean showFullRange() throws TskCoreException {
synchronized (filteredEvents) {
return pushTimeRange(filteredEvents.getSpanningInterval());
}
@ -359,7 +358,7 @@ public class TimeLineController {
* ViewInTimelineRequestedEvent in the List View.
*
* @param requestEvent Contains the ID of the requested events and the
* timerange to show.
* timerange to show.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private void showInListView(ViewInTimelineRequestedEvent requestEvent) throws TskCoreException {
@ -376,14 +375,13 @@ public class TimeLineController {
}
}
/**
* Shuts down the task executor in charge of handling case events.
*/
void shutDownTimeLineListeners() {
ThreadUtils.shutDownTaskExecutor(executor);
}
/**
* "Shut down" Timeline. Close the timeline window.
*/
@ -394,15 +392,13 @@ public class TimeLineController {
topComponent = null;
}
}
/**
* Add the case and ingest listeners, prompt for rebuilding the database if
* necessary, and show the timeline window.
*
* @param file The AbstractFile from which to choose an event to show in
* the List View.
* @param file The AbstractFile from which to choose an event to show in the
* List View.
* @param artifact The BlackboardArtifact to show in the List View.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@ -421,9 +417,7 @@ public class TimeLineController {
try {
if (file == null && artifact == null) {
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
this.showFullRange();
} else {
//prompt user to pick specific event and time range
ShowInTimelineDialog showInTimelineDilaog = (file == null)
? new ShowInTimelineDialog(this, artifact)
@ -453,7 +447,7 @@ public class TimeLineController {
* around the middle of the currently viewed time range.
*
* @param period The period of time to show around the current center of the
* view.
* view.
*/
synchronized public void pushPeriod(ReadablePeriod period) throws TskCoreException {
synchronized (filteredEvents) {
@ -496,8 +490,8 @@ public class TimeLineController {
*/
topComponent.requestActive();
}
synchronized public TimeLineTopComponent getTopComponent(){
synchronized public TimeLineTopComponent getTopComponent() {
return topComponent;
}
@ -517,7 +511,7 @@ public class TimeLineController {
* @param timeRange The Interval to view.
*
* @return True if the interval was changed. False if the interval was the
* same as the existing one and no change happened.
* same as the existing one and no change happened.
*/
synchronized public boolean pushTimeRange(Interval timeRange) throws TskCoreException {
//clamp timerange to case
@ -709,7 +703,7 @@ public class TimeLineController {
* Register the given object to receive events.
*
* @param listener The object to register. Must implement public methods
* annotated with Subscribe.
* annotated with Subscribe.
*/
synchronized public void registerForEvents(Object listener) {
eventbus.register(listener);