Merge pull request #6252 from wschaeferB/6714-displayDomainGroups-with6713

6714 display domain groups with6713
This commit is contained in:
Richard Cordovano 2020-09-16 13:41:51 -04:00 committed by GitHub
commit 80b78b8736
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 650 additions and 165 deletions

View File

@ -1,9 +1,11 @@
DiscoveryAttributes.GroupingAttributeType.datasource.displayName=Data Source
DiscoveryAttributes.GroupingAttributeType.fileType.displayName=File Type
DiscoveryAttributes.GroupingAttributeType.firstDate.displayName=First Activity Date
DiscoveryAttributes.GroupingAttributeType.frequency.displayName=Past Occurrences
DiscoveryAttributes.GroupingAttributeType.hash.displayName=Hash Set
DiscoveryAttributes.GroupingAttributeType.interestingItem.displayName=Interesting Item
DiscoveryAttributes.GroupingAttributeType.keywordList.displayName=Keyword
DiscoveryAttributes.GroupingAttributeType.mostRecentDate.displayName=Most Recent Activity Date
DiscoveryAttributes.GroupingAttributeType.none.displayName=None
DiscoveryAttributes.GroupingAttributeType.object.displayName=Object Detected
DiscoveryAttributes.GroupingAttributeType.parent.displayName=Parent Folder
@ -15,15 +17,17 @@ DiscoveryKeyUtils.DataSourceGroupKey.datasourceAndID={0}(ID: {1})
# {0} - Data source ID
DiscoveryKeyUtils.DataSourceGroupKey.idOnly=Data source (ID: {0})
DiscoveryKeyUtils.FileTagGroupKey.noSets=None
DiscoveryKeyUtils.FirstActivityDateGroupKey.noDate=No Date Available
DiscoveryKeyUtils.HashHitsGroupKey.noHashHits=None
DiscoveryKeyUtils.InterestingItemGroupKey.noSets=None
DiscoveryKeyUtils.KeywordListGroupKey.noKeywords=None
DiscoveryKeyUtils.MostRecentActivityDateGroupKey.noDate=No Date Available
DiscoveryKeyUtils.NoGroupingGroupKey.allFiles=All Files
DiscoveryKeyUtils.ObjectDetectedGroupKey.noSets=None
FileGroup.groupSortingAlgorithm.groupName.text=Group Name
FileGroup.groupSortingAlgorithm.groupSize.text=Group Size
FileSearch.documentSummary.noBytes=No bytes read for document, unable to display preview.
FileSearch.documentSummary.noPreview=No preview available.
FileSearch.HashHitsGroupKey.noHashHits=None
FileSearch.InterestingItemGroupKey.noSets=None
FileSearch.KeywordListGroupKey.noKeywords=None
FileSearch.ObjectDetectedGroupKey.noSets=None
FileSearchFiltering.concatenateSetNamesForDisplay.comma=,
# {0} - filters
FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0}

View File

@ -222,7 +222,7 @@ public class DiscoveryAttributes {
static class FrequencyAttribute extends AttributeType {
static final int BATCH_SIZE = 50; // Number of hashes to look up at one time
static final int DOMAIN_BATCH_SIZE = 500; // Number of domains to look up at one time
@Override
@ -256,14 +256,12 @@ public class DiscoveryAttributes {
CentralRepository centralRepoDb) throws DiscoveryException {
List<ResultFile> currentFiles = new ArrayList<>();
Set<String> hashesToLookUp = new HashSet<>();
List<ResultDomain> domainsToQuery = new ArrayList<>();
for (Result result : results) {
if (result.getKnown() == TskData.FileKnown.KNOWN) {
result.setFrequency(SearchData.Frequency.KNOWN);
}
if (result.getType() != SearchData.Type.DOMAIN) {
ResultFile file = (ResultFile) result;
if (file.getFrequency() == SearchData.Frequency.UNKNOWN
@ -272,41 +270,41 @@ public class DiscoveryAttributes {
hashesToLookUp.add(file.getFirstInstance().getMd5Hash());
currentFiles.add(file);
}
if (hashesToLookUp.size() >= BATCH_SIZE) {
computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
hashesToLookUp.clear();
currentFiles.clear();
if (hashesToLookUp.size() >= BATCH_SIZE) {
computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
hashesToLookUp.clear();
currentFiles.clear();
}
} else {
ResultDomain domainInstance = (ResultDomain) result;
domainsToQuery.add(domainInstance);
if (domainsToQuery.size() == DOMAIN_BATCH_SIZE) {
queryDomainFrequency(domainsToQuery, centralRepoDb);
domainsToQuery.clear();
}
}
}
queryDomainFrequency(domainsToQuery, centralRepoDb);
computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
}
}
private static void queryDomainFrequency(List<ResultDomain> domainsToQuery, CentralRepository centralRepository) throws DiscoveryException {
if (domainsToQuery.isEmpty()) {
return;
}
try {
final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>();
final StringJoiner joiner = new StringJoiner(", ");
final CorrelationAttributeInstance.Type attributeType = centralRepository.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
for(ResultDomain domainInstance : domainsToQuery) {
for (ResultDomain domainInstance : domainsToQuery) {
try {
final String domainValue = domainInstance.getDomain();
final String normalizedDomain = CorrelationAttributeNormalizer.normalize(attributeType, domainValue);
@ -320,10 +318,10 @@ public class DiscoveryAttributes {
}
final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
final String domainFrequencyQuery = " value AS domain_name, COUNT(*) AS frequency " +
"FROM " + tableName + " " +
"WHERE value IN (" + joiner + ") " +
"GROUP BY value";
final String domainFrequencyQuery = " value AS domain_name, COUNT(*) AS frequency "
+ "FROM " + tableName + " "
+ "WHERE value IN (" + joiner + ") "
+ "GROUP BY value";
final DomainFrequencyCallback frequencyCallback = new DomainFrequencyCallback(resultDomainTable);
centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback);
@ -335,15 +333,15 @@ public class DiscoveryAttributes {
throw new DiscoveryException("Fatal exception encountered querying the CR.", ex);
}
}
private static class DomainFrequencyCallback implements InstanceTableCallback {
private final Map<String, List<ResultDomain>> domainLookup;
private SQLException sqlCause;
private DomainFrequencyCallback(Map<String, List<ResultDomain>> domainLookup) {
this.domainLookup = domainLookup;
}
}
@Override
public void process(ResultSet resultSet) {
@ -351,9 +349,9 @@ public class DiscoveryAttributes {
while (resultSet.next()) {
String domain = resultSet.getString("domain_name");
Long frequency = resultSet.getLong("frequency");
List<ResultDomain> domainInstances = domainLookup.get(domain);
for(ResultDomain domainInstance : domainInstances) {
for (ResultDomain domainInstance : domainInstances) {
domainInstance.setFrequency(SearchData.Frequency.fromCount(frequency));
}
}
@ -361,7 +359,7 @@ public class DiscoveryAttributes {
this.sqlCause = ex;
}
}
SQLException getCause() {
return this.sqlCause;
}
@ -560,6 +558,30 @@ public class DiscoveryAttributes {
}
}
/**
* Attribute for grouping/sorting by date of most recent activity.
*/
static class MostRecentActivityDateAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.MostRecentActivityDateGroupKey(result);
}
}
/**
* Attribute for grouping/sorting by date of first activity.
*/
static class FirstActivityDateAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.FirstActivityDateGroupKey(result);
}
}
/**
* Attribute for grouping/sorting by objects detected
*/
@ -682,6 +704,8 @@ public class DiscoveryAttributes {
"DiscoveryAttributes.GroupingAttributeType.interestingItem.displayName=Interesting Item",
"DiscoveryAttributes.GroupingAttributeType.tag.displayName=Tag",
"DiscoveryAttributes.GroupingAttributeType.object.displayName=Object Detected",
"DiscoveryAttributes.GroupingAttributeType.mostRecentDate.displayName=Most Recent Activity Date",
"DiscoveryAttributes.GroupingAttributeType.firstDate.displayName=First Activity Date",
"DiscoveryAttributes.GroupingAttributeType.none.displayName=None"})
public enum GroupingAttributeType {
FILE_SIZE(new FileSizeAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_size_displayName()),
@ -693,6 +717,8 @@ public class DiscoveryAttributes {
INTERESTING_ITEM_SET(new InterestingItemAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_interestingItem_displayName()),
FILE_TAG(new FileTagAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_tag_displayName()),
OBJECT_DETECTED(new ObjectDetectedAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_object_displayName()),
MOST_RECENT_DATE(new MostRecentActivityDateAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_mostRecentDate_displayName()),
FIRST_DATE(new FirstActivityDateAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_firstDate_displayName()),
NO_GROUPING(new NoGroupingAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_none_displayName());
private final AttributeType attributeType;
@ -713,15 +739,24 @@ public class DiscoveryAttributes {
}
/**
* Get the list of enums that are valid for grouping images.
* Get the list of enums that are valid for grouping files.
*
* @return enums that can be used to group images
* @return Enums that can be used to group files.
*/
public static List<GroupingAttributeType> getOptionsForGrouping() {
public static List<GroupingAttributeType> getOptionsForGroupingForFiles() {
return Arrays.asList(FILE_SIZE, FREQUENCY, PARENT_PATH, OBJECT_DETECTED, HASH_LIST_NAME, INTERESTING_ITEM_SET);
}
/**
* Get the list of enums that are valid for grouping files.
*
* @return Enums that can be used to group files.
*/
public static List<GroupingAttributeType> getOptionsForGroupingForDomains() {
return Arrays.asList(FREQUENCY, MOST_RECENT_DATE, FIRST_DATE);
}
}
/**
* Computes the CR frequency of all the given hashes and updates the list of
* files.

View File

@ -18,9 +18,13 @@
*/
package org.sleuthkit.autopsy.discovery.search;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
@ -64,13 +68,13 @@ public class DiscoveryKeyUtils {
SearchKey(String userName, List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod,
ResultsSorter.SortingMethod fileSortingMethod,
SleuthkitCase sleuthkitCase, CentralRepository centralRepository) {
this.groupAttributeType = groupAttributeType;
this.groupSortingType = groupSortingType;
this.fileSortingMethod = fileSortingMethod;
this.filters = filters;
StringBuilder searchStringBuilder = new StringBuilder();
searchStringBuilder.append(userName);
for (AbstractFilter filter : filters) {
@ -81,15 +85,16 @@ public class DiscoveryKeyUtils {
this.sleuthkitCase = sleuthkitCase;
this.centralRepository = centralRepository;
}
/**
* Construct a SearchKey without a SleuthkitCase or CentralRepositry instance.
* Construct a SearchKey without a SleuthkitCase or CentralRepositry
* instance.
*/
SearchKey(String userName, List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod) {
this(userName, filters, groupAttributeType, groupSortingType,
this(userName, filters, groupAttributeType, groupSortingType,
fileSortingMethod, null, null);
}
@ -109,11 +114,11 @@ public class DiscoveryKeyUtils {
}
SearchKey otherSearchKey = (SearchKey) otherKey;
if (this.sleuthkitCase != otherSearchKey.getSleuthkitCase() ||
this.centralRepository != otherSearchKey.getCentralRepository()) {
if (this.sleuthkitCase != otherSearchKey.getSleuthkitCase()
|| this.centralRepository != otherSearchKey.getCentralRepository()) {
return false;
}
return getKeyString().equals(otherSearchKey.getKeyString());
}
@ -125,32 +130,54 @@ public class DiscoveryKeyUtils {
}
/**
* @return the keyString
* Get the String representation of this key.
*
* @return The String representation of this key.
*/
String getKeyString() {
return keyString;
}
/**
* Get the list of filters associated with this key.
*
* @return The list of filters associated with this key.
*/
List<AbstractFilter> getFilters() {
return Collections.unmodifiableList(this.filters);
}
/**
* Get the group sorting type for this key.
*
* @return The group sorting type for this key.
*/
Group.GroupSortingAlgorithm getGroupSortingType() {
return groupSortingType;
}
/**
* Get the grouping attribute for this key.
*
* @return The grouping attribute for this key.
*/
DiscoveryAttributes.AttributeType getGroupAttributeType() {
return groupAttributeType;
}
/**
* Get the fileSorting
*
* @return
*/
ResultsSorter.SortingMethod getFileSortingMethod() {
return fileSortingMethod;
}
SleuthkitCase getSleuthkitCase() {
return this.sleuthkitCase;
}
CentralRepository getCentralRepository() {
return this.centralRepository;
}
@ -326,12 +353,12 @@ public class DiscoveryKeyUtils {
private final String keywordListNamesString;
@NbBundle.Messages({
"FileSearch.KeywordListGroupKey.noKeywords=None"})
"DiscoveryKeyUtils.KeywordListGroupKey.noKeywords=None"})
KeywordListGroupKey(ResultFile file) {
keywordListNames = file.getKeywordListNames();
if (keywordListNames.isEmpty()) {
keywordListNamesString = Bundle.FileSearch_KeywordListGroupKey_noKeywords();
keywordListNamesString = Bundle.DiscoveryKeyUtils_KeywordListGroupKey_noKeywords();
} else {
keywordListNamesString = String.join(",", keywordListNames); // NON-NLS
}
@ -760,12 +787,12 @@ public class DiscoveryKeyUtils {
private final String hashSetNamesString;
@NbBundle.Messages({
"FileSearch.HashHitsGroupKey.noHashHits=None"})
"DiscoveryKeyUtils.HashHitsGroupKey.noHashHits=None"})
HashHitsGroupKey(ResultFile file) {
hashSetNames = file.getHashSetNames();
if (hashSetNames.isEmpty()) {
hashSetNamesString = Bundle.FileSearch_HashHitsGroupKey_noHashHits();
hashSetNamesString = Bundle.DiscoveryKeyUtils_HashHitsGroupKey_noHashHits();
} else {
hashSetNamesString = String.join(",", hashSetNames); // NON-NLS
}
@ -841,12 +868,12 @@ public class DiscoveryKeyUtils {
private final String interestingItemSetNamesString;
@NbBundle.Messages({
"FileSearch.InterestingItemGroupKey.noSets=None"})
"DiscoveryKeyUtils.InterestingItemGroupKey.noSets=None"})
InterestingItemGroupKey(ResultFile file) {
interestingItemSetNames = file.getInterestingSetNames();
if (interestingItemSetNames.isEmpty()) {
interestingItemSetNamesString = Bundle.FileSearch_InterestingItemGroupKey_noSets();
interestingItemSetNamesString = Bundle.DiscoveryKeyUtils_InterestingItemGroupKey_noSets();
} else {
interestingItemSetNamesString = String.join(",", interestingItemSetNames); // NON-NLS
}
@ -913,6 +940,176 @@ public class DiscoveryKeyUtils {
}
}
/**
* Key representing a date of most recent activity.
*/
static class MostRecentActivityDateGroupKey extends GroupKey {
private final Long epochDate;
private final String dateNameString;
@NbBundle.Messages({
"DiscoveryKeyUtils.MostRecentActivityDateGroupKey.noDate=No Date Available"})
MostRecentActivityDateGroupKey(Result result) {
if (result instanceof ResultDomain) {
epochDate = ((ResultDomain) result).getActivityEnd();
dateNameString = new SimpleDateFormat("yyyy/MM/dd").format(new Date(TimeUnit.SECONDS.toMillis(epochDate)));
} else {
epochDate = Long.MAX_VALUE;
dateNameString = Bundle.DiscoveryKeyUtils_MostRecentActivityDateGroupKey_noDate();
}
}
@Override
String getDisplayName() {
return getDateNameString();
}
@Override
public boolean equals(Object otherKey) {
if (otherKey == this) {
return true;
}
if (!(otherKey instanceof MostRecentActivityDateGroupKey)) {
return false;
}
MostRecentActivityDateGroupKey dateGroupKey = (MostRecentActivityDateGroupKey) otherKey;
return getDateNameString().equals(dateGroupKey.getDateNameString());
}
@Override
public int hashCode() {
return Objects.hash(getDateNameString());
}
@Override
public int compareTo(GroupKey otherGroupKey) {
if (otherGroupKey instanceof MostRecentActivityDateGroupKey) {
MostRecentActivityDateGroupKey otherDateGroupKey = (MostRecentActivityDateGroupKey) otherGroupKey;
// Put the empty list at the end
if (this.getEpochDate().equals(Long.MAX_VALUE)) {
if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) {
return 0;
} else {
return 1;
}
} else if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) {
return -1;
}
return getDateNameString().compareTo(otherDateGroupKey.getDateNameString());
} else {
return compareClassNames(otherGroupKey);
}
}
/**
* Get the date this group is for as a Long.
*
* @return The date.
*/
Long getEpochDate() {
return epochDate;
}
/**
* Get the name which identifies this group.
*
* @return The dateNameString
*/
String getDateNameString() {
return dateNameString;
}
}
/**
* Key representing a date of first activity.
*/
static class FirstActivityDateGroupKey extends GroupKey {
private final Long epochDate;
private final String dateNameString;
@NbBundle.Messages({
"DiscoveryKeyUtils.FirstActivityDateGroupKey.noDate=No Date Available"})
FirstActivityDateGroupKey(Result result) {
if (result instanceof ResultDomain) {
epochDate = ((ResultDomain) result).getActivityStart();
dateNameString = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(new Date(TimeUnit.SECONDS.toMillis(epochDate)));
} else {
epochDate = Long.MAX_VALUE;
dateNameString = Bundle.DiscoveryKeyUtils_FirstActivityDateGroupKey_noDate();
}
}
@Override
String getDisplayName() {
return getDateNameString();
}
@Override
public boolean equals(Object otherKey) {
if (otherKey == this) {
return true;
}
if (!(otherKey instanceof FirstActivityDateGroupKey)) {
return false;
}
FirstActivityDateGroupKey dateGroupKey = (FirstActivityDateGroupKey) otherKey;
return getDateNameString().equals(dateGroupKey.getDateNameString());
}
@Override
public int hashCode() {
return Objects.hash(getDateNameString());
}
@Override
public int compareTo(GroupKey otherGroupKey) {
if (otherGroupKey instanceof FirstActivityDateGroupKey) {
FirstActivityDateGroupKey otherDateGroupKey = (FirstActivityDateGroupKey) otherGroupKey;
// Put the empty list at the end
if (this.getEpochDate().equals(Long.MAX_VALUE)) {
if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) {
return 0;
} else {
return 1;
}
} else if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) {
return -1;
}
return getDateNameString().compareTo(otherDateGroupKey.getDateNameString());
} else {
return compareClassNames(otherGroupKey);
}
}
/**
* Get the date this group is for as a Long.
*
* @return The date.
*/
Long getEpochDate() {
return epochDate;
}
/**
* Get the name which identifies this group.
*
* @return The dateNameString
*/
String getDateNameString() {
return dateNameString;
}
}
/**
* Key representing an object detected group
*/
@ -922,12 +1119,12 @@ public class DiscoveryKeyUtils {
private final String objectDetectedNamesString;
@NbBundle.Messages({
"FileSearch.ObjectDetectedGroupKey.noSets=None"})
"DiscoveryKeyUtils.ObjectDetectedGroupKey.noSets=None"})
ObjectDetectedGroupKey(ResultFile file) {
objectDetectedNames = file.getObjectDetectedNames();
if (objectDetectedNames.isEmpty()) {
objectDetectedNamesString = Bundle.FileSearch_ObjectDetectedGroupKey_noSets();
objectDetectedNamesString = Bundle.DiscoveryKeyUtils_ObjectDetectedGroupKey_noSets();
} else {
objectDetectedNamesString = String.join(",", objectDetectedNames); // NON-NLS
}

View File

@ -36,10 +36,10 @@ import org.sleuthkit.datamodel.SleuthkitCase;
class DomainSearchCache {
private static final int MAXIMUM_CACHE_SIZE = 10;
private static final LoadingCache<SearchKey, Map<GroupKey, List<Result>>> cache =
CacheBuilder.newBuilder()
.maximumSize(MAXIMUM_CACHE_SIZE)
.build(new DomainSearchCacheLoader());
private static final LoadingCache<SearchKey, Map<GroupKey, List<Result>>> cache
= CacheBuilder.newBuilder()
.maximumSize(MAXIMUM_CACHE_SIZE)
.build(new DomainSearchCacheLoader());
/**
* Get domain search results matching the given parameters. If no results

View File

@ -229,16 +229,16 @@ public class ResultsSorter implements Comparator<Result> {
return compareStrings(((ResultFile) result1).getFirstInstance().getName().toLowerCase(), (((ResultFile) result2).getFirstInstance().getName().toLowerCase()));
};
}
/**
* Sorts domain names in lexographical order, ignoring case.
*/
private static Comparator<Result> getDomainNameComparator() {
return (Result domain1, Result domain2) -> {
if(domain1.getType() != SearchData.Type.DOMAIN) {
if (domain1.getType() != SearchData.Type.DOMAIN) {
return 0;
}
ResultDomain first = (ResultDomain) domain1;
ResultDomain second = (ResultDomain) domain2;
return compareStrings(first.getDomain().toLowerCase(), second.getDomain().toLowerCase());
@ -327,7 +327,7 @@ public class ResultsSorter implements Comparator<Result> {
BY_KEYWORD_LIST_NAMES(Arrays.asList(new DiscoveryAttributes.KeywordListAttribute()),
Bundle.FileSorter_SortingMethod_keywordlist_displayName()), // Sort alphabetically by list of keyword list names found
BY_FULL_PATH(new ArrayList<>(),
Bundle.FileSorter_SortingMethod_fullPath_displayName()), // Sort alphabetically by path
Bundle.FileSorter_SortingMethod_fullPath_displayName()), // Sort alphabetically by path
BY_DOMAIN_NAME(new ArrayList<>(),
Bundle.FileSorter_SortingMethod_domain_displayName());
@ -349,12 +349,22 @@ public class ResultsSorter implements Comparator<Result> {
}
/**
* Get the list of enums that are valid for ordering images.
* Get the list of enums that are valid for ordering files.
*
* @return enums that can be used to ordering images.
* @return Enums that can be used to ordering files.
*/
public static List<SortingMethod> getOptionsForOrdering() {
public static List<SortingMethod> getOptionsForOrderingFiles() {
return Arrays.asList(BY_FILE_SIZE, BY_FULL_PATH, BY_FILE_NAME, BY_DATA_SOURCE);
}
/**
* Get the list of enums that are valid for ordering files.
*
* @return Enums that can be used to ordering files.
*/
public static List<SortingMethod> getOptionsForOrderingDomains() {
return Arrays.asList(BY_DOMAIN_NAME, BY_DATA_SOURCE);
}
}
}

View File

@ -76,7 +76,7 @@ class SearchResults {
for (Result result : results) {
// Add the file to the appropriate group, creating it if necessary
GroupKey groupKey = attrType.getGroupKey(result);
if (!groupMap.containsKey(groupKey)) {
groupMap.put(groupKey, new Group(groupSortingType, groupKey));
}

View File

@ -32,6 +32,9 @@ import javax.swing.JSplitPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes.GroupingAttributeType;
import org.sleuthkit.autopsy.discovery.search.Group;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter.SortingMethod;
import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
@ -55,6 +58,9 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
private final JPanel secondColumnPanel = new JPanel();
private int firstColumnY = 0;
private int secondColumnY = 0;
private SortingMethod lastSortingMethod = SortingMethod.BY_FILE_NAME;
private GroupingAttributeType lastGroupingAttributeType = GroupingAttributeType.PARENT_PATH;
private Group.GroupSortingAlgorithm lastGroupSortingAlg = Group.GroupSortingAlgorithm.BY_GROUP_SIZE;
/**
* Setup necessary for implementations of this abstract class.
@ -64,6 +70,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
secondColumnPanel.setLayout(new GridBagLayout());
}
/**
* Get the type of results this filters panel is for.
*
@ -120,7 +127,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
secondColumnY += constraints.gridheight;
}
}
/**
* Add the panels representing the two columns to the specified JSplitPane.
*
@ -248,12 +255,9 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
synchronized List<AbstractFilter> getFilters() {
List<AbstractFilter> filtersToUse = new ArrayList<>();
if (getType().equals(SearchData.Type.DOMAIN)) {
} else {
if (getType() != SearchData.Type.DOMAIN) { //Domain type does not have a file type
filtersToUse.add(new SearchFiltering.FileTypeFilter(getType()));
}
for (AbstractDiscoveryFilterPanel filterPanel : filters) {
if (filterPanel.getCheckbox().isSelected()) {
AbstractFilter filter = filterPanel.getFilter();
@ -274,4 +278,46 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
}
}
/**
* @return the lastSortingMethod
*/
SortingMethod getLastSortingMethod() {
return lastSortingMethod;
}
/**
* @param lastSortingMethod the lastSortingMethod to set
*/
final void setLastSortingMethod(SortingMethod lastSortingMethod) {
this.lastSortingMethod = lastSortingMethod;
}
/**
* @return the lastGroupingAttributeType
*/
GroupingAttributeType getLastGroupingAttributeType() {
return lastGroupingAttributeType;
}
/**
* @param lastGroupingAttributeType the lastGroupingAttributeType to set
*/
final void setLastGroupingAttributeType(GroupingAttributeType lastGroupingAttributeType) {
this.lastGroupingAttributeType = lastGroupingAttributeType;
}
/**
* @return the lastGroupSortingAlg
*/
Group.GroupSortingAlgorithm getLastGroupSortingAlg() {
return lastGroupSortingAlg;
}
/**
* @param lastGroupSortingAlg the lastGroupSortingAlg to set
*/
final void setLastGroupSortingAlg(Group.GroupSortingAlgorithm lastGroupSortingAlg) {
this.lastGroupSortingAlg = lastGroupSortingAlg;
}
}

View File

@ -8,6 +8,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="ArtifactTypeFilterPanel.artifactTypeCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="artifactTypeCheckboxActionPerformed"/>
</Events>
</Component>
</NonVisualComponents>
<Properties>
@ -49,7 +52,7 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="jList1">
<Component class="javax.swing.JList" name="artifactList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultListModel&lt;ArtifactTypeItem&gt;()" type="code"/>

View File

@ -18,12 +18,16 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import java.util.List;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
@ -47,7 +51,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
*/
private void setUpArtifactTypeFilter() {
int count = 0;
DefaultListModel<ArtifactTypeItem> artifactTypeModel = (DefaultListModel<ArtifactTypeItem>) jList1.getModel();
DefaultListModel<ArtifactTypeItem> artifactTypeModel = (DefaultListModel<ArtifactTypeItem>) artifactList.getModel();
artifactTypeModel.removeAllElements();
for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SearchData.Type.DOMAIN.getArtifactTypes()) {
artifactTypeModel.add(count, new ArtifactTypeItem(artifactType));
@ -66,17 +70,22 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
artifactTypeCheckbox = new javax.swing.JCheckBox();
artifactTypeScrollPane = new javax.swing.JScrollPane();
jList1 = new javax.swing.JList<>();
artifactList = new javax.swing.JList<>();
org.openide.awt.Mnemonics.setLocalizedText(artifactTypeCheckbox, org.openide.util.NbBundle.getMessage(ArtifactTypeFilterPanel.class, "ArtifactTypeFilterPanel.artifactTypeCheckbox.text")); // NOI18N
artifactTypeCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
artifactTypeCheckboxActionPerformed(evt);
}
});
setPreferredSize(new java.awt.Dimension(27, 27));
artifactTypeScrollPane.setPreferredSize(new java.awt.Dimension(27, 27));
jList1.setModel(new DefaultListModel<ArtifactTypeItem>());
jList1.setEnabled(false);
artifactTypeScrollPane.setViewportView(jList1);
artifactList.setModel(new DefaultListModel<ArtifactTypeItem>());
artifactList.setEnabled(false);
artifactTypeScrollPane.setViewportView(artifactList);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
@ -90,9 +99,24 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
);
}// </editor-fold>//GEN-END:initComponents
private void artifactTypeCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_artifactTypeCheckboxActionPerformed
artifactTypeScrollPane.setEnabled(artifactTypeCheckbox.isSelected());
artifactList.setEnabled(artifactTypeCheckbox.isSelected());
}//GEN-LAST:event_artifactTypeCheckboxActionPerformed
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
artifactTypeCheckbox.setSelected(selected);
if (artifactTypeCheckbox.isEnabled() && artifactTypeCheckbox.isSelected()) {
artifactTypeScrollPane.setEnabled(true);
artifactList.setEnabled(true);
if (indicesSelected != null) {
artifactList.setSelectedIndices(indicesSelected);
}
} else {
artifactTypeScrollPane.setEnabled(false);
artifactList.setEnabled(false);
}
}
@Override
@ -102,7 +126,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
@Override
JList<?> getList() {
return null;
return artifactList;
}
@Override
@ -110,13 +134,24 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
return null;
}
@NbBundle.Messages({"ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected."})
@Override
String checkForError() {
return "Domain search is not implemented.";
if (artifactTypeCheckbox.isSelected() && artifactList.getSelectedValuesList().isEmpty()) {
return Bundle.ArtifactTypeFilterPanel_selectionNeeded_text();
}
return "";
}
@Override
AbstractFilter getFilter() {
if (artifactTypeCheckbox.isSelected() && !artifactList.getSelectedValuesList().isEmpty()) {
List<BlackboardArtifact.ARTIFACT_TYPE> artifactTypeList = new ArrayList<>();
for (ArtifactTypeItem item : artifactList.getSelectedValuesList()) {
artifactTypeList.add(item.getArtifactType());
}
return new ArtifactTypeFilter(artifactTypeList);
}
return null;
}
@ -153,8 +188,8 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JList<ArtifactTypeItem> artifactList;
private javax.swing.JCheckBox artifactTypeCheckbox;
private javax.swing.JScrollPane artifactTypeScrollPane;
private javax.swing.JList<ArtifactTypeItem> jList1;
// End of variables declaration//GEN-END:variables
}

View File

@ -29,17 +29,15 @@ ResultsSplitPaneDivider.showButton.text=
ResultsSplitPaneDivider.hideButton.text=
ImageFilterPanel.imageFiltersSplitPane.toolTipText=
ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show
ArtifactTypeFilterPanel.artifactTypeCheckbox.text=Artifact Type:
ArtifactTypeFilterPanel.artifactTypeCheckbox.text=Result Type:
InterestingItemsFilterPanel.interestingItemsCheckbox.text=Interesting Item:
DocumentPanel.fileSizeLabel.toolTipText=
DocumentPanel.isDeletedLabel.toolTipText=
DomainFilterPanel.domainFiltersSplitPane.toolTipText=
DomainFilterPanel.domainFiltersSplitPane.border.title=Step 2: Filter which domains to show
SizeFilterPanel.sizeCheckbox.text=File Size:
DateFilterPanel.dateFilterCheckbox.text=Date Filter:
DateFilterPanel.endCheckBox.text=End:
DateFilterPanel.startCheckBox.text=Start:
DateFilterPanel.mostRecentButton.text=Only last:
DateFilterPanel.daysLabel.text=days of activity
ImageThumbnailPanel.isDeletedLabel.toolTipText=
ResultsPanel.pageControlsLabel.text=Pages:
@ -54,3 +52,5 @@ PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
ObjectDetectedFilterPanel.text=Object Detected:
DetailsPanel.instancesList.border.title=Instances
DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter:

View File

@ -83,17 +83,15 @@ ResultsSplitPaneDivider.showButton.text=
ResultsSplitPaneDivider.hideButton.text=
ImageFilterPanel.imageFiltersSplitPane.toolTipText=
ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show
ArtifactTypeFilterPanel.artifactTypeCheckbox.text=Artifact Type:
ArtifactTypeFilterPanel.artifactTypeCheckbox.text=Result Type:
InterestingItemsFilterPanel.interestingItemsCheckbox.text=Interesting Item:
DocumentPanel.fileSizeLabel.toolTipText=
DocumentPanel.isDeletedLabel.toolTipText=
DomainFilterPanel.domainFiltersSplitPane.toolTipText=
DomainFilterPanel.domainFiltersSplitPane.border.title=Step 2: Filter which domains to show
SizeFilterPanel.sizeCheckbox.text=File Size:
DateFilterPanel.dateFilterCheckbox.text=Date Filter:
DateFilterPanel.endCheckBox.text=End:
DateFilterPanel.startCheckBox.text=Start:
DateFilterPanel.mostRecentButton.text=Only last:
DateFilterPanel.daysLabel.text=days of activity
ImageThumbnailPanel.isDeletedLabel.toolTipText=
ResultsPanel.pageControlsLabel.text=Pages:
@ -108,6 +106,8 @@ PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
ObjectDetectedFilterPanel.text=Object Detected:
DetailsPanel.instancesList.border.title=Instances
DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter:
VideoThumbnailPanel.bytes.text=bytes
VideoThumbnailPanel.deleted.text=All instances of file are deleted.
VideoThumbnailPanel.gigaBytes.text=GB

View File

@ -4,12 +4,15 @@
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="buttonGroup1">
</Component>
<Component class="javax.swing.JCheckBox" name="dateFilterCheckbox">
<Component class="javax.swing.JCheckBox" name="dateFilterCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DateFilterPanel.dateFilterCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DateFilterPanel.dateFilterCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="dateFilterCheckBoxActionPerformed"/>
</Events>
</Component>
</NonVisualComponents>
<AuxValues>
@ -51,7 +54,7 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="mostRecentButton" min="-2" pref="90" max="-2" attributes="0"/>
<Component id="mostRecentRadioButton" min="-2" pref="90" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="daysSpinner" min="-2" pref="80" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
@ -76,7 +79,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="mostRecentButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="mostRecentRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="daysSpinner" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="daysLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
@ -100,10 +103,16 @@
<SubComponents>
<Component class="javax.swing.JSpinner" name="daysSpinner">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="1" minimum="1" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[75, 26]"/>
</Property>
<Property name="value" type="java.lang.Object" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="7" type="code"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new javax.swing.JSpinner(numberModel)"/>
@ -117,18 +126,19 @@
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="mostRecentButton">
<Component class="javax.swing.JRadioButton" name="mostRecentRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup1"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DateFilterPanel.mostRecentButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DateFilterPanel.mostRecentRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="mostRecentButtonActionPerformed"/>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="mostRecentRadioButtonStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="startCheckBox">
@ -144,6 +154,9 @@
</Component>
<Component class="com.github.lgooddatepicker.components.DatePicker" name="startDatePicker">
<Properties>
<Property name="date" type="java.time.LocalDate" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="LocalDate.now()" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[60, 22]"/>
@ -155,6 +168,9 @@
</Component>
<Component class="com.github.lgooddatepicker.components.DatePicker" name="endDatePicker">
<Properties>
<Property name="date" type="java.time.LocalDate" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="LocalDate.now()" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[60, 22]"/>
@ -183,7 +199,7 @@
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="rangeRadioButtonActionPerformed"/>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="rangeRadioButtonStateChanged"/>
</Events>
</Component>
</SubComponents>

View File

@ -18,13 +18,19 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.awt.event.ActionListener;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.communications.Utils;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
/**
* Filter panel for allowing the user to filter on date.
@ -33,6 +39,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
private static final long serialVersionUID = 1L;
private final SpinnerNumberModel numberModel;
private static final long SECS_PER_DAY = 86400;
/**
* Creates new form DateFilterPanel.
@ -56,31 +63,39 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
private void initComponents() {
buttonGroup1 = new javax.swing.ButtonGroup();
dateFilterCheckbox = new javax.swing.JCheckBox();
dateFilterCheckBox = new javax.swing.JCheckBox();
jPanel1 = new javax.swing.JPanel();
daysSpinner = new javax.swing.JSpinner(numberModel);
daysLabel = new javax.swing.JLabel();
mostRecentButton = new javax.swing.JRadioButton();
mostRecentRadioButton = new javax.swing.JRadioButton();
startCheckBox = new javax.swing.JCheckBox();
startDatePicker = new com.github.lgooddatepicker.components.DatePicker();
endDatePicker = new com.github.lgooddatepicker.components.DatePicker();
endCheckBox = new javax.swing.JCheckBox();
rangeRadioButton = new javax.swing.JRadioButton();
org.openide.awt.Mnemonics.setLocalizedText(dateFilterCheckbox, org.openide.util.NbBundle.getMessage(DateFilterPanel.class, "DateFilterPanel.dateFilterCheckbox.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(dateFilterCheckBox, org.openide.util.NbBundle.getMessage(DateFilterPanel.class, "DateFilterPanel.dateFilterCheckBox.text")); // NOI18N
dateFilterCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
dateFilterCheckBoxActionPerformed(evt);
}
});
daysSpinner.setModel(new javax.swing.SpinnerNumberModel(1, 1, null, 1));
daysSpinner.setEnabled(false);
daysSpinner.setPreferredSize(new java.awt.Dimension(75, 26));
daysSpinner.setValue(7);
org.openide.awt.Mnemonics.setLocalizedText(daysLabel, org.openide.util.NbBundle.getMessage(DateFilterPanel.class, "DateFilterPanel.daysLabel.text")); // NOI18N
daysLabel.setEnabled(false);
buttonGroup1.add(mostRecentButton);
org.openide.awt.Mnemonics.setLocalizedText(mostRecentButton, org.openide.util.NbBundle.getMessage(DateFilterPanel.class, "DateFilterPanel.mostRecentButton.text")); // NOI18N
mostRecentButton.setEnabled(false);
mostRecentButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
mostRecentButtonActionPerformed(evt);
buttonGroup1.add(mostRecentRadioButton);
mostRecentRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(mostRecentRadioButton, org.openide.util.NbBundle.getMessage(DateFilterPanel.class, "DateFilterPanel.mostRecentRadioButton.text")); // NOI18N
mostRecentRadioButton.setEnabled(false);
mostRecentRadioButton.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
mostRecentRadioButtonStateChanged(evt);
}
});
@ -92,10 +107,12 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
}
});
startDatePicker.setDate(LocalDate.now());
startDatePicker.setEnabled(false);
startDatePicker.setMinimumSize(new java.awt.Dimension(60, 22));
startDatePicker.setPreferredSize(new java.awt.Dimension(110, 22));
endDatePicker.setDate(LocalDate.now());
endDatePicker.setEnabled(false);
endDatePicker.setMinimumSize(new java.awt.Dimension(60, 22));
endDatePicker.setPreferredSize(new java.awt.Dimension(110, 22));
@ -110,9 +127,9 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
buttonGroup1.add(rangeRadioButton);
rangeRadioButton.setEnabled(false);
rangeRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
rangeRadioButtonActionPerformed(evt);
rangeRadioButton.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
rangeRadioButtonStateChanged(evt);
}
});
@ -121,7 +138,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(mostRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(mostRecentRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(daysSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@ -141,7 +158,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(mostRecentButton)
.addComponent(mostRecentRadioButton)
.addComponent(daysSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(daysLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
@ -176,31 +193,48 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
}// </editor-fold>//GEN-END:initComponents
private void startCheckBoxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_startCheckBoxStateChanged
startDatePicker.setEnabled(startCheckBox.isSelected());
// validateFilters(); //TODO JIRA-6714 when search will begin doing something
startDatePicker.setEnabled(startCheckBox.isEnabled() && startCheckBox.isSelected());
}//GEN-LAST:event_startCheckBoxStateChanged
private void endCheckBoxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_endCheckBoxStateChanged
endDatePicker.setEnabled(endCheckBox.isSelected());
// validateFilters(); //TODO JIRA-6714 when search will begin doing something
endDatePicker.setEnabled(endCheckBox.isEnabled() && endCheckBox.isSelected());
}//GEN-LAST:event_endCheckBoxStateChanged
private void mostRecentButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mostRecentButtonActionPerformed
private void dateFilterCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dateFilterCheckBoxActionPerformed
rangeRadioButton.setEnabled(dateFilterCheckBox.isSelected());
mostRecentRadioButton.setEnabled(dateFilterCheckBox.isSelected());
rangeRadioButton.firePropertyChange("DateFilterChange", !rangeRadioButton.isEnabled(), rangeRadioButton.isEnabled());
mostRecentRadioButton.firePropertyChange("DateFilterChange", !mostRecentRadioButton.isEnabled(), mostRecentRadioButton.isEnabled());
}//GEN-LAST:event_dateFilterCheckBoxActionPerformed
}//GEN-LAST:event_mostRecentButtonActionPerformed
private void mostRecentRadioButtonStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_mostRecentRadioButtonStateChanged
daysSpinner.setEnabled(mostRecentRadioButton.isSelected());
daysLabel.setEnabled(mostRecentRadioButton.isSelected());
}//GEN-LAST:event_mostRecentRadioButtonStateChanged
private void rangeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rangeRadioButtonActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_rangeRadioButtonActionPerformed
private void rangeRadioButtonStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_rangeRadioButtonStateChanged
startCheckBox.setEnabled(rangeRadioButton.isEnabled() && rangeRadioButton.isSelected());
endCheckBox.setEnabled(rangeRadioButton.isEnabled() && rangeRadioButton.isSelected());
startCheckBox.firePropertyChange("StartButtonChange", true, false);
endCheckBox.firePropertyChange("EndButtonChange", true, false);
}//GEN-LAST:event_rangeRadioButtonStateChanged
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
dateFilterCheckbox.setSelected(selected);
dateFilterCheckBox.setSelected(selected);
if (dateFilterCheckBox.isEnabled() && dateFilterCheckBox.isSelected()) {
mostRecentRadioButton.setEnabled(true);
rangeRadioButton.setEnabled(true);
mostRecentRadioButton.setSelected(true);
} else {
mostRecentRadioButton.setEnabled(false);
rangeRadioButton.setEnabled(false);
}
}
@Override
JCheckBox getCheckbox() {
return dateFilterCheckbox;
return dateFilterCheckBox;
}
@Override
@ -213,26 +247,81 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
return null;
}
@Override
void addListeners(ActionListener actionListener, ListSelectionListener listListener) {
dateFilterCheckBox.addActionListener(actionListener);
startCheckBox.addActionListener(actionListener);
endCheckBox.addActionListener(actionListener);
rangeRadioButton.addActionListener(actionListener);
mostRecentRadioButton.addActionListener(actionListener);
}
@Override
void removeListeners() {
for (ActionListener listener : dateFilterCheckBox.getActionListeners()) {
dateFilterCheckBox.removeActionListener(listener);
}
for (ActionListener listener : rangeRadioButton.getActionListeners()) {
rangeRadioButton.removeActionListener(listener);
}
for (ActionListener listener : mostRecentRadioButton.getActionListeners()) {
mostRecentRadioButton.removeActionListener(listener);
}
for (ActionListener listener : rangeRadioButton.getActionListeners()) {
rangeRadioButton.removeActionListener(listener);
}
for (ActionListener listener : startCheckBox.getActionListeners()) {
startCheckBox.removeActionListener(listener);
}
for (ActionListener listener : endCheckBox.getActionListeners()) {
endCheckBox.removeActionListener(listener);
}
}
@NbBundle.Messages({"DateFilterPanel.invalidRange.text=Range or Only Last must be selected",
"DateFilterPanel.startOrEndNeeded.text=A start or end date must be specified to use the range filter"})
@Override
String checkForError() {
return "Domain search is not implemented.";
if (dateFilterCheckBox.isSelected()) {
if (!(rangeRadioButton.isSelected() || mostRecentRadioButton.isSelected())) {
return Bundle.DateFilterPanel_invalidRange_text();
} else if (rangeRadioButton.isSelected() && !(startCheckBox.isSelected() || endCheckBox.isSelected())) {
return Bundle.DateFilterPanel_startOrEndNeeded_text();
}
}
return "";
}
@Override
AbstractFilter getFilter() {
if (dateFilterCheckBox.isSelected()) {
LocalDate startDate = LocalDate.MIN;
LocalDate endDate = LocalDate.MAX;
ZoneId zone = Utils.getUserPreferredZoneId();
if (rangeRadioButton.isSelected() && (startCheckBox.isSelected() || endCheckBox.isSelected())) {
if (startCheckBox.isSelected() && startDatePicker.getDate() != null) {
startDate = startDatePicker.getDate();
}
if (endCheckBox.isSelected() && endDatePicker.getDate() != null) {
endDate = endDatePicker.getDate();
}
} else if (dateFilterCheckBox.isSelected() && mostRecentRadioButton.isSelected()) {
endDate = LocalDate.now();
startDate = LocalDate.now().minus(Period.ofDays((Integer) daysSpinner.getValue()));
}
return new SearchFiltering.ArtifactDateRangeFilter(startDate.atStartOfDay(zone).toEpochSecond(), endDate.atStartOfDay(zone).toEpochSecond() + SECS_PER_DAY);//to insure end date is inclusive
}
return null;
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JCheckBox dateFilterCheckbox;
private javax.swing.JCheckBox dateFilterCheckBox;
private javax.swing.JLabel daysLabel;
private javax.swing.JSpinner daysSpinner;
private javax.swing.JCheckBox endCheckBox;
private com.github.lgooddatepicker.components.DatePicker endDatePicker;
private javax.swing.JPanel jPanel1;
private javax.swing.JRadioButton mostRecentButton;
private javax.swing.JRadioButton mostRecentRadioButton;
private javax.swing.JRadioButton rangeRadioButton;
private javax.swing.JCheckBox startCheckBox;
private com.github.lgooddatepicker.components.DatePicker startDatePicker;

View File

@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import static java.awt.BorderLayout.CENTER;
import java.awt.Color;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
@ -29,6 +31,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
@ -41,12 +44,9 @@ import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.Group;
import org.sleuthkit.autopsy.discovery.search.Group.GroupSortingAlgorithm;
import static org.sleuthkit.autopsy.discovery.search.Group.GroupSortingAlgorithm.BY_GROUP_SIZE;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes.GroupingAttributeType;
import static org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes.GroupingAttributeType.PARENT_PATH;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter.SortingMethod;
import static org.sleuthkit.autopsy.discovery.search.ResultsSorter.SortingMethod.BY_FILE_NAME;
import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
@ -111,12 +111,41 @@ final class DiscoveryDialog extends javax.swing.JDialog {
}
}
};
for (GroupSortingAlgorithm groupSortAlgorithm : GroupSortingAlgorithm.values()) {
groupSortingComboBox.addItem(groupSortAlgorithm);
}
updateSearchSettings();
groupByCombobox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) {
SwingUtilities.invokeLater(() -> {
getSelectedFilterPanel().setLastGroupingAttributeType(groupByCombobox.getItemAt(groupByCombobox.getSelectedIndex()));
});
}
}
});
orderByCombobox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) {
SwingUtilities.invokeLater(() -> {
getSelectedFilterPanel().setLastSortingMethod(orderByCombobox.getItemAt(orderByCombobox.getSelectedIndex()));
});
}
}
});
groupSortingComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) {
SwingUtilities.invokeLater(() -> {
getSelectedFilterPanel().setLastGroupSortingAlg(groupSortingComboBox.getItemAt(groupSortingComboBox.getSelectedIndex()));
});
}
}
});
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this.new CasePropertyChangeListener());
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, this.new ModuleChangeListener());
}
/**
@ -140,7 +169,6 @@ final class DiscoveryDialog extends javax.swing.JDialog {
add(imageFilterPanel, CENTER);
imageFilterPanel.addPropertyChangeListener(listener);
updateComboBoxes();
groupSortingComboBox.setSelectedItem(BY_GROUP_SIZE);
pack();
repaint();
}
@ -167,20 +195,54 @@ final class DiscoveryDialog extends javax.swing.JDialog {
* Private helper method to perform update of comboboxes update.
*/
private void updateComboBoxes() {
groupByCombobox.removeAllItems();
// Set up the grouping attributes
for (DiscoveryAttributes.GroupingAttributeType groupingType : DiscoveryAttributes.GroupingAttributeType.getOptionsForGrouping()) {
List<GroupingAttributeType> groupingAttrs = new ArrayList<>();
List<SortingMethod> sortingMethods = new ArrayList<>();
groupByCombobox.removeAllItems();
if (type == SearchData.Type.DOMAIN) {
groupingAttrs.addAll(GroupingAttributeType.getOptionsForGroupingForDomains());
sortingMethods.addAll(SortingMethod.getOptionsForOrderingDomains());
} else {
groupingAttrs.addAll(GroupingAttributeType.getOptionsForGroupingForFiles());
sortingMethods.addAll(SortingMethod.getOptionsForOrderingFiles());
}
for (GroupingAttributeType groupingType : groupingAttrs) {
addTypeToGroupByComboBox(groupingType);
}
groupByCombobox.setSelectedItem(PARENT_PATH);
groupByCombobox.setSelectedItem(getSelectedFilterPanel().getLastGroupingAttributeType());
orderByCombobox.removeAllItems();
// Set up the file order list
for (ResultsSorter.SortingMethod method : ResultsSorter.SortingMethod.getOptionsForOrdering()) {
for (SortingMethod method : sortingMethods) {
if (method != SortingMethod.BY_FREQUENCY || CentralRepository.isEnabled()) {
orderByCombobox.addItem(method);
}
}
orderByCombobox.setSelectedItem(BY_FILE_NAME);
orderByCombobox.setSelectedItem(getSelectedFilterPanel().getLastSortingMethod());
groupSortingComboBox.removeAllItems();
for (GroupSortingAlgorithm groupSortAlgorithm : GroupSortingAlgorithm.values()) {
groupSortingComboBox.addItem(groupSortAlgorithm);
}
groupSortingComboBox.setSelectedItem(getSelectedFilterPanel().getLastGroupSortingAlg());
}
/**
* Private helper method to get the correct panel for the selected type.
*
* @return The panel that corresponds to the currently selected type.
*/
private AbstractFiltersPanel getSelectedFilterPanel() {
switch (type) {
case IMAGE:
return imageFilterPanel;
case VIDEO:
return videoFilterPanel;
case DOCUMENT:
return documentFilterPanel;
case DOMAIN:
return domainFilterPanel;
default:
return imageFilterPanel;
}
}
/**
@ -221,29 +283,9 @@ final class DiscoveryDialog extends javax.swing.JDialog {
* Validate the filter settings for File type filters.
*/
synchronized void validateDialog() {
switch (type) {
case IMAGE:
if (imageFilterPanel != null) {
imageFilterPanel.validateFields();
}
break;
case VIDEO:
if (videoFilterPanel != null) {
videoFilterPanel.validateFields();
}
break;
case DOCUMENT:
if (documentFilterPanel != null) {
documentFilterPanel.validateFields();
}
break;
case DOMAIN:
if (domainFilterPanel != null) {
domainFilterPanel.validateFields();
}
break;
default:
break;
AbstractFiltersPanel panel = getSelectedFilterPanel();
if (panel != null) {
panel.validateFields();
}
}
@ -469,6 +511,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
imagesButton.setForeground(Color.BLACK);
type = SearchData.Type.IMAGE;
imageFilterPanel.addPropertyChangeListener(listener);
updateComboBoxes();
validateDialog();
pack();
repaint();
@ -484,6 +527,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
videosButton.setForeground(Color.BLACK);
videoFilterPanel.addPropertyChangeListener(listener);
type = SearchData.Type.VIDEO;
updateComboBoxes();
validateDialog();
pack();
repaint();
@ -499,6 +543,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
documentsButton.setForeground(Color.BLACK);
type = SearchData.Type.DOCUMENT;
documentFilterPanel.addPropertyChangeListener(listener);
updateComboBoxes();
validateDialog();
pack();
repaint();
@ -512,6 +557,10 @@ final class DiscoveryDialog extends javax.swing.JDialog {
remove(imageFilterPanel);
imageFilterPanel.removePropertyChangeListener(listener);
}
if (domainFilterPanel != null) {
remove(domainFilterPanel);
domainFilterPanel.removePropertyChangeListener(listener);
}
if (documentFilterPanel != null) {
remove(documentFilterPanel);
documentFilterPanel.removePropertyChangeListener(listener);
@ -520,10 +569,6 @@ final class DiscoveryDialog extends javax.swing.JDialog {
remove(videoFilterPanel);
videoFilterPanel.removePropertyChangeListener(listener);
}
if (domainFilterPanel != null) {
remove(domainFilterPanel);
domainFilterPanel.removePropertyChangeListener(listener);
}
}
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
@ -584,7 +629,8 @@ final class DiscoveryDialog extends javax.swing.JDialog {
domainsButton.setBackground(SELECTED_COLOR);
domainsButton.setForeground(Color.BLACK);
type = SearchData.Type.DOMAIN;
documentFilterPanel.addPropertyChangeListener(listener);
domainFilterPanel.addPropertyChangeListener(listener);
updateComboBoxes();
validateDialog();
pack();
repaint();

View File

@ -19,6 +19,8 @@
package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/**
@ -44,6 +46,8 @@ public class DomainFilterPanel extends AbstractFiltersPanel {
}
addFilter(new PastOccurrencesFilterPanel(TYPE), true, pastOccurrencesIndices, 0);
addPanelsToScrollPane(domainFiltersSplitPane);
setLastGroupingAttributeType(DiscoveryAttributes.GroupingAttributeType.MOST_RECENT_DATE);
setLastSortingMethod(ResultsSorter.SortingMethod.BY_DOMAIN_NAME);
}
/**