7084 bug fixes for feedback

This commit is contained in:
William Schaefer 2021-01-07 14:11:23 -05:00
parent e846885d88
commit 52fcedb5ea
10 changed files with 119 additions and 28 deletions

View File

@ -201,7 +201,7 @@ public class DomainSearch {
* @throws DiscoveryException if unable to get the artifacts or the date * @throws DiscoveryException if unable to get the artifacts or the date
* attributes from an artifact. * attributes from an artifact.
*/ */
public List<MiniTimelineResult> getAllArtifactsForDomain(SleuthkitCase sleuthkitCase, String domain) throws DiscoveryException, InterruptedException { public List<MiniTimelineResult> getAllArtifactsForDomain(SleuthkitCase sleuthkitCase, String domain) throws DiscoveryException {
List<BlackboardArtifact> artifacts = new ArrayList<>(); List<BlackboardArtifact> artifacts = new ArrayList<>();
Map<String, List<BlackboardArtifact>> dateMap = new HashMap<>(); Map<String, List<BlackboardArtifact>> dateMap = new HashMap<>();
if (!StringUtils.isBlank(domain)) { if (!StringUtils.isBlank(domain)) {

View File

@ -94,11 +94,15 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
@Override @Override
BlackboardArtifact getSelectedArtifact() { BlackboardArtifact getSelectedArtifact() {
if (artifactsTable.getModel() instanceof DomainArtifactTableModel) {
int selectedIndex = artifactsTable.getSelectionModel().getLeadSelectionIndex(); int selectedIndex = artifactsTable.getSelectionModel().getLeadSelectionIndex();
if (selectedIndex < artifactsTable.getSelectionModel().getMinSelectionIndex() || artifactsTable.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > artifactsTable.getSelectionModel().getMaxSelectionIndex()) { if (selectedIndex < artifactsTable.getSelectionModel().getMinSelectionIndex() || artifactsTable.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > artifactsTable.getSelectionModel().getMaxSelectionIndex()) {
return null; return null;
} }
return tableModel.getArtifactByRow(artifactsTable.convertRowIndexToModel(selectedIndex)); return tableModel.getArtifactByRow(artifactsTable.convertRowIndexToModel(selectedIndex));
} else {
return null;
}
} }
@Override @Override
@ -124,7 +128,12 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void addArtifacts(List<BlackboardArtifact> artifactList) { void addArtifacts(List<BlackboardArtifact> artifactList) {
if (!artifactList.isEmpty()) {
artifactsTable.setModel(tableModel);
tableModel.setContents(artifactList); tableModel.setContents(artifactList);
} else {
artifactsTable.setModel(new EmptyTableModel());
}
artifactsTable.validate(); artifactsTable.validate();
artifactsTable.repaint(); artifactsTable.repaint();
tableModel.fireTableDataChanged(); tableModel.fireTableDataChanged();
@ -359,6 +368,45 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
} }
} }
} }
/**
* Table model which displays only that no results were found.
*/
private class EmptyTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
@Override
public int getRowCount() {
return 1;
}
@Override
public int getColumnCount() {
return 1;
}
@NbBundle.Messages({"ArtifactsListPanel.noResultsFound.text=No results found"})
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return Bundle.ArtifactsListPanel_value_noValue();
}
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return Bundle.ArtifactsListPanel_dateColumn_name();
case 1:
return Bundle.ArtifactsListPanel_titleColumn_name();
case 2:
return Bundle.ArtifactsListPanel_mimeTypeColumn_name();
default:
return "";
}
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTable artifactsTable; private javax.swing.JTable artifactsTable;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -61,7 +61,7 @@ class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType)); return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType));
} catch (DiscoveryException ex) { } catch (DiscoveryException ex) {
if (ex.getCause() instanceof InterruptedException) { if (ex.getCause() instanceof InterruptedException) {
logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain); //ignore the exception as it was cancelled while the cache was performing its get and we support cancellation
} else { } else {
throw ex; throw ex;
} }
@ -73,7 +73,7 @@ class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
@Override @Override
protected void done() { protected void done() {
List<BlackboardArtifact> listOfArtifacts = new ArrayList<>(); List<BlackboardArtifact> listOfArtifacts = new ArrayList<>();
if (!isCancelled() && isDone()) { if (!isCancelled()) {
try { try {
listOfArtifacts.addAll(get()); listOfArtifacts.addAll(get());
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.ArtifactSearchResultEvent(artifactType, listOfArtifacts)); DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.ArtifactSearchResultEvent(artifactType, listOfArtifacts));

View File

@ -3,6 +3,7 @@ ArtifactMenuMouseAdapter_label=Extract Files
ArtifactsListPanel.dateColumn.name=Date/Time ArtifactsListPanel.dateColumn.name=Date/Time
ArtifactsListPanel.fileNameColumn.name=Name ArtifactsListPanel.fileNameColumn.name=Name
ArtifactsListPanel.mimeTypeColumn.name=MIME Type ArtifactsListPanel.mimeTypeColumn.name=MIME Type
ArtifactsListPanel.noResultsFound.text=No results found
ArtifactsListPanel.termColumn.name=Term ArtifactsListPanel.termColumn.name=Term
ArtifactsListPanel.titleColumn.name=Title ArtifactsListPanel.titleColumn.name=Title
ArtifactsListPanel.urlColumn.name=URL ArtifactsListPanel.urlColumn.name=URL

View File

@ -40,6 +40,7 @@ import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils; import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils.PopulateDomainTabsEvent;
import org.sleuthkit.autopsy.discovery.search.SearchData.Type; import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
import static org.sleuthkit.autopsy.discovery.search.SearchData.Type.DOMAIN; import static org.sleuthkit.autopsy.discovery.search.SearchData.Type.DOMAIN;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter; import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
@ -60,9 +61,11 @@ public final class DiscoveryTopComponent extends TopComponent {
private volatile static int previousDividerLocation = 250; private volatile static int previousDividerLocation = 250;
private final GroupListPanel groupListPanel; private final GroupListPanel groupListPanel;
private final ResultsPanel resultsPanel; private final ResultsPanel resultsPanel;
private JPanel detailsPanel = new JPanel();
private String selectedDomainTabName; private String selectedDomainTabName;
private Type searchType; private Type searchType;
private int dividerLocation = JSplitPane.UNDEFINED_CONDITION; private int dividerLocation = JSplitPane.UNDEFINED_CONDITION;
private SwingAnimator animator = null; private SwingAnimator animator = null;
/** /**
@ -88,19 +91,19 @@ public final class DiscoveryTopComponent extends TopComponent {
rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)) { if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)
//Only change the saved location when it was a manual change by the user and not the animation or the window opening initially && ((animator == null || !animator.isRunning())
if ((animator == null || !animator.isRunning())
&& evt.getNewValue() instanceof Integer && evt.getNewValue() instanceof Integer
&& evt.getOldValue() instanceof Integer && evt.getOldValue() instanceof Integer
&& ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize()) && ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue()) && (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())
&& ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION)) { && ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION))) {
//Only change the saved location when it was a manual change by the user and not the animation or the window opening initially
previousDividerLocation = (int) evt.getNewValue(); previousDividerLocation = (int) evt.getNewValue();
} }
} }
} }
}); );
} }
@ -132,7 +135,6 @@ public final class DiscoveryTopComponent extends TopComponent {
*/ */
public static DiscoveryTopComponent getTopComponent() { public static DiscoveryTopComponent getTopComponent() {
DiscoveryTopComponent discoveryTopComp = (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); DiscoveryTopComponent discoveryTopComp = (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID);
discoveryTopComp.resetBottomComponent();
return discoveryTopComp; return discoveryTopComp;
} }
@ -169,9 +171,9 @@ public final class DiscoveryTopComponent extends TopComponent {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this); DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel); DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel); DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(rightSplitPane.getBottomComponent()); DiscoveryEventUtils.getDiscoveryEventBus().unregister(detailsPanel);
if (rightSplitPane.getBottomComponent() instanceof DomainDetailsPanel) { if (detailsPanel instanceof DomainDetailsPanel) {
selectedDomainTabName = ((DomainDetailsPanel) rightSplitPane.getBottomComponent()).getSelectedTabName(); selectedDomainTabName = ((DomainDetailsPanel) detailsPanel).getSelectedTabName();
} }
resetBottomComponent(); resetBottomComponent();
super.componentClosed(); super.componentClosed();
@ -274,6 +276,25 @@ public final class DiscoveryTopComponent extends TopComponent {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
/**
* Respond to the PopulateDomainTabsEvent to ensure the bottom area only
* shows when it has data
*
* @param populateDomainTabsEvent The event which indicates the domain tabs
* contents should change.
*/
@Subscribe
private void handlePopulateDomainTabsEvent(PopulateDomainTabsEvent populateDomainTabsEvent) {
if (detailsPanel instanceof DomainDetailsPanel) {
SwingUtilities.invokeLater(() -> {
if (((DomainDetailsPanel) detailsPanel).getCurrentTabStatus() == DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED
|| ((DomainDetailsPanel) detailsPanel).getCurrentTabStatus() == DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING) {
rightSplitPane.setBottomComponent(detailsPanel);
}
});
}
}
/** /**
* Subscribe to the DetailsVisible event and animate the panel as it changes * Subscribe to the DetailsVisible event and animate the panel as it changes
* visibility. * visibility.
@ -311,6 +332,7 @@ public final class DiscoveryTopComponent extends TopComponent {
@Subscribe @Subscribe
void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) { void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
rightSplitPane.setBottomComponent(new JPanel());
newSearchButton.setText(Bundle.DiscoveryTopComponent_cancelButton_text()); newSearchButton.setText(Bundle.DiscoveryTopComponent_cancelButton_text());
progressMessageTextArea.setForeground(Color.red); progressMessageTextArea.setForeground(Color.red);
searchType = searchStartedEvent.getType(); searchType = searchStartedEvent.getType();
@ -343,12 +365,16 @@ public final class DiscoveryTopComponent extends TopComponent {
} }
selectedDomainTabName = validateLastSelectedType(searchCompleteEvent); selectedDomainTabName = validateLastSelectedType(searchCompleteEvent);
DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel(); DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel();
rightSplitPane.setBottomComponent(domainDetailsPanel);
domainDetailsPanel.configureArtifactTabs(selectedDomainTabName); domainDetailsPanel.configureArtifactTabs(selectedDomainTabName);
detailsPanel = domainDetailsPanel;
} else { } else {
rightSplitPane.setBottomComponent(new FileDetailsPanel()); FileDetailsPanel fileDetailsPanel = new FileDetailsPanel();
DiscoveryEventUtils.getDiscoveryEventBus().register(fileDetailsPanel);
detailsPanel = new FileDetailsPanel();
rightSplitPane.setBottomComponent(detailsPanel);
} }
DiscoveryEventUtils.getDiscoveryEventBus().register(rightSplitPane.getBottomComponent());
DiscoveryEventUtils.getDiscoveryEventBus().register(detailsPanel);
descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; ")); descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; "));
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText)); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText));
progressMessageTextArea.setCaretPosition(0); progressMessageTextArea.setCaretPosition(0);

View File

@ -104,6 +104,20 @@ final class DomainDetailsPanel extends JPanel {
} }
} }
/**
* Get the status of the currently selected tab.
*
* @return The loading status of the currently selected tab.
*/
DomainArtifactsTabPanel.ArtifactRetrievalStatus getCurrentTabStatus() {
if (jTabbedPane1.getSelectedComponent() instanceof MiniTimelinePanel) {
return ((MiniTimelinePanel) jTabbedPane1.getSelectedComponent()).getStatus();
} else if (jTabbedPane1.getSelectedComponent() instanceof DomainArtifactsTabPanel) {
return ((DomainArtifactsTabPanel) jTabbedPane1.getSelectedComponent()).getStatus();
}
return null;
}
/** /**
* Run the worker which retrieves the list of artifacts for the domain to * Run the worker which retrieves the list of artifacts for the domain to
* populate the details area. * populate the details area.

View File

@ -33,6 +33,7 @@
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="domainListModel" type="code"/> <Connection code="domainListModel" type="code"/>
</Property> </Property>
<Property name="selectionMode" type="int" value="0"/>
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DomainSummaryPanel()" type="code"/> <Connection code="new DomainSummaryPanel()" type="code"/>
</Property> </Property>

View File

@ -64,6 +64,7 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
setLayout(new java.awt.BorderLayout()); setLayout(new java.awt.BorderLayout());
domainList.setModel(domainListModel); domainList.setModel(domainListModel);
domainList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
domainList.setCellRenderer(new DomainSummaryPanel()); domainList.setCellRenderer(new DomainSummaryPanel());
domainScrollPane.setViewportView(domainList); domainScrollPane.setViewportView(domainList);

View File

@ -60,7 +60,7 @@ class MiniTimelineWorker extends SwingWorker<List<MiniTimelineResult>, Void> {
} catch (DiscoveryException ex) { } catch (DiscoveryException ex) {
if (ex.getCause() instanceof InterruptedException) { if (ex.getCause() instanceof InterruptedException) {
logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain); //ignore the exception as it was cancelled while the cache was performing its get and we support cancellation
} else { } else {
throw ex; throw ex;
} }
@ -72,7 +72,7 @@ class MiniTimelineWorker extends SwingWorker<List<MiniTimelineResult>, Void> {
@Override @Override
protected void done() { protected void done() {
List<MiniTimelineResult> results = new ArrayList<>(); List<MiniTimelineResult> results = new ArrayList<>();
if (!isCancelled() && isDone()) { if (!isCancelled()) {
try { try {
results.addAll(get()); results.addAll(get());
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results)); DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results));