mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
Merge branch 'develop' of github.com:sleuthkit/autopsy into 6332-annotationViewer
This commit is contained in:
commit
3dce7f528b
@ -1563,11 +1563,12 @@ public class Case {
|
||||
*
|
||||
* This should not be called from the event dispatch thread (EDT)
|
||||
*
|
||||
* @param newTag new ContentTag added
|
||||
* @param deletedTag Removed ContentTag
|
||||
* @param newTag The added ContentTag.
|
||||
* @param deletedTagList List of ContentTags that were removed as a result
|
||||
* of the addition of newTag.
|
||||
*/
|
||||
public void notifyContentTagAdded(ContentTag newTag, ContentTag deletedTag) {
|
||||
eventPublisher.publish(new ContentTagAddedEvent(newTag, deletedTag));
|
||||
public void notifyContentTagAdded(ContentTag newTag, List<ContentTag> deletedTagList) {
|
||||
eventPublisher.publish(new ContentTagAddedEvent(newTag, deletedTagList));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1627,11 +1628,12 @@ public class Case {
|
||||
*
|
||||
* This should not be called from the event dispatch thread (EDT)
|
||||
*
|
||||
* @param newTag new BlackboardArtifactTag added
|
||||
* @param removedTag The BlackboardArtifactTag that was removed.
|
||||
* @param newTag The added ContentTag.
|
||||
* @param removedTagList List of ContentTags that were removed as a result
|
||||
* of the addition of newTag.
|
||||
*/
|
||||
public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag, BlackboardArtifactTag removedTag) {
|
||||
eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag, removedTag));
|
||||
public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag, List<BlackboardArtifactTag> removedTagList) {
|
||||
eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag, removedTagList));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,6 +19,8 @@
|
||||
package org.sleuthkit.autopsy.casemodule.events;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
@ -30,7 +32,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* Event sent when a black board artifact tag is added.
|
||||
*/
|
||||
@Immutable
|
||||
public class BlackBoardArtifactTagAddedEvent extends TagAddedEvent<BlackboardArtifactTag> implements Serializable {
|
||||
public class BlackBoardArtifactTagAddedEvent extends TagAddedEvent<BlackboardArtifactTag, DeletedBlackboardArtifactTagInfo> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -38,8 +40,8 @@ public class BlackBoardArtifactTagAddedEvent extends TagAddedEvent<BlackboardArt
|
||||
super(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString(), newTag);
|
||||
}
|
||||
|
||||
public BlackBoardArtifactTagAddedEvent(BlackboardArtifactTag newTag, BlackboardArtifactTag removedTag) {
|
||||
super(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString(), newTag, (removedTag != null ? new DeletedBlackboardArtifactTagInfo(removedTag) : null));
|
||||
public BlackBoardArtifactTagAddedEvent(BlackboardArtifactTag newTag, List<BlackboardArtifactTag> removedTagList) {
|
||||
super(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString(), newTag, (removedTagList != null ? getDeletedInfo(removedTagList) : null));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,4 +56,24 @@ public class BlackBoardArtifactTagAddedEvent extends TagAddedEvent<BlackboardArt
|
||||
BlackboardArtifactTag getTagByID() throws NoCurrentCaseException, TskCoreException {
|
||||
return Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagByTagID(getTagID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of DeletedContentTagInfo objects from a list of
|
||||
* BlackboardArtifactTags.
|
||||
*
|
||||
* @param deletedTagList List of deleted ContentTags.
|
||||
*
|
||||
* @return List of DeletedContentTagInfo objects or empty list if
|
||||
* deletedTagList was empty or null.
|
||||
*/
|
||||
private static List<DeletedBlackboardArtifactTagInfo> getDeletedInfo(List<BlackboardArtifactTag> deletedTagList) {
|
||||
List<DeletedBlackboardArtifactTagInfo> deletedInfoList = new ArrayList<>();
|
||||
if (deletedTagList != null) {
|
||||
for (BlackboardArtifactTag tag : deletedTagList) {
|
||||
deletedInfoList.add(new DeletedBlackboardArtifactTagInfo(tag));
|
||||
}
|
||||
}
|
||||
|
||||
return deletedInfoList;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
package org.sleuthkit.autopsy.casemodule.events;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
@ -30,7 +32,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* An event that is fired when a ContentTag is added.
|
||||
*/
|
||||
@Immutable
|
||||
public class ContentTagAddedEvent extends TagAddedEvent<ContentTag> implements Serializable {
|
||||
public class ContentTagAddedEvent extends TagAddedEvent<ContentTag, DeletedContentTagInfo> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -38,8 +40,8 @@ public class ContentTagAddedEvent extends TagAddedEvent<ContentTag> implements S
|
||||
super(Case.Events.CONTENT_TAG_ADDED.toString(), newTag);
|
||||
}
|
||||
|
||||
public ContentTagAddedEvent(ContentTag newTag, ContentTag deletedTag) {
|
||||
super(Case.Events.CONTENT_TAG_ADDED.toString(), newTag, (deletedTag != null ? new DeletedContentTagInfo(deletedTag) : null));
|
||||
public ContentTagAddedEvent(ContentTag newTag, List<ContentTag> deletedTagList) {
|
||||
super(Case.Events.CONTENT_TAG_ADDED.toString(), newTag, getDeletedInfo(deletedTagList));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,7 +52,26 @@ public class ContentTagAddedEvent extends TagAddedEvent<ContentTag> implements S
|
||||
* @throws NoCurrentCaseException
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
@Override
|
||||
ContentTag getTagByID() throws NoCurrentCaseException, TskCoreException {
|
||||
return Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagByTagID(getTagID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of DeletedContentTagInfo objects from a list of ContentTags.
|
||||
*
|
||||
* @param deletedTagList List of deleted ContentTags.
|
||||
*
|
||||
* @return List of DeletedContentTagInfo objects or empty list if deletedTagList was empty or null.
|
||||
*/
|
||||
private static List<DeletedContentTagInfo> getDeletedInfo(List<ContentTag> deletedTagList) {
|
||||
List<DeletedContentTagInfo> deletedInfoList = new ArrayList<>();
|
||||
if (deletedTagList != null) {
|
||||
for (ContentTag tag : deletedTagList) {
|
||||
deletedInfoList.add(new DeletedContentTagInfo(tag));
|
||||
}
|
||||
}
|
||||
|
||||
return deletedInfoList;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
package org.sleuthkit.autopsy.casemodule.events;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.events.TagDeletedEvent.DeletedTagInfo;
|
||||
@ -30,7 +32,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
/**
|
||||
* Base Class for events that are fired when a Tag is added
|
||||
*/
|
||||
abstract class TagAddedEvent<T extends Tag> extends AutopsyEvent implements Serializable {
|
||||
abstract class TagAddedEvent<T extends Tag, V extends DeletedTagInfo<T>> extends AutopsyEvent implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -40,6 +42,8 @@ abstract class TagAddedEvent<T extends Tag> extends AutopsyEvent implements Seri
|
||||
*/
|
||||
private transient T tag;
|
||||
|
||||
private List<V> deletedTagInfoList;
|
||||
|
||||
/**
|
||||
* The id of the tag that was added. This will be used to re-load the
|
||||
* transient tag from the database.
|
||||
@ -50,10 +54,19 @@ abstract class TagAddedEvent<T extends Tag> extends AutopsyEvent implements Seri
|
||||
this(propertyName, addedTag, null);
|
||||
}
|
||||
|
||||
TagAddedEvent(String propertyName, T addedTag, DeletedTagInfo<T> deletedTagInfo) {
|
||||
super(propertyName, deletedTagInfo, null);
|
||||
/**
|
||||
* Construct a TagAddedEvent.
|
||||
*
|
||||
* @param propertyName Name of property changing
|
||||
* @param addedTag Instance of added tag.
|
||||
* @param deletedTagInfoList List of tags deleted as a result of the
|
||||
* addition of addedTag.
|
||||
*/
|
||||
TagAddedEvent(String propertyName, T addedTag, List<V> deletedTagInfoList) {
|
||||
super(propertyName, deletedTagInfoList, null);
|
||||
tag = addedTag;
|
||||
tagID = addedTag.getId();
|
||||
this.deletedTagInfoList = deletedTagInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,6 +109,21 @@ abstract class TagAddedEvent<T extends Tag> extends AutopsyEvent implements Seri
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of tags that were removed as a result of the addition
|
||||
* of the T.
|
||||
*
|
||||
* @return A list of removed tags or null if no tags were removed.
|
||||
*/
|
||||
public List<V> getDeletedTags() {
|
||||
return deletedTagInfoList != null ? Collections.unmodifiableList(deletedTagInfoList) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOldValue() {
|
||||
return getDeletedTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* implementors should override this to lookup the appropriate kind of tag
|
||||
* (Content/BlackBoardArtifact) during the lazy load of the transient tag
|
||||
|
@ -80,7 +80,7 @@ final class TagNameDefinition implements Comparable<TagNameDefinition> {
|
||||
PROJECT_VIC_TAG_DEFINITIONS.put(CATEGORY_TWO_NAME, new TagNameDefinition(CATEGORY_TWO_NAME, "", TagName.HTML_COLOR.LIME, TskData.FileKnown.BAD));
|
||||
PROJECT_VIC_TAG_DEFINITIONS.put(CATEGORY_THREE_NAME, new TagNameDefinition(CATEGORY_THREE_NAME, "", TagName.HTML_COLOR.YELLOW, TskData.FileKnown.BAD));
|
||||
PROJECT_VIC_TAG_DEFINITIONS.put(CATEGORY_FOUR_NAME, new TagNameDefinition(CATEGORY_FOUR_NAME, "", TagName.HTML_COLOR.PURPLE, TskData.FileKnown.UNKNOWN));
|
||||
PROJECT_VIC_TAG_DEFINITIONS.put(CATEGORY_FIVE_NAME, new TagNameDefinition(CATEGORY_FIVE_NAME, "", TagName.HTML_COLOR.SILVER, TskData.FileKnown.UNKNOWN));
|
||||
PROJECT_VIC_TAG_DEFINITIONS.put(CATEGORY_FIVE_NAME, new TagNameDefinition(CATEGORY_FIVE_NAME, "", TagName.HTML_COLOR.FUCHSIA, TskData.FileKnown.UNKNOWN));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -481,7 +481,7 @@ public class TagsManager implements Closeable {
|
||||
try {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
|
||||
currentCase.notifyContentTagAdded(tagChange.getAddedTag(), tagChange.getRemovedTags().isEmpty() ? null : tagChange.getRemovedTags().get(0));
|
||||
currentCase.notifyContentTagAdded(tagChange.getAddedTag(), tagChange.getRemovedTags().isEmpty() ? null : tagChange.getRemovedTags());
|
||||
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new TskCoreException("Added a tag to a closed case", ex);
|
||||
@ -701,7 +701,7 @@ public class TagsManager implements Closeable {
|
||||
TaggingManager.BlackboardArtifactTagChange tagChange = caseDb.getTaggingManager().addArtifactTag(artifact, tagName, comment);
|
||||
try {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
currentCase.notifyBlackBoardArtifactTagAdded(tagChange.getAddedTag(), tagChange.getRemovedTags().isEmpty() ? null : tagChange.getRemovedTags().get(0));
|
||||
currentCase.notifyBlackBoardArtifactTagAdded(tagChange.getAddedTag(), tagChange.getRemovedTags().isEmpty() ? null : tagChange.getRemovedTags());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new TskCoreException("Added a tag to a closed case", ex);
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
|
||||
@ -62,6 +63,11 @@ import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT;
|
||||
import org.sleuthkit.datamodel.CommunicationsUtils;
|
||||
|
||||
/**
|
||||
* Listen for ingest events and update entries in the Central Repository
|
||||
@ -337,6 +343,94 @@ public class IngestEventsListener {
|
||||
event = evt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically creates personas from all the TSK_CONTACT artifacts
|
||||
* found in a data source.
|
||||
*
|
||||
* @param dataSource Data source that was just analyzed.
|
||||
* @throws TskCoreException If there is any error getting contact
|
||||
* artifacts from case database.
|
||||
* @throws CentralRepoException If there is an error in creating
|
||||
* personas in the Central Repo.
|
||||
*/
|
||||
private void autoGenerateContactPersonas(Content dataSource) throws TskCoreException, CentralRepoException {
|
||||
|
||||
Blackboard blackboard;
|
||||
try {
|
||||
blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// get all TSK_CONTACT artifacts in this data source.
|
||||
List<BlackboardArtifact> contactArtifacts = blackboard.getArtifacts(TSK_CONTACT.getTypeID(), dataSource.getId());
|
||||
for (BlackboardArtifact artifact : contactArtifacts) {
|
||||
|
||||
BlackboardAttribute nameAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME));
|
||||
String personaName = (nameAttr != null) ? nameAttr.getValueString() : null;
|
||||
|
||||
// Get phone number and email attributes.
|
||||
BlackboardAttribute phoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER));
|
||||
BlackboardAttribute homePhoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME));
|
||||
BlackboardAttribute mobilePhoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE));
|
||||
BlackboardAttribute emailAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL));
|
||||
|
||||
Persona persona = personaFromContactAttribute(null, Account.Type.PHONE, phoneAttr, personaName);
|
||||
persona = personaFromContactAttribute(persona, Account.Type.PHONE, homePhoneAttr, personaName);
|
||||
persona = personaFromContactAttribute(persona, Account.Type.PHONE, mobilePhoneAttr, personaName);
|
||||
personaFromContactAttribute(persona, Account.Type.EMAIL, emailAttr, personaName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets central repo account for the given attribute for a TSK_CONTACT
|
||||
* artifact. Associates the given persona with that account. Creates a
|
||||
* Persona, if one isn't provided.
|
||||
*
|
||||
* @param persona Persona to associate with the account. May be null, in
|
||||
* which case a persona is created first.
|
||||
* @param accountType Account type of account to be associated.
|
||||
* @param attribute Attribute form which get the account id.
|
||||
* @param personaName Persona name, if a persona needs to be created.
|
||||
* @return Persona created or associated with the account.
|
||||
*
|
||||
* @throws TskCoreException If there is an error in normalizing the
|
||||
* account id.
|
||||
* @throws CentralRepoException If there is an erorr is getting the
|
||||
* account or associating the persona with it.
|
||||
*/
|
||||
private Persona personaFromContactAttribute(Persona persona, Account.Type accountType, BlackboardAttribute attribute, String personaName) throws CentralRepoException, TskCoreException {
|
||||
|
||||
Persona personaToReturn = persona;
|
||||
if (attribute != null) {
|
||||
|
||||
String accountId = attribute.getValueString();
|
||||
if (CommunicationsUtils.isValidAccountId(accountType, accountId)) {
|
||||
if (accountType == Account.Type.PHONE) {
|
||||
accountId = CommunicationsUtils.normalizePhoneNum(accountId);
|
||||
} else if (accountType == Account.Type.EMAIL) {
|
||||
accountId = CommunicationsUtils.normalizeEmailAddress(accountId);
|
||||
}
|
||||
|
||||
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountType.getTypeName());
|
||||
CentralRepoAccount crAccount = CentralRepository.getInstance().getOrCreateAccount(crAccountType, accountId);
|
||||
|
||||
PersonaAccount personaAccount;
|
||||
// If persona doesnt exist, create one
|
||||
if (persona == null) {
|
||||
personaAccount = Persona.createPersonaForAccount(personaName, "Auto generated contact persona", Persona.PersonaStatus.UNKNOWN, crAccount, "Found in contact book entry", Persona.Confidence.DERIVED);
|
||||
personaToReturn = personaAccount.getPersona();
|
||||
} else {
|
||||
persona.addAccountToPersona(crAccount, "Found in contact book entry", Persona.Confidence.DERIVED);
|
||||
}
|
||||
}
|
||||
}
|
||||
return personaToReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// clear the tracker to reduce memory usage
|
||||
@ -411,6 +505,8 @@ public class IngestEventsListener {
|
||||
correlationDataSource.setSha256(imageSha256Hash);
|
||||
}
|
||||
}
|
||||
// automatically generate persona from contact artifacts.
|
||||
autoGenerateContactPersonas(dataSource);
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format(
|
||||
|
@ -257,6 +257,6 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
|
||||
return waypointMostRecent;
|
||||
}
|
||||
|
||||
return null;
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
|
@ -658,6 +658,17 @@ public final class EventsModel {
|
||||
return postTagsDeleted(updatedEventIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the events model for a data source added event.
|
||||
*
|
||||
* @throws TskCoreException If there is an error reading model data from the
|
||||
* case database.
|
||||
*/
|
||||
synchronized void handleDataSourceAdded() throws TskCoreException {
|
||||
populateDataSourcesCache();
|
||||
invalidateCaches(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the events model for an artifact tag deleted event and publishes
|
||||
* a tag deleted event via the model's event bus.
|
||||
@ -782,7 +793,6 @@ public final class EventsModel {
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public synchronized void invalidateCaches(Collection<Long> updatedEventIDs) throws TskCoreException {
|
||||
populateDataSourcesCache();
|
||||
minEventTimeCache.invalidateAll();
|
||||
maxEventTimeCache.invalidateAll();
|
||||
idsToEventsCache.invalidateAll(emptyIfNull(updatedEventIDs));
|
||||
|
@ -783,7 +783,7 @@ public class TimeLineController {
|
||||
break;
|
||||
case DATA_SOURCE_ADDED:
|
||||
future = executor.submit(() -> {
|
||||
filteredEvents.invalidateCaches(null);
|
||||
filteredEvents.handleDataSourceAdded();
|
||||
return null;
|
||||
});
|
||||
break;
|
||||
|
@ -79,6 +79,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TagSet;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
@ -108,6 +109,8 @@ public final class ImageGalleryController {
|
||||
Case.Events.DATA_SOURCE_DELETED
|
||||
);
|
||||
|
||||
private static final String CATEGORY_TAG_SET_PREFIX = "Project VIC";
|
||||
|
||||
/*
|
||||
* There is an image gallery controller per case. It is created during the
|
||||
* opening of case resources and destroyed during the closing of case
|
||||
@ -228,14 +231,16 @@ public final class ImageGalleryController {
|
||||
void startUp() throws TskCoreException {
|
||||
selectionModel = new FileIDSelectionModel(this);
|
||||
thumbnailCache = new ThumbnailCache(this);
|
||||
|
||||
TagSet categoryTagSet = getCategoryTagSet();
|
||||
/*
|
||||
* TODO (JIRA-5212): The next two lines need to be executed in this
|
||||
* order. Why? This suggests there is some inappropriate coupling
|
||||
* between the DrawableDB and GroupManager classes.
|
||||
*/
|
||||
groupManager = new GroupManager(this);
|
||||
drawableDB = DrawableDB.getDrawableDB(this);
|
||||
categoryManager = new CategoryManager(this);
|
||||
drawableDB = DrawableDB.getDrawableDB(this, categoryTagSet);
|
||||
categoryManager = new CategoryManager(this, categoryTagSet);
|
||||
tagsManager = new DrawableTagsManager(this);
|
||||
tagsManager.registerListener(groupManager);
|
||||
tagsManager.registerListener(categoryManager);
|
||||
@ -721,6 +726,28 @@ public final class ImageGalleryController {
|
||||
return (abstractFile.getKnown() != TskData.FileKnown.KNOWN) && FileTypeUtils.isDrawable(abstractFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TagSet with the image gallery categories.
|
||||
*
|
||||
* @return Category TagSet.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private TagSet getCategoryTagSet() throws TskCoreException {
|
||||
List<TagSet> tagSetList = getCaseDatabase().getTaggingManager().getTagSets();
|
||||
if (tagSetList != null && !tagSetList.isEmpty()) {
|
||||
for (TagSet set : tagSetList) {
|
||||
if (set.getName().startsWith(CATEGORY_TAG_SET_PREFIX)) {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
// If we get to here the Project VIC Test set wasn't found;
|
||||
throw new TskCoreException("Error loading Project VIC tag set: Tag set not found.");
|
||||
} else {
|
||||
throw new TskCoreException("Error loading Project VIC tag set: Tag set not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener for ingest module application events.
|
||||
*/
|
||||
|
@ -27,7 +27,7 @@ import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* A task that updates one drawable file in the drawables database.
|
||||
* A task that updates one drawable file in the drawable database.
|
||||
*/
|
||||
class UpdateDrawableFileTask extends DrawableDbTask {
|
||||
|
||||
|
@ -19,6 +19,9 @@
|
||||
package org.sleuthkit.autopsy.imagegallery.actions;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -27,9 +30,10 @@ import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.embed.swing.SwingFXUtils;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javax.annotation.Nonnull;
|
||||
@ -41,9 +45,7 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.DrawableDbTask;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager;
|
||||
@ -51,6 +53,7 @@ import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import javafx.scene.image.ImageView;
|
||||
|
||||
/**
|
||||
* An action that associates a drawable file with a Project Vic category.
|
||||
@ -62,24 +65,24 @@ public class CategorizeAction extends Action {
|
||||
|
||||
private final ImageGalleryController controller;
|
||||
private final UndoRedoManager undoManager;
|
||||
private final DhsImageCategory cat;
|
||||
private final Set<Long> selectedFileIDs;
|
||||
private final Boolean createUndo;
|
||||
private final TagName tagName;
|
||||
|
||||
public CategorizeAction(ImageGalleryController controller, DhsImageCategory cat, Set<Long> selectedFileIDs) {
|
||||
this(controller, cat, selectedFileIDs, true);
|
||||
public CategorizeAction(ImageGalleryController controller, TagName tagName, Set<Long> selectedFileIDs) {
|
||||
this(controller, tagName, selectedFileIDs, true);
|
||||
}
|
||||
|
||||
private CategorizeAction(ImageGalleryController controller, DhsImageCategory cat, Set<Long> selectedFileIDs, Boolean createUndo) {
|
||||
super(cat.getDisplayName());
|
||||
private CategorizeAction(ImageGalleryController controller, TagName tagName, Set<Long> selectedFileIDs, Boolean createUndo) {
|
||||
super(tagName.getDisplayName());
|
||||
this.controller = controller;
|
||||
this.undoManager = controller.getUndoManager();
|
||||
this.cat = cat;
|
||||
this.selectedFileIDs = selectedFileIDs;
|
||||
this.createUndo = createUndo;
|
||||
setGraphic(cat.getGraphic());
|
||||
this.tagName = tagName;
|
||||
setGraphic(getGraphic(tagName));
|
||||
setEventHandler(actionEvent -> addCatToFiles(selectedFileIDs));
|
||||
setAccelerator(new KeyCodeCombination(KeyCode.getKeyCode(Integer.toString(cat.getCategoryNumber()))));
|
||||
setAccelerator(new KeyCodeCombination(KeyCode.getKeyCode(getCategoryNumberFromTagName(tagName))));
|
||||
}
|
||||
|
||||
static public Menu getCategoriesMenu(ImageGalleryController controller) {
|
||||
@ -87,8 +90,18 @@ public class CategorizeAction extends Action {
|
||||
}
|
||||
|
||||
final void addCatToFiles(Set<Long> ids) {
|
||||
Logger.getAnonymousLogger().log(Level.INFO, "categorizing{0} as {1}", new Object[]{ids.toString(), cat.getDisplayName()}); //NON-NLS
|
||||
controller.queueDBTask(new CategorizeDrawableFileTask(ids, cat, createUndo));
|
||||
Logger.getAnonymousLogger().log(Level.INFO, "categorizing{0} as {1}", new Object[]{ids.toString(), tagName.getDisplayName()}); //NON-NLS
|
||||
controller.queueDBTask(new CategorizeDrawableFileTask(ids, tagName, createUndo));
|
||||
}
|
||||
|
||||
private String getCategoryNumberFromTagName(TagName tagName) {
|
||||
String displayName = tagName.getDisplayName();
|
||||
if (displayName.contains("CAT")) {
|
||||
String[] split = displayName.split(":");
|
||||
split = split[0].split("-");
|
||||
return split[1];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,8 +117,8 @@ public class CategorizeAction extends Action {
|
||||
|
||||
// Each category get an item in the sub-menu. Selecting one of these menu items adds
|
||||
// a tag with the associated category.
|
||||
for (final DhsImageCategory cat : DhsImageCategory.values()) {
|
||||
MenuItem categoryItem = ActionUtils.createMenuItem(new CategorizeAction(controller, cat, selected));
|
||||
for (TagName tagName : controller.getCategoryManager().getCategories()) {
|
||||
MenuItem categoryItem = ActionUtils.createMenuItem(new CategorizeAction(controller, tagName, selected));
|
||||
getItems().add(categoryItem);
|
||||
}
|
||||
}
|
||||
@ -124,54 +137,39 @@ public class CategorizeAction extends Action {
|
||||
final Set<Long> fileIDs;
|
||||
|
||||
final boolean createUndo;
|
||||
final DhsImageCategory cat;
|
||||
final TagName catTagName;
|
||||
|
||||
CategorizeDrawableFileTask(Set<Long> fileIDs, @Nonnull DhsImageCategory cat, boolean createUndo) {
|
||||
CategorizeDrawableFileTask(Set<Long> fileIDs, @Nonnull TagName catTagName, boolean createUndo) {
|
||||
super();
|
||||
this.fileIDs = fileIDs;
|
||||
java.util.Objects.requireNonNull(cat);
|
||||
this.cat = cat;
|
||||
java.util.Objects.requireNonNull(catTagName);
|
||||
this.catTagName = catTagName;
|
||||
this.createUndo = createUndo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||
final CategoryManager categoryManager = controller.getCategoryManager();
|
||||
Map<Long, DhsImageCategory> oldCats = new HashMap<>();
|
||||
TagName tagName = categoryManager.getTagName(cat);
|
||||
Map<Long, TagName> oldCats = new HashMap<>();
|
||||
for (long fileID : fileIDs) {
|
||||
try {
|
||||
DrawableFile file = controller.getFileFromID(fileID); //drawable db access
|
||||
if (createUndo) {
|
||||
DhsImageCategory oldCat = file.getCategory(); //drawable db access
|
||||
TagName oldCatTagName = categoryManager.getTagName(oldCat);
|
||||
if (false == tagName.equals(oldCatTagName)) {
|
||||
oldCats.put(fileID, oldCat);
|
||||
TagName oldCatTagName = file.getCategory(); //drawable db access
|
||||
if (false == catTagName.equals(oldCatTagName)) {
|
||||
oldCats.put(fileID, oldCatTagName);
|
||||
}
|
||||
}
|
||||
|
||||
final List<ContentTag> fileTags = tagsManager.getContentTags(file);
|
||||
if (tagName == categoryManager.getTagName(DhsImageCategory.ZERO)) {
|
||||
// delete all cat tags for cat-0
|
||||
fileTags.stream()
|
||||
.filter(tag -> CategoryManager.isCategoryTagName(tag.getName()))
|
||||
.forEach((ct) -> {
|
||||
try {
|
||||
tagsManager.deleteContentTag(ct);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error removing old categories result", ex); //NON-NLS
|
||||
}
|
||||
});
|
||||
} else {
|
||||
//add cat tag if no existing cat tag for that cat
|
||||
if (fileTags.stream()
|
||||
.map(Tag::getName)
|
||||
.filter(tagName::equals)
|
||||
.collect(Collectors.toList()).isEmpty()) {
|
||||
tagsManager.addContentTag(file, tagName, "");
|
||||
}
|
||||
|
||||
if (fileTags.stream()
|
||||
.map(Tag::getName)
|
||||
.filter(tagName::equals)
|
||||
.collect(Collectors.toList()).isEmpty()) {
|
||||
tagsManager.addContentTag(file, tagName, "");
|
||||
}
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error categorizing result", ex); //NON-NLS
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
@ -183,7 +181,7 @@ public class CategorizeAction extends Action {
|
||||
}
|
||||
|
||||
if (createUndo && oldCats.isEmpty() == false) {
|
||||
undoManager.addToUndo(new CategorizationChange(controller, cat, oldCats));
|
||||
undoManager.addToUndo(new CategorizationChange(controller, catTagName, oldCats));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -194,14 +192,14 @@ public class CategorizeAction extends Action {
|
||||
@Immutable
|
||||
private final class CategorizationChange implements UndoRedoManager.UndoableCommand {
|
||||
|
||||
private final DhsImageCategory newCategory;
|
||||
private final ImmutableMap<Long, DhsImageCategory> oldCategories;
|
||||
private final TagName newTagNameCategory;
|
||||
private final ImmutableMap<Long, TagName> oldTagNameCategories;
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
CategorizationChange(ImageGalleryController controller, DhsImageCategory newCategory, Map<Long, DhsImageCategory> oldCategories) {
|
||||
CategorizationChange(ImageGalleryController controller, TagName newTagNameCategory, Map<Long, TagName> oldTagNameCategories) {
|
||||
this.controller = controller;
|
||||
this.newCategory = newCategory;
|
||||
this.oldCategories = ImmutableMap.copyOf(oldCategories);
|
||||
this.newTagNameCategory = newTagNameCategory;
|
||||
this.oldTagNameCategories = ImmutableMap.copyOf(oldTagNameCategories);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,7 +208,7 @@ public class CategorizeAction extends Action {
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
new CategorizeAction(controller, newCategory, this.oldCategories.keySet(), false)
|
||||
new CategorizeAction(controller, newTagNameCategory, this.oldTagNameCategories.keySet(), false)
|
||||
.handle(null);
|
||||
}
|
||||
|
||||
@ -221,10 +219,42 @@ public class CategorizeAction extends Action {
|
||||
@Override
|
||||
public void undo() {
|
||||
|
||||
for (Map.Entry<Long, DhsImageCategory> entry : oldCategories.entrySet()) {
|
||||
for (Map.Entry<Long, TagName> entry : oldTagNameCategories.entrySet()) {
|
||||
new CategorizeAction(controller, entry.getValue(), Collections.singleton(entry.getKey()), false)
|
||||
.handle(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an BufferedImage to use as the icon for the given TagName.
|
||||
*
|
||||
* @param tagName The category TagName.
|
||||
*
|
||||
* @return TagName Icon BufferedImage.
|
||||
*/
|
||||
private BufferedImage getImageForTagName(TagName tagName) {
|
||||
BufferedImage off_image = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2 = off_image.createGraphics();
|
||||
|
||||
g2.setColor(java.awt.Color.decode(tagName.getColor().getRgbValue()));
|
||||
g2.fillRect(0, 0, 16, 16);
|
||||
|
||||
g2.setColor(Color.BLACK);
|
||||
g2.drawRect(0, 0, 16, 16);
|
||||
return off_image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Node which is a ImageView of the icon for the given TagName.
|
||||
*
|
||||
* @param tagname
|
||||
*
|
||||
* @return Node for use as the TagName menu item graphic.
|
||||
*/
|
||||
private Node getGraphic(TagName tagname) {
|
||||
BufferedImage buff_image = getImageForTagName(tagname);
|
||||
return new ImageView(SwingFXUtils.toFXImage(buff_image, null));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import java.util.logging.Level;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
import javafx.scene.control.ButtonType;
|
||||
@ -37,9 +38,9 @@ import javafx.scene.layout.VBox;
|
||||
import static org.apache.commons.lang.ObjectUtils.notEqual;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryPreferences;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -50,7 +51,7 @@ public class CategorizeGroupAction extends CategorizeAction {
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(CategorizeGroupAction.class.getName());
|
||||
|
||||
public CategorizeGroupAction(DhsImageCategory newCat, ImageGalleryController controller) {
|
||||
public CategorizeGroupAction(TagName newCat, ImageGalleryController controller) {
|
||||
super(controller, newCat, null);
|
||||
setEventHandler(actionEvent -> {
|
||||
controller.getViewState().getGroup().ifPresent(group -> {
|
||||
@ -60,12 +61,12 @@ public class CategorizeGroupAction extends CategorizeAction {
|
||||
//if they have preveiously disabled the warning, just go ahead and apply categories.
|
||||
addCatToFiles(ImmutableSet.copyOf(fileIDs));
|
||||
} else {
|
||||
final Map<DhsImageCategory, Long> catCountMap = new HashMap<>();
|
||||
final Map<TagName, Long> catCountMap = new HashMap<>();
|
||||
|
||||
for (Long fileID : fileIDs) {
|
||||
try {
|
||||
DhsImageCategory category = controller.getFileFromID(fileID).getCategory();
|
||||
if (false == DhsImageCategory.ZERO.equals(category) && newCat.equals(category) == false) {
|
||||
TagName category = controller.getFileFromID(fileID).getCategory();
|
||||
if (category != null && newCat.equals(category) == false) {
|
||||
catCountMap.merge(category, 1L, Long::sum);
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
@ -90,18 +91,19 @@ public class CategorizeGroupAction extends CategorizeAction {
|
||||
"CategorizeGroupAction.fileCountMessage={0} with {1}",
|
||||
"CategorizeGroupAction.dontShowAgain=Don't show this message again",
|
||||
"CategorizeGroupAction.fileCountHeader=Files in the following categories will have their categories overwritten: "})
|
||||
private void showConfirmationDialog(final Map<DhsImageCategory, Long> catCountMap, DhsImageCategory newCat, ObservableList<Long> fileIDs) {
|
||||
private void showConfirmationDialog(final Map<TagName, Long> catCountMap, TagName newCat, ObservableList<Long> fileIDs) {
|
||||
|
||||
ButtonType categorizeButtonType
|
||||
= new ButtonType(Bundle.CategorizeGroupAction_OverwriteButton_text(), ButtonBar.ButtonData.APPLY);
|
||||
|
||||
VBox textFlow = new VBox();
|
||||
|
||||
for (Map.Entry<DhsImageCategory, Long> entry : catCountMap.entrySet()) {
|
||||
if (entry.getValue() > 0
|
||||
&& notEqual(entry.getKey(), newCat)) {
|
||||
for (Map.Entry<TagName, Long> entry : catCountMap.entrySet()) {
|
||||
|
||||
if (entry != null && entry.getValue() > 0
|
||||
&& notEqual(entry.getKey(), newCat)) {
|
||||
Label label = new Label(Bundle.CategorizeGroupAction_fileCountMessage(entry.getValue(), entry.getKey().getDisplayName()),
|
||||
entry.getKey().getGraphic());
|
||||
getGraphic(entry.getKey()));
|
||||
label.setContentDisplay(ContentDisplay.RIGHT);
|
||||
textFlow.getChildren().add(label);
|
||||
}
|
||||
@ -127,4 +129,8 @@ public class CategorizeGroupAction extends CategorizeAction {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Node getGraphic(TagName tagName) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -19,14 +19,14 @@
|
||||
package org.sleuthkit.autopsy.imagegallery.actions;
|
||||
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class CategorizeSelectedFilesAction extends CategorizeAction {
|
||||
|
||||
public CategorizeSelectedFilesAction(DhsImageCategory cat, ImageGalleryController controller) {
|
||||
public CategorizeSelectedFilesAction(TagName cat, ImageGalleryController controller) {
|
||||
super(controller, cat, null);
|
||||
setEventHandler(actionEvent ->
|
||||
addCatToFiles(controller.getSelectionModel().getSelected())
|
||||
|
@ -24,8 +24,11 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.eventbus.AsyncEventBus;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
import java.util.logging.Level;
|
||||
@ -33,11 +36,13 @@ import javax.annotation.concurrent.Immutable;
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent.DeletedContentTagInfo;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TagSet;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -56,8 +61,6 @@ public class CategoryManager {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CategoryManager.class.getName());
|
||||
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
/**
|
||||
* the DrawableDB that backs the category counts cache. The counts are
|
||||
* initialized from this, and the counting of CAT-0 is always delegated to
|
||||
@ -65,6 +68,8 @@ public class CategoryManager {
|
||||
*/
|
||||
private final DrawableDB drawableDb;
|
||||
|
||||
private final TagSet categoryTagSet;
|
||||
|
||||
/**
|
||||
* Used to distribute CategoryChangeEvents
|
||||
*/
|
||||
@ -79,32 +84,20 @@ public class CategoryManager {
|
||||
* the count related methods go through this cache, which loads initial
|
||||
* values from the database if needed.
|
||||
*/
|
||||
private final LoadingCache<DhsImageCategory, LongAdder> categoryCounts
|
||||
private final LoadingCache<TagName, LongAdder> categoryCounts
|
||||
= CacheBuilder.newBuilder().build(CacheLoader.from(this::getCategoryCountHelper));
|
||||
/**
|
||||
* cached TagNames corresponding to Categories, looked up from
|
||||
* autopsyTagManager at initial request or if invalidated by case change.
|
||||
*/
|
||||
private final LoadingCache<DhsImageCategory, TagName> catTagNameMap
|
||||
= CacheBuilder.newBuilder().build(new CacheLoader<DhsImageCategory, TagName>() {
|
||||
@Override
|
||||
public TagName load(DhsImageCategory cat) throws TskCoreException {
|
||||
return getController().getTagsManager().getTagName(cat);
|
||||
}
|
||||
});
|
||||
|
||||
public CategoryManager(ImageGalleryController controller) {
|
||||
this.controller = controller;
|
||||
public CategoryManager(ImageGalleryController controller, TagSet categoryTagSet) throws TskCoreException {
|
||||
this.drawableDb = controller.getDrawablesDatabase();
|
||||
this.categoryTagSet = categoryTagSet;
|
||||
}
|
||||
|
||||
private ImageGalleryController getController() {
|
||||
return controller;
|
||||
public List<TagName> getCategories() {
|
||||
return Collections.unmodifiableList(getSortedTagNames(categoryTagSet.getTagNames()));
|
||||
}
|
||||
|
||||
synchronized public void invalidateCaches() {
|
||||
categoryCounts.invalidateAll();
|
||||
catTagNameMap.invalidateAll();
|
||||
fireChange(Collections.emptyList(), null);
|
||||
}
|
||||
|
||||
@ -115,16 +108,8 @@ public class CategoryManager {
|
||||
*
|
||||
* @return the number of files with the given Category
|
||||
*/
|
||||
synchronized public long getCategoryCount(DhsImageCategory cat) {
|
||||
if (cat == DhsImageCategory.ZERO) {
|
||||
// Keeping track of the uncategorized files is a bit tricky while ingest
|
||||
// is going on, so always use the list of file IDs we already have along with the
|
||||
// other category counts instead of trying to track it separately.
|
||||
long allOtherCatCount = getCategoryCount(DhsImageCategory.ONE) + getCategoryCount(DhsImageCategory.TWO) + getCategoryCount(DhsImageCategory.THREE) + getCategoryCount(DhsImageCategory.FOUR) + getCategoryCount(DhsImageCategory.FIVE);
|
||||
return drawableDb.getNumberOfImageFilesInList() - allOtherCatCount;
|
||||
} else {
|
||||
return categoryCounts.getUnchecked(cat).sum();
|
||||
}
|
||||
synchronized public long getCategoryCount(TagName tagName) {
|
||||
return categoryCounts.getUnchecked(tagName).sum();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,10 +118,8 @@ public class CategoryManager {
|
||||
*
|
||||
* @param cat the Category to increment
|
||||
*/
|
||||
synchronized public void incrementCategoryCount(DhsImageCategory cat) {
|
||||
if (cat != DhsImageCategory.ZERO) {
|
||||
categoryCounts.getUnchecked(cat).increment();
|
||||
}
|
||||
synchronized public void incrementCategoryCount(TagName tagName) {
|
||||
categoryCounts.getUnchecked(tagName).increment();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,10 +128,8 @@ public class CategoryManager {
|
||||
*
|
||||
* @param cat the Category to decrement
|
||||
*/
|
||||
synchronized public void decrementCategoryCount(DhsImageCategory cat) {
|
||||
if (cat != DhsImageCategory.ZERO) {
|
||||
categoryCounts.getUnchecked(cat).decrement();
|
||||
}
|
||||
synchronized public void decrementCategoryCount(TagName tagName) {
|
||||
categoryCounts.getUnchecked(tagName).decrement();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,14 +142,14 @@ public class CategoryManager {
|
||||
* @return a LongAdder whose value is set to the number of file with the
|
||||
* given Category
|
||||
*/
|
||||
synchronized private LongAdder getCategoryCountHelper(DhsImageCategory cat) {
|
||||
synchronized private LongAdder getCategoryCountHelper(TagName cat) {
|
||||
LongAdder longAdder = new LongAdder();
|
||||
longAdder.decrement();
|
||||
try {
|
||||
longAdder.add(drawableDb.getCategoryCount(cat));
|
||||
longAdder.increment();
|
||||
} catch (IllegalStateException ex) {
|
||||
LOGGER.log(Level.WARNING, "Case closed while getting files"); //NON-NLS
|
||||
LOGGER.log(Level.WARNING, "Case closed while getting files", ex); //NON-NLS
|
||||
}
|
||||
return longAdder;
|
||||
}
|
||||
@ -178,8 +159,8 @@ public class CategoryManager {
|
||||
*
|
||||
* @param fileIDs
|
||||
*/
|
||||
public void fireChange(Collection<Long> fileIDs, DhsImageCategory newCategory) {
|
||||
categoryEventBus.post(new CategoryChangeEvent(fileIDs, newCategory));
|
||||
public void fireChange(Collection<Long> fileIDs, TagName tagName) {
|
||||
categoryEventBus.post(new CategoryChangeEvent(fileIDs, tagName));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,68 +197,66 @@ public class CategoryManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* get the TagName used to store this Category in the main autopsy db.
|
||||
* Returns true if the given TagName is a category tag.
|
||||
*
|
||||
* @return the TagName used for this Category
|
||||
* @param tName TagName
|
||||
*
|
||||
* @return True if tName is a category tag.
|
||||
*/
|
||||
synchronized public TagName getTagName(DhsImageCategory cat) {
|
||||
return catTagNameMap.getUnchecked(cat);
|
||||
public boolean isCategoryTagName(TagName tName) {
|
||||
return categoryTagSet.getTagNames().contains(tName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given TagName is not a category tag.
|
||||
*
|
||||
* Keep for use in location were a reference to this function is passed.
|
||||
*
|
||||
* @param tName TagName
|
||||
*
|
||||
* @return True if the given tName is not a category tag.
|
||||
*/
|
||||
public boolean isNotCategoryTagName(TagName tName) {
|
||||
return !isCategoryTagName(tName);
|
||||
|
||||
}
|
||||
|
||||
public static DhsImageCategory categoryFromTagName(TagName tagName) {
|
||||
return DhsImageCategory.fromDisplayName(tagName.getDisplayName());
|
||||
}
|
||||
|
||||
public static boolean isCategoryTagName(TagName tName) {
|
||||
return DhsImageCategory.isCategoryName(tName.getDisplayName());
|
||||
}
|
||||
|
||||
public static boolean isNotCategoryTagName(TagName tName) {
|
||||
return DhsImageCategory.isNotCategoryName(tName.getDisplayName());
|
||||
|
||||
/**
|
||||
* Returns the category tag set.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TagSet getCategorySet() {
|
||||
return categoryTagSet;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void handleTagAdded(ContentTagAddedEvent event) {
|
||||
final ContentTag addedTag = event.getAddedTag();
|
||||
if (isCategoryTagName(addedTag.getName())) {
|
||||
final DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||
try {
|
||||
//remove old category tag(s) if necessary
|
||||
for (ContentTag ct : tagsManager.getContentTags(addedTag.getContent())) {
|
||||
if (ct.getId() != addedTag.getId()
|
||||
&& CategoryManager.isCategoryTagName(ct.getName())) {
|
||||
try {
|
||||
tagsManager.deleteContentTag(ct);
|
||||
} catch (TskCoreException tskException) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to delete content tag. Unable to maintain categories in a consistent state.", tskException); //NON-NLS
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException tskException) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to get content tags for content. Unable to maintain category in a consistent state.", tskException); //NON-NLS
|
||||
}
|
||||
DhsImageCategory newCat = CategoryManager.categoryFromTagName(addedTag.getName());
|
||||
if (newCat != DhsImageCategory.ZERO) {
|
||||
incrementCategoryCount(newCat);
|
||||
}
|
||||
|
||||
fireChange(Collections.singleton(addedTag.getContent().getId()), newCat);
|
||||
List<DeletedContentTagInfo> removedTags = event.getDeletedTags();
|
||||
if (removedTags != null) {
|
||||
for (DeletedContentTagInfo tagInfo : removedTags) {
|
||||
handleDeletedInfo(tagInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (isCategoryTagName(addedTag.getName())) {
|
||||
incrementCategoryCount(addedTag.getName());
|
||||
fireChange(Collections.singleton(addedTag.getContent().getId()), addedTag.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void handleTagDeleted(ContentTagDeletedEvent event) {
|
||||
final ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = event.getDeletedTagInfo();
|
||||
handleDeletedInfo(deletedTagInfo);
|
||||
}
|
||||
|
||||
private void handleDeletedInfo(DeletedContentTagInfo deletedTagInfo) {
|
||||
TagName tagName = deletedTagInfo.getName();
|
||||
if (isCategoryTagName(tagName)) {
|
||||
|
||||
DhsImageCategory deletedCat = CategoryManager.categoryFromTagName(tagName);
|
||||
if (deletedCat != DhsImageCategory.ZERO) {
|
||||
decrementCategoryCount(deletedCat);
|
||||
}
|
||||
decrementCategoryCount(tagName);
|
||||
fireChange(Collections.singleton(deletedTagInfo.getContentID()), null);
|
||||
}
|
||||
}
|
||||
@ -290,16 +269,16 @@ public class CategoryManager {
|
||||
public static class CategoryChangeEvent {
|
||||
|
||||
private final ImmutableSet<Long> fileIDs;
|
||||
private final DhsImageCategory newCategory;
|
||||
private final TagName tagName;
|
||||
|
||||
public CategoryChangeEvent(Collection<Long> fileIDs, DhsImageCategory newCategory) {
|
||||
public CategoryChangeEvent(Collection<Long> fileIDs, TagName tagName) {
|
||||
super();
|
||||
this.fileIDs = ImmutableSet.copyOf(fileIDs);
|
||||
this.newCategory = newCategory;
|
||||
this.tagName = tagName;
|
||||
}
|
||||
|
||||
public DhsImageCategory getNewCategory() {
|
||||
return newCategory;
|
||||
public TagName getNewCategory() {
|
||||
return tagName;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -309,4 +288,18 @@ public class CategoryManager {
|
||||
return fileIDs;
|
||||
}
|
||||
}
|
||||
|
||||
private List<TagName> getSortedTagNames(List<TagName> tagNames) {
|
||||
Comparator<TagName> compareByDisplayName = new Comparator<TagName>() {
|
||||
@Override
|
||||
public int compare(TagName tagName1, TagName tagName2) {
|
||||
return tagName1.getDisplayName().compareTo(tagName2.getDisplayName());
|
||||
}
|
||||
};
|
||||
|
||||
List<TagName> sortedTagNames = new ArrayList<>(tagNames);
|
||||
sortedTagNames.sort(compareByDisplayName);
|
||||
|
||||
return sortedTagNames;
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.imagegallery.datamodel;
|
||||
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -89,15 +88,17 @@ public class DrawableAttribute<T extends Comparable<T>> {
|
||||
* //TODO: this has lead to awkward hard to maintain code, and little
|
||||
* advantage. move categories into DrawableDB?
|
||||
*/
|
||||
public final static DrawableAttribute<DhsImageCategory> CATEGORY
|
||||
= new DrawableAttribute<DhsImageCategory>(AttributeName.CATEGORY, Bundle.DrawableAttribute_category(),
|
||||
public final static DrawableAttribute<TagName> CATEGORY
|
||||
= new DrawableAttribute<TagName>(AttributeName.CATEGORY, Bundle.DrawableAttribute_category(),
|
||||
false,
|
||||
"category-icon.png", //NON-NLS
|
||||
f -> Collections.singleton(f.getCategory())) {
|
||||
|
||||
@Override
|
||||
public Node getGraphicForValue(DhsImageCategory val) {
|
||||
return val.getGraphic();
|
||||
public Node getGraphicForValue(TagName val) {
|
||||
|
||||
return null;
|
||||
//return val.getGraphic();
|
||||
}
|
||||
};
|
||||
|
||||
@ -235,9 +236,13 @@ public class DrawableAttribute<T extends Comparable<T>> {
|
||||
.filter(value -> (value != null && value.toString().isEmpty() == false))
|
||||
.collect(Collectors.toSet());
|
||||
} catch (Exception ex) {
|
||||
/* There is a catch-all here because the code in the try block executes third-party
|
||||
library calls that throw unchecked exceptions. See JIRA-5144, where an IllegalStateException
|
||||
was thrown because a file's MIME type was incorrectly identified as a picture type. */
|
||||
/*
|
||||
* There is a catch-all here because the code in the try block
|
||||
* executes third-party library calls that throw unchecked
|
||||
* exceptions. See JIRA-5144, where an IllegalStateException was
|
||||
* thrown because a file's MIME type was incorrectly identified as a
|
||||
* picture type.
|
||||
*/
|
||||
logger.log(Level.WARNING, "Exception while getting image attributes", ex); //NON-NLS
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
@ -52,11 +52,9 @@ import java.util.logging.Level;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.swing.SortOrder;
|
||||
import static org.apache.commons.lang3.ObjectUtils.notEqual;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.FileTypeUtils;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule;
|
||||
@ -80,6 +78,7 @@ import org.sleuthkit.datamodel.TskDataException;
|
||||
import org.sleuthkit.datamodel.VersionNumber;
|
||||
import org.sqlite.SQLiteJDBCLoader;
|
||||
import java.util.stream.Collectors;
|
||||
import org.sleuthkit.datamodel.TagSet;
|
||||
|
||||
/**
|
||||
* Provides access to the image gallery database and selected tables in the case
|
||||
@ -250,7 +249,7 @@ public final class DrawableDB {
|
||||
* could not be correctly initialized for Image
|
||||
* Gallery use.
|
||||
*/
|
||||
private DrawableDB(Path dbPath, ImageGalleryController controller) throws IOException, SQLException, TskCoreException {
|
||||
private DrawableDB(Path dbPath, ImageGalleryController controller, TagSet standardCategories) throws IOException, SQLException, TskCoreException {
|
||||
this.dbPath = dbPath;
|
||||
this.controller = controller;
|
||||
caseDb = this.controller.getCaseDatabase();
|
||||
@ -259,7 +258,7 @@ public final class DrawableDB {
|
||||
dbWriteLock();
|
||||
try {
|
||||
con = DriverManager.getConnection("jdbc:sqlite:" + dbPath.toString()); //NON-NLS
|
||||
if (!initializeDBSchema() || !upgradeDBSchema() || !prepareStatements() || !initializeStandardGroups() || !removeDeletedDataSources() || !initializeImageList()) {
|
||||
if (!initializeDBSchema() || !upgradeDBSchema() || !prepareStatements() || !initializeStandardGroups(standardCategories) || !removeDeletedDataSources() || !initializeImageList()) {
|
||||
close();
|
||||
throw new TskCoreException("Failed to initialize drawables database for Image Gallery use"); //NON-NLS
|
||||
}
|
||||
@ -297,12 +296,13 @@ public final class DrawableDB {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean initializeStandardGroups() {
|
||||
private boolean initializeStandardGroups(TagSet standardCategories) {
|
||||
CaseDbTransaction caseDbTransaction = null;
|
||||
try {
|
||||
caseDbTransaction = caseDb.beginTransaction();
|
||||
for (DhsImageCategory cat : DhsImageCategory.values()) {
|
||||
insertGroup(cat.getDisplayName(), DrawableAttribute.CATEGORY, caseDbTransaction);
|
||||
|
||||
for(TagName tagName: standardCategories.getTagNames()) {
|
||||
insertGroup(tagName.getDisplayName(), DrawableAttribute.CATEGORY, caseDbTransaction);
|
||||
}
|
||||
caseDbTransaction.commit();
|
||||
return true;
|
||||
@ -466,7 +466,7 @@ public final class DrawableDB {
|
||||
*
|
||||
* @throws org.sleuthkit.datamodel.TskCoreException
|
||||
*/
|
||||
public static DrawableDB getDrawableDB(ImageGalleryController controller) throws TskCoreException {
|
||||
public static DrawableDB getDrawableDB(ImageGalleryController controller, TagSet standardCategories) throws TskCoreException {
|
||||
Path dbPath = ImageGalleryModule.getModuleOutputDir(controller.getCase()).resolve("drawable.db");
|
||||
try {
|
||||
deleteDatabaseIfOlderVersion(dbPath);
|
||||
@ -477,7 +477,7 @@ public final class DrawableDB {
|
||||
}
|
||||
|
||||
try {
|
||||
return new DrawableDB(dbPath, controller);
|
||||
return new DrawableDB(dbPath, controller, standardCategories);
|
||||
} catch (IOException ex) {
|
||||
throw new TskCoreException("Failed to create drawables database directory", ex); //NON-NLS
|
||||
} catch (SQLException ex) {
|
||||
@ -2068,7 +2068,7 @@ public final class DrawableDB {
|
||||
case MIME_TYPE:
|
||||
return groupManager.getFileIDsWithMimeType((String) groupKey.getValue());
|
||||
case CATEGORY:
|
||||
return groupManager.getFileIDsWithCategory((DhsImageCategory) groupKey.getValue());
|
||||
return groupManager.getFileIDsWithCategory((TagName) groupKey.getValue());
|
||||
case TAGS:
|
||||
return groupManager.getFileIDsWithTag((TagName) groupKey.getValue());
|
||||
}
|
||||
@ -2269,9 +2269,8 @@ public final class DrawableDB {
|
||||
*
|
||||
* @return the number of the with the given category
|
||||
*/
|
||||
public long getCategoryCount(DhsImageCategory cat) {
|
||||
public long getCategoryCount(TagName tagName) {
|
||||
try {
|
||||
TagName tagName = controller.getTagsManager().getTagName(cat);
|
||||
if (nonNull(tagName)) {
|
||||
return caseDb.getContentTagsByTagName(tagName).stream()
|
||||
.map(ContentTag::getContent)
|
||||
@ -2280,7 +2279,7 @@ public final class DrawableDB {
|
||||
.count();
|
||||
}
|
||||
} catch (IllegalStateException ex) {
|
||||
logger.log(Level.WARNING, "Case closed while getting files"); //NON-NLS
|
||||
logger.log(Level.WARNING, "Case closed while getting files", ex); //NON-NLS
|
||||
} catch (TskCoreException ex1) {
|
||||
logger.log(Level.SEVERE, "Failed to get content tags by tag name.", ex1); //NON-NLS
|
||||
}
|
||||
@ -2314,7 +2313,6 @@ public final class DrawableDB {
|
||||
DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||
|
||||
String catTagNameIDs = tagsManager.getCategoryTagNames().stream()
|
||||
.filter(tagName -> notEqual(tagName.getDisplayName(), DhsImageCategory.ZERO.getDisplayName()))
|
||||
.map(TagName::getId)
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining(",", "(", ")"));
|
||||
|
@ -40,8 +40,8 @@ import org.apache.commons.lang3.text.WordUtils;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.FileTypeUtils;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -94,15 +94,19 @@ public abstract class DrawableFile {
|
||||
|
||||
private final SimpleBooleanProperty analyzed;
|
||||
|
||||
private final SimpleObjectProperty<DhsImageCategory> category = new SimpleObjectProperty<>(null);
|
||||
private final SimpleObjectProperty<TagName> categoryTagName = new SimpleObjectProperty<>(null);
|
||||
|
||||
private String make;
|
||||
|
||||
private String model;
|
||||
|
||||
private final CategoryManager categoryManager;
|
||||
|
||||
protected DrawableFile(AbstractFile file, Boolean analyzed) {
|
||||
this.analyzed = new SimpleBooleanProperty(analyzed);
|
||||
this.file = file;
|
||||
|
||||
categoryManager = ImageGalleryController.getController(Case.getCurrentCase()).getCategoryManager();
|
||||
}
|
||||
|
||||
public abstract boolean isVideo();
|
||||
@ -229,32 +233,30 @@ public abstract class DrawableFile {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setCategory(DhsImageCategory category) {
|
||||
categoryProperty().set(category);
|
||||
|
||||
}
|
||||
|
||||
public DhsImageCategory getCategory() {
|
||||
public TagName getCategory() {
|
||||
updateCategory();
|
||||
return category.get();
|
||||
return categoryTagName.get();
|
||||
}
|
||||
|
||||
public SimpleObjectProperty<DhsImageCategory> categoryProperty() {
|
||||
return category;
|
||||
public SimpleObjectProperty<TagName> categoryProperty() {
|
||||
return categoryTagName;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the category property to the most severe one found
|
||||
* Update the category property.
|
||||
*/
|
||||
private void updateCategory() {
|
||||
try {
|
||||
category.set(getContentTags().stream()
|
||||
.map(Tag::getName).filter(CategoryManager::isCategoryTagName)
|
||||
.map(TagName::getDisplayName)
|
||||
.map(DhsImageCategory::fromDisplayName)
|
||||
.sorted().findFirst() //sort by severity and take the first
|
||||
.orElse(DhsImageCategory.ZERO)
|
||||
);
|
||||
List<ContentTag> contentTags = getContentTags();
|
||||
TagName tag = null;
|
||||
for (ContentTag ct : contentTags) {
|
||||
TagName tagName = ct.getName();
|
||||
if (categoryManager.isCategoryTagName(tagName)) {
|
||||
tag = tagName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
categoryTagName.set(tag);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "problem looking up category for " + this.getContentPathSafe(), ex); //NON-NLS
|
||||
} catch (IllegalStateException ex) {
|
||||
|
@ -20,10 +20,11 @@ package org.sleuthkit.autopsy.imagegallery.datamodel;
|
||||
|
||||
import com.google.common.eventbus.AsyncEventBus;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
@ -33,7 +34,6 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
@ -54,10 +54,16 @@ public final class DrawableTagsManager {
|
||||
private static final Image BOOKMARK_IMAGE = new Image("/org/sleuthkit/autopsy/images/star-bookmark-icon-16.png");
|
||||
|
||||
private final TagsManager autopsyTagsManager;
|
||||
/** The tag name corresponding to the "built-in" tag "Follow Up" */
|
||||
/**
|
||||
* The tag name corresponding to the "built-in" tag "Follow Up"
|
||||
*/
|
||||
private final TagName followUpTagName;
|
||||
private final TagName bookmarkTagName;
|
||||
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
private final Comparator<TagName> compareByDisplayName;
|
||||
|
||||
/**
|
||||
* Used to distribute TagsChangeEvents
|
||||
*/
|
||||
@ -74,6 +80,14 @@ public final class DrawableTagsManager {
|
||||
this.autopsyTagsManager = controller.getCase().getServices().getTagsManager();
|
||||
followUpTagName = getTagName(Bundle.DrawableTagsManager_followUp());
|
||||
bookmarkTagName = getTagName(Bundle.DrawableTagsManager_bookMark());
|
||||
this.controller = controller;
|
||||
|
||||
compareByDisplayName = new Comparator<TagName>() {
|
||||
@Override
|
||||
public int compare(TagName tagName1, TagName tagName2) {
|
||||
return tagName1.getDisplayName().compareTo(tagName2.getDisplayName());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,25 +143,26 @@ public final class DrawableTagsManager {
|
||||
* @throws org.sleuthkit.datamodel.TskCoreException
|
||||
*/
|
||||
public List<TagName> getNonCategoryTagNames() throws TskCoreException {
|
||||
return autopsyTagsManager.getAllTagNames().stream()
|
||||
.filter(CategoryManager::isNotCategoryTagName)
|
||||
.distinct().sorted()
|
||||
.collect(Collectors.toList());
|
||||
List<TagName> nonCategoryTagNames = new ArrayList<>();
|
||||
List<TagName> allTags = autopsyTagsManager.getAllTagNames();
|
||||
for (TagName tag : allTags) {
|
||||
if (controller.getCategoryManager().isNotCategoryTagName(tag)) {
|
||||
nonCategoryTagNames.add(tag);
|
||||
}
|
||||
}
|
||||
nonCategoryTagNames.sort(compareByDisplayName);
|
||||
return nonCategoryTagNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the TagNames that are categories
|
||||
*
|
||||
* @return All the TagNames that are categories, in alphabetical order by
|
||||
* displayName.
|
||||
* @return All the TagNames that are categories.
|
||||
*
|
||||
* @throws org.sleuthkit.datamodel.TskCoreException
|
||||
*/
|
||||
public List<TagName> getCategoryTagNames() throws TskCoreException {
|
||||
return autopsyTagsManager.getAllTagNames().stream()
|
||||
.filter(CategoryManager::isCategoryTagName)
|
||||
.distinct().sorted()
|
||||
.collect(Collectors.toList());
|
||||
return controller.getCategoryManager().getCategorySet().getTagNames();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,10 +210,6 @@ public final class DrawableTagsManager {
|
||||
}
|
||||
}
|
||||
|
||||
public TagName getTagName(DhsImageCategory cat) throws TskCoreException {
|
||||
return getTagName(cat.getDisplayName());
|
||||
}
|
||||
|
||||
public ContentTag addContentTag(DrawableFile file, TagName tagName, String comment) throws TskCoreException {
|
||||
return autopsyTagsManager.addContentTag(file.getAbstractFile(), tagName, comment);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ public class VideoFile extends DrawableFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the genereric video thumbnail.
|
||||
* Get the generic video thumbnail.
|
||||
*
|
||||
* @return The thumbnail.
|
||||
*/
|
||||
|
@ -59,6 +59,7 @@ public class GroupKey<T extends Comparable<T>> implements Comparable<GroupKey<T>
|
||||
|
||||
public String getValueDisplayName() {
|
||||
return Objects.equals(attr, DrawableAttribute.TAGS)
|
||||
|| Objects.equals(attr, DrawableAttribute.CATEGORY)
|
||||
? ((TagName) getValue()).getDisplayName()
|
||||
: Objects.toString(getValue(), "unknown");
|
||||
}
|
||||
@ -74,8 +75,9 @@ public class GroupKey<T extends Comparable<T>> implements Comparable<GroupKey<T>
|
||||
|
||||
hash = 79 * hash + Objects.hashCode(this.val);
|
||||
hash = 79 * hash + Objects.hashCode(this.attr);
|
||||
if (this.dataSource != null)
|
||||
hash = 79 * hash + (int)this.dataSource.getId();
|
||||
if (this.dataSource != null) {
|
||||
hash = 79 * hash + (int) this.dataSource.getId();
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
@ -103,12 +105,12 @@ public class GroupKey<T extends Comparable<T>> implements Comparable<GroupKey<T>
|
||||
// Data source is significant only for PATH based groups.
|
||||
if (this.attr == DrawableAttribute.PATH) {
|
||||
if (this.dataSource != null && other.dataSource != null) {
|
||||
return this.dataSource.getId() == other.dataSource.getId();
|
||||
return this.dataSource.getId() == other.dataSource.getId();
|
||||
} else if (this.dataSource == null && other.dataSource == null) {
|
||||
// neither group has a datasource
|
||||
return true;
|
||||
} else {
|
||||
// one group has a datasource, other doesn't
|
||||
// one group has a datasource, other doesn't
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,6 @@ import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.swing.SortOrder;
|
||||
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
|
||||
import static org.apache.commons.lang3.ObjectUtils.notEqual;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
@ -70,9 +69,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.coreutils.LoggedTask;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||
@ -106,23 +103,23 @@ public class GroupManager {
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
/**
|
||||
* Keeps track of the current path group
|
||||
* - a change in path indicates the current path group is analyzed
|
||||
* Keeps track of the current path group - a change in path indicates the
|
||||
* current path group is analyzed
|
||||
*/
|
||||
@GuardedBy("this") //NOPMD
|
||||
private GroupKey<?> currentPathGroup = null;
|
||||
|
||||
/**
|
||||
* list of all analyzed groups - i.e. groups that are ready to be shown to user.
|
||||
* These are groups under the selected groupBy attribute.
|
||||
* list of all analyzed groups - i.e. groups that are ready to be shown to
|
||||
* user. These are groups under the selected groupBy attribute.
|
||||
*/
|
||||
@GuardedBy("this") //NOPMD
|
||||
private final ObservableList<DrawableGroup> analyzedGroups = FXCollections.observableArrayList();
|
||||
private final ObservableList<DrawableGroup> unmodifiableAnalyzedGroups = FXCollections.unmodifiableObservableList(analyzedGroups);
|
||||
|
||||
/**
|
||||
* list of unseen groups
|
||||
* These are groups under the selected groupBy attribute.
|
||||
* list of unseen groups These are groups under the selected groupBy
|
||||
* attribute.
|
||||
*/
|
||||
@GuardedBy("this") //NOPMD
|
||||
private final ObservableList<DrawableGroup> unSeenGroups = FXCollections.observableArrayList();
|
||||
@ -187,14 +184,14 @@ public class GroupManager {
|
||||
synchronized public Set<GroupKey<?>> getAllGroupKeysForFile(DrawableFile file) throws TskCoreException, TskDataException {
|
||||
Set<GroupKey<?>> resultSet = new HashSet<>();
|
||||
|
||||
for (DrawableAttribute<?> attr: DrawableAttribute.getGroupableAttrs()) {
|
||||
for (DrawableAttribute<?> attr : DrawableAttribute.getGroupableAttrs()) {
|
||||
for (Comparable<?> val : attr.getValue(file)) {
|
||||
|
||||
if (attr == DrawableAttribute.PATH) {
|
||||
resultSet.add(new GroupKey(attr, val, file.getDataSource()));
|
||||
} else if (attr == DrawableAttribute.TAGS) {
|
||||
//don't show groups for the categories when grouped by tags.
|
||||
if (CategoryManager.isNotCategoryTagName((TagName) val)) {
|
||||
if (controller.getCategoryManager().isNotCategoryTagName((TagName) val)) {
|
||||
resultSet.add(new GroupKey(attr, val, null));
|
||||
}
|
||||
} else {
|
||||
@ -205,8 +202,7 @@ public class GroupManager {
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
*
|
||||
* Returns GroupKeys for all the Groups the given file is a part of.
|
||||
*
|
||||
@ -322,13 +318,13 @@ public class GroupManager {
|
||||
* @param group
|
||||
*/
|
||||
synchronized private void updateUnSeenGroups(DrawableGroup group) {
|
||||
if (group.isSeen()) {
|
||||
unSeenGroups.removeAll(group);
|
||||
} else if (unSeenGroups.contains(group) == false &&
|
||||
getGroupBy() == group.getGroupKey().getAttribute()) {
|
||||
unSeenGroups.add(group);
|
||||
}
|
||||
sortUnseenGroups();
|
||||
if (group.isSeen()) {
|
||||
unSeenGroups.removeAll(group);
|
||||
} else if (unSeenGroups.contains(group) == false
|
||||
&& getGroupBy() == group.getGroupKey().getAttribute()) {
|
||||
unSeenGroups.add(group);
|
||||
}
|
||||
sortUnseenGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -390,7 +386,7 @@ public class GroupManager {
|
||||
switch (groupKey.getAttribute().attrName) {
|
||||
//these cases get special treatment
|
||||
case CATEGORY:
|
||||
return getFileIDsWithCategory((DhsImageCategory) groupKey.getValue());
|
||||
return getFileIDsWithCategory((TagName) groupKey.getValue());
|
||||
case TAGS:
|
||||
return getFileIDsWithTag((TagName) groupKey.getValue());
|
||||
case MIME_TYPE:
|
||||
@ -405,33 +401,18 @@ public class GroupManager {
|
||||
|
||||
// @@@ This was kind of slow in the profiler. Maybe we should cache it.
|
||||
// Unless the list of file IDs is necessary, use countFilesWithCategory() to get the counts.
|
||||
synchronized public Set<Long> getFileIDsWithCategory(DhsImageCategory category) throws TskCoreException {
|
||||
synchronized public Set<Long> getFileIDsWithCategory(TagName category) throws TskCoreException {
|
||||
Set<Long> fileIDsToReturn = Collections.emptySet();
|
||||
|
||||
try {
|
||||
final DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||
if (category == DhsImageCategory.ZERO) {
|
||||
Set<Long> fileIDs = new HashSet<>();
|
||||
for (TagName catTagName : tagsManager.getCategoryTagNames()) {
|
||||
if (notEqual(catTagName.getDisplayName(), DhsImageCategory.ZERO.getDisplayName())) {
|
||||
tagsManager.getContentTagsByTagName(catTagName).stream()
|
||||
.filter(ct -> ct.getContent() instanceof AbstractFile)
|
||||
.map(ct -> ct.getContent().getId())
|
||||
.filter(getDrawableDB()::isInDB)
|
||||
.forEach(fileIDs::add);
|
||||
}
|
||||
}
|
||||
|
||||
fileIDsToReturn = getDrawableDB().findAllFileIdsWhere("obj_id NOT IN (" + StringUtils.join(fileIDs, ',') + ")"); //NON-NLS
|
||||
} else {
|
||||
|
||||
List<ContentTag> contentTags = tagsManager.getContentTagsByTagName(tagsManager.getTagName(category));
|
||||
fileIDsToReturn = contentTags.stream()
|
||||
.filter(ct -> ct.getContent() instanceof AbstractFile)
|
||||
.filter(ct -> getDrawableDB().isInDB(ct.getContent().getId()))
|
||||
.map(ct -> ct.getContent().getId())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
List<ContentTag> contentTags = tagsManager.getContentTagsByTagName(category);
|
||||
fileIDsToReturn = contentTags.stream()
|
||||
.filter(ct -> ct.getContent() instanceof AbstractFile)
|
||||
.filter(ct -> getDrawableDB().isInDB(ct.getContent().getId()))
|
||||
.map(ct -> ct.getContent().getId())
|
||||
.collect(Collectors.toSet());
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "TSK error getting files in Category:" + category.getDisplayName(), ex); //NON-NLS
|
||||
throw ex;
|
||||
@ -552,14 +533,14 @@ public class GroupManager {
|
||||
synchronized public void handleTagAdded(ContentTagAddedEvent evt) {
|
||||
GroupKey<?> newGroupKey = null;
|
||||
final long fileID = evt.getAddedTag().getContent().getId();
|
||||
if (getGroupBy() == DrawableAttribute.CATEGORY && CategoryManager.isCategoryTagName(evt.getAddedTag().getName())) {
|
||||
newGroupKey = new GroupKey<>(DrawableAttribute.CATEGORY, CategoryManager.categoryFromTagName(evt.getAddedTag().getName()), getDataSource());
|
||||
if (getGroupBy() == DrawableAttribute.CATEGORY && controller.getCategoryManager().isCategoryTagName(evt.getAddedTag().getName())) {
|
||||
newGroupKey = new GroupKey<>(DrawableAttribute.CATEGORY, evt.getAddedTag().getName(), getDataSource());
|
||||
for (GroupKey<?> oldGroupKey : groupMap.keySet()) {
|
||||
if (oldGroupKey.equals(newGroupKey) == false) {
|
||||
removeFromGroup(oldGroupKey, fileID);
|
||||
}
|
||||
}
|
||||
} else if (getGroupBy() == DrawableAttribute.TAGS && CategoryManager.isNotCategoryTagName(evt.getAddedTag().getName())) {
|
||||
} else if (getGroupBy() == DrawableAttribute.TAGS && controller.getCategoryManager().isNotCategoryTagName(evt.getAddedTag().getName())) {
|
||||
newGroupKey = new GroupKey<>(DrawableAttribute.TAGS, evt.getAddedTag().getName(), getDataSource());
|
||||
}
|
||||
if (newGroupKey != null) {
|
||||
@ -569,7 +550,8 @@ public class GroupManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an analyzed file to the in-memory group data structures. Marks the group as unseen.
|
||||
* Adds an analyzed file to the in-memory group data structures. Marks the
|
||||
* group as unseen.
|
||||
*
|
||||
* @param group Group being added to (will be null if a group has not yet
|
||||
* been created)
|
||||
@ -584,10 +566,14 @@ public class GroupManager {
|
||||
//if there wasn't already a DrawableGroup, then check if this group is now
|
||||
// in an appropriate state to get one made.
|
||||
// Path group, for example, only gets a DrawableGroup created when all files are analyzed
|
||||
/* NOTE: With the current (Jan 2019) behavior of how we detect a PATH group as being analyzed, the group
|
||||
* is not marked as analyzed until we add a file for another folder. So, when the last picture in a folder
|
||||
* is added to the group, the call to 'populateIfAnalyzed' will still not return a group and therefore this
|
||||
* method will never mark the group as unseen. */
|
||||
/*
|
||||
* NOTE: With the current (Jan 2019) behavior of how we detect a
|
||||
* PATH group as being analyzed, the group is not marked as analyzed
|
||||
* until we add a file for another folder. So, when the last picture
|
||||
* in a folder is added to the group, the call to
|
||||
* 'populateIfAnalyzed' will still not return a group and therefore
|
||||
* this method will never mark the group as unseen.
|
||||
*/
|
||||
group = popuplateIfAnalyzed(groupKey, null);
|
||||
} else {
|
||||
//if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it.
|
||||
@ -605,18 +591,14 @@ public class GroupManager {
|
||||
GroupKey<?> groupKey = null;
|
||||
final ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo();
|
||||
final TagName deletedTagName = deletedTagInfo.getName();
|
||||
if (getGroupBy() == DrawableAttribute.CATEGORY && CategoryManager.isCategoryTagName(deletedTagName)) {
|
||||
groupKey = new GroupKey<>(DrawableAttribute.CATEGORY, CategoryManager.categoryFromTagName(deletedTagName), null);
|
||||
} else if (getGroupBy() == DrawableAttribute.TAGS && CategoryManager.isNotCategoryTagName(deletedTagName)) {
|
||||
if (getGroupBy() == DrawableAttribute.CATEGORY && controller.getCategoryManager().isCategoryTagName(deletedTagName)) {
|
||||
groupKey = new GroupKey<>(DrawableAttribute.CATEGORY, deletedTagName, null);
|
||||
} else if (getGroupBy() == DrawableAttribute.TAGS && controller.getCategoryManager().isNotCategoryTagName(deletedTagName)) {
|
||||
groupKey = new GroupKey<>(DrawableAttribute.TAGS, deletedTagName, null);
|
||||
}
|
||||
if (groupKey != null) {
|
||||
final long fileID = deletedTagInfo.getContentID();
|
||||
DrawableGroup g = removeFromGroup(groupKey, fileID);
|
||||
|
||||
if (controller.getCategoryManager().getTagName(DhsImageCategory.ZERO).equals(deletedTagName) == false) {
|
||||
addFileToGroup(null, new GroupKey<>(DrawableAttribute.CATEGORY, DhsImageCategory.ZERO, null), fileID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -653,7 +635,7 @@ public class GroupManager {
|
||||
try {
|
||||
DrawableFile file = getDrawableDB().getFileFromID(fileId);
|
||||
String pathVal = file.getDrawablePath();
|
||||
GroupKey<?> pathGroupKey = new GroupKey<>(DrawableAttribute.PATH,pathVal, file.getDataSource());
|
||||
GroupKey<?> pathGroupKey = new GroupKey<>(DrawableAttribute.PATH, pathVal, file.getDataSource());
|
||||
|
||||
updateCurrentPathGroup(pathGroupKey);
|
||||
} catch (TskCoreException | TskDataException ex) {
|
||||
@ -674,17 +656,18 @@ public class GroupManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given path is different from the current path group.
|
||||
* If so, updates the current path group as analyzed, and sets current path
|
||||
* group to the given path.
|
||||
* Checks if the given path is different from the current path group. If so,
|
||||
* updates the current path group as analyzed, and sets current path group
|
||||
* to the given path.
|
||||
*
|
||||
* The idea is that when the path of the files being processed changes,
|
||||
* we have moved from one folder to the next, and the group for the
|
||||
* previous PATH can be considered as analyzed and can be displayed.
|
||||
* The idea is that when the path of the files being processed changes, we
|
||||
* have moved from one folder to the next, and the group for the previous
|
||||
* PATH can be considered as analyzed and can be displayed.
|
||||
*
|
||||
* NOTE: this a close approximation for when all files in a folder have been processed,
|
||||
* but there's some room for error - files may go down the ingest pipleline
|
||||
* out of order or the events may not always arrive in the same order
|
||||
* NOTE: this a close approximation for when all files in a folder have been
|
||||
* processed, but there's some room for error - files may go down the ingest
|
||||
* pipleline out of order or the events may not always arrive in the same
|
||||
* order
|
||||
*
|
||||
* @param groupKey
|
||||
*/
|
||||
@ -694,8 +677,7 @@ public class GroupManager {
|
||||
|
||||
if (this.currentPathGroup == null) {
|
||||
currentPathGroup = groupKey;
|
||||
}
|
||||
else if (groupKey.getValue().toString().equalsIgnoreCase(this.currentPathGroup.getValue().toString()) == false) {
|
||||
} else if (groupKey.getValue().toString().equalsIgnoreCase(this.currentPathGroup.getValue().toString()) == false) {
|
||||
// mark the last path group as analyzed
|
||||
getDrawableDB().markGroupAnalyzed(currentPathGroup);
|
||||
popuplateIfAnalyzed(currentPathGroup, null);
|
||||
@ -703,14 +685,14 @@ public class GroupManager {
|
||||
currentPathGroup = groupKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TskCoreException ex) {
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error setting is_analyzed status for group: %s", groupKey.getValue().toString()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets current path group, after marking the current path group as analyzed.
|
||||
* Resets current path group, after marking the current path group as
|
||||
* analyzed.
|
||||
*/
|
||||
synchronized public void resetCurrentPathGroup() {
|
||||
try {
|
||||
@ -719,11 +701,11 @@ public class GroupManager {
|
||||
popuplateIfAnalyzed(currentPathGroup, null);
|
||||
currentPathGroup = null;
|
||||
}
|
||||
}
|
||||
catch (TskCoreException ex) {
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error resetting last path group: %s", currentPathGroup.getValue().toString()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the group is analyzed (or other criteria based on grouping) and should
|
||||
* be shown to the user, then add it to the appropriate data structures so
|
||||
@ -770,10 +752,10 @@ public class GroupManager {
|
||||
}
|
||||
|
||||
// Add to analyzedGroups only if it's the a group with the selected groupBy attribute
|
||||
if ((analyzedGroups.contains(group) == false) &&
|
||||
(getGroupBy() == group.getGroupKey().getAttribute())) {
|
||||
analyzedGroups.add(group);
|
||||
sortAnalyzedGroups();
|
||||
if ((analyzedGroups.contains(group) == false)
|
||||
&& (getGroupBy() == group.getGroupKey().getAttribute())) {
|
||||
analyzedGroups.add(group);
|
||||
sortAnalyzedGroups();
|
||||
}
|
||||
updateUnSeenGroups(group);
|
||||
|
||||
@ -944,11 +926,11 @@ public class GroupManager {
|
||||
switch (groupBy.attrName) {
|
||||
//these cases get special treatment
|
||||
case CATEGORY:
|
||||
results.putAll(null, Arrays.asList(DhsImageCategory.values()));
|
||||
results.putAll(null, controller.getCategoryManager().getCategories());
|
||||
break;
|
||||
case TAGS:
|
||||
results.putAll(null, controller.getTagsManager().getTagNamesInUse().stream()
|
||||
.filter(CategoryManager::isNotCategoryTagName)
|
||||
.filter(controller.getCategoryManager()::isNotCategoryTagName)
|
||||
.collect(Collectors.toList()));
|
||||
break;
|
||||
|
||||
|
@ -63,9 +63,7 @@ public class GroupSortBy implements Comparator<DrawableGroup> {
|
||||
*/
|
||||
public final static GroupSortBy PRIORITY
|
||||
= new GroupSortBy(Bundle.GroupSortBy_priority(), "hashset_hits.png",
|
||||
Comparator.comparing(DrawableGroup::getHashHitDensity)
|
||||
.thenComparing(Comparator.comparing(DrawableGroup::getUncategorizedCount))
|
||||
.reversed());
|
||||
Comparator.comparing(DrawableGroup::getHashHitDensity).reversed());
|
||||
|
||||
private final static ObservableList<GroupSortBy> values = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(PRIORITY, NONE, GROUP_BY_VALUE, FILE_COUNT));
|
||||
|
||||
|
@ -56,7 +56,7 @@ public final class GuiUtils {
|
||||
|
||||
/**
|
||||
* Create a MenuItem that performs the given action and also set the Action
|
||||
* as the action for the given Button. Usefull to have a SplitMenuButton
|
||||
* as the action for the given Button. Useful to have a SplitMenuButton
|
||||
* remember the last chosen menu item as its action.
|
||||
*
|
||||
* @param button
|
||||
|
@ -34,10 +34,10 @@ import javafx.scene.layout.VBox;
|
||||
import javafx.util.Pair;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager.CategoryChangeEvent;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
|
||||
/**
|
||||
* Displays summary statistics (counts) for each group
|
||||
@ -45,13 +45,13 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager.CategoryChan
|
||||
public class SummaryTablePane extends AnchorPane {
|
||||
|
||||
@FXML
|
||||
private TableColumn<Pair<DhsImageCategory, Long>, String> catColumn;
|
||||
private TableColumn<Pair<TagName, Long>, String> catColumn;
|
||||
|
||||
@FXML
|
||||
private TableColumn<Pair<DhsImageCategory, Long>, Long> countColumn;
|
||||
private TableColumn<Pair<TagName, Long>, Long> countColumn;
|
||||
|
||||
@FXML
|
||||
private TableView<Pair<DhsImageCategory, Long>> tableView;
|
||||
private TableView<Pair<TagName, Long>> tableView;
|
||||
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
@ -97,9 +97,9 @@ public class SummaryTablePane extends AnchorPane {
|
||||
*/
|
||||
@Subscribe
|
||||
public void handleCategoryChanged(CategoryChangeEvent evt) {
|
||||
final ObservableList<Pair<DhsImageCategory, Long>> data = FXCollections.observableArrayList();
|
||||
final ObservableList<Pair<TagName, Long>> data = FXCollections.observableArrayList();
|
||||
if (Case.isCaseOpen()) {
|
||||
for (DhsImageCategory cat : DhsImageCategory.values()) {
|
||||
for (TagName cat : controller.getCategoryManager().getCategories()) {
|
||||
data.add(new Pair<>(cat, controller.getCategoryManager().getCategoryCount(cat)));
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,9 @@ package org.sleuthkit.autopsy.imagegallery.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -64,7 +61,6 @@ import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED;
|
||||
import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_DELETED;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeGroupAction;
|
||||
@ -220,13 +216,13 @@ public class Toolbar extends ToolBar {
|
||||
});
|
||||
initTagMenuButton();
|
||||
|
||||
CategorizeGroupAction cat5GroupAction = new CategorizeGroupAction(DhsImageCategory.FIVE, controller);
|
||||
CategorizeGroupAction cat5GroupAction = new CategorizeGroupAction(controller.getCategoryManager().getCategories().get(0), controller);
|
||||
catGroupMenuButton.setOnAction(cat5GroupAction);
|
||||
catGroupMenuButton.setText(cat5GroupAction.getText());
|
||||
catGroupMenuButton.setGraphic(cat5GroupAction.getGraphic());
|
||||
catGroupMenuButton.showingProperty().addListener(showing -> {
|
||||
if (catGroupMenuButton.isShowing()) {
|
||||
List<MenuItem> categoryMenues = Lists.transform(Arrays.asList(DhsImageCategory.values()),
|
||||
List<MenuItem> categoryMenues = Lists.transform(controller.getCategoryManager().getCategories(),
|
||||
cat -> GuiUtils.createAutoAssigningMenuItem(catGroupMenuButton, new CategorizeGroupAction(cat, controller)));
|
||||
catGroupMenuButton.getItems().setAll(categoryMenues);
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import javafx.application.Platform;
|
||||
@ -34,10 +36,11 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TagName.HTML_COLOR;
|
||||
|
||||
/**
|
||||
* Interface for classes that are views of a single DrawableFile. Implementation
|
||||
@ -54,19 +57,9 @@ public interface DrawableView {
|
||||
|
||||
static final CornerRadii CAT_CORNER_RADII = new CornerRadii(3);
|
||||
|
||||
static final Border HASH_BORDER = new Border(new BorderStroke(Color.PURPLE, BorderStrokeStyle.DASHED, CAT_CORNER_RADII, CAT_BORDER_WIDTHS));
|
||||
Border HASH_BORDER = new Border(new BorderStroke(Color.CYAN, BorderStrokeStyle.DASHED, CAT_CORNER_RADII, CAT_BORDER_WIDTHS));
|
||||
|
||||
static final Border CAT1_BORDER = new Border(new BorderStroke(DhsImageCategory.ONE.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS));
|
||||
|
||||
static final Border CAT2_BORDER = new Border(new BorderStroke(DhsImageCategory.TWO.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS));
|
||||
|
||||
static final Border CAT3_BORDER = new Border(new BorderStroke(DhsImageCategory.THREE.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS));
|
||||
|
||||
static final Border CAT4_BORDER = new Border(new BorderStroke(DhsImageCategory.FOUR.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS));
|
||||
|
||||
static final Border CAT5_BORDER = new Border(new BorderStroke(DhsImageCategory.FIVE.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS));
|
||||
|
||||
static final Border CAT0_BORDER = new Border(new BorderStroke(DhsImageCategory.ZERO.getColor(), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS));
|
||||
Map<String, Border> BORDER_MAP = new HashMap<>();
|
||||
|
||||
Region getCategoryBorderRegion();
|
||||
|
||||
@ -115,38 +108,38 @@ public interface DrawableView {
|
||||
|
||||
}
|
||||
|
||||
static Border getCategoryBorder(DhsImageCategory category) {
|
||||
if (category != null) {
|
||||
switch (category) {
|
||||
case ONE:
|
||||
return CAT1_BORDER;
|
||||
case TWO:
|
||||
return CAT2_BORDER;
|
||||
case THREE:
|
||||
return CAT3_BORDER;
|
||||
case FOUR:
|
||||
return CAT4_BORDER;
|
||||
case FIVE:
|
||||
return CAT5_BORDER;
|
||||
case ZERO:
|
||||
default:
|
||||
return CAT0_BORDER;
|
||||
/**
|
||||
* Get the boarder for the given category.
|
||||
*
|
||||
* Static instances of the boarders will lazily constructed and stored in
|
||||
* the BORDER_MAP.
|
||||
*
|
||||
* @param category
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static Border getCategoryBorder(TagName category) {
|
||||
Border border = null;
|
||||
if (category != null && category.getColor() != HTML_COLOR.NONE) {
|
||||
border = BORDER_MAP.get(category.getDisplayName());
|
||||
|
||||
if (border == null) {
|
||||
border = new Border(new BorderStroke(Color.web(category.getColor().getRgbValue()), BorderStrokeStyle.SOLID, CAT_CORNER_RADII, CAT_BORDER_WIDTHS));
|
||||
BORDER_MAP.put(category.getDisplayName(), border);
|
||||
}
|
||||
} else {
|
||||
return CAT0_BORDER;
|
||||
}
|
||||
return border;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.ANY)
|
||||
default DhsImageCategory updateCategory() {
|
||||
default TagName updateCategory() {
|
||||
if (getFile().isPresent()) {
|
||||
final DhsImageCategory category = getFile().map(DrawableFile::getCategory).orElse(DhsImageCategory.ZERO);
|
||||
final Border border = hasHashHit() && (category == DhsImageCategory.ZERO) ? HASH_BORDER : getCategoryBorder(category);
|
||||
final TagName tagNameCat = getFile().map(DrawableFile::getCategory).orElse(null);
|
||||
final Border border = hasHashHit() ? HASH_BORDER : getCategoryBorder(tagNameCat);
|
||||
Platform.runLater(() -> getCategoryBorderRegion().setBorder(border));
|
||||
return category;
|
||||
return tagNameCat;
|
||||
} else {
|
||||
return DhsImageCategory.ZERO;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,35 +182,6 @@
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
<HBox fx:id="catSegmentedContainer" alignment="CENTER" maxWidth="-Infinity" spacing="5.0">
|
||||
<children>
|
||||
<Label fx:id="catHeadingLabel" text="Category:">
|
||||
<graphic>
|
||||
<ImageView fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true">
|
||||
<image>
|
||||
<Image url="@../../images/category-icon.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</Label>
|
||||
<SegmentedButton fx:id="catSegmentedButton">
|
||||
<buttons>
|
||||
<RadioButton fx:id="cat0Toggle" mnemonicParsing="false" text="0" HBox.hgrow="ALWAYS">
|
||||
<toggleGroup>
|
||||
<ToggleGroup fx:id="cat" />
|
||||
</toggleGroup></RadioButton>
|
||||
<RadioButton fx:id="cat1Toggle" mnemonicParsing="false" style="" styleClass="button" text="1" toggleGroup="$cat" HBox.hgrow="ALWAYS" />
|
||||
<RadioButton id="Cat2Toggle" fx:id="cat2Toggle" mnemonicParsing="false" styleClass="button" text="2" toggleGroup="$cat" HBox.hgrow="ALWAYS" />
|
||||
<RadioButton fx:id="cat3Toggle" mnemonicParsing="false" styleClass="button" text="3" toggleGroup="$cat" HBox.hgrow="ALWAYS" />
|
||||
<RadioButton fx:id="cat4Toggle" mnemonicParsing="false" styleClass="button" text="4" toggleGroup="$cat" HBox.hgrow="ALWAYS" />
|
||||
<RadioButton fx:id="cat5Toggle" mnemonicParsing="false" text="5" toggleGroup="$cat" HBox.hgrow="ALWAYS" />
|
||||
</buttons>
|
||||
</SegmentedButton>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets left="5.0" />
|
||||
</padding>
|
||||
</HBox>
|
||||
</items>
|
||||
</ToolBar>
|
||||
</children>
|
||||
|
@ -19,12 +19,10 @@
|
||||
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import static com.google.common.collect.Lists.transform;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import static java.util.Arrays.asList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -50,7 +48,6 @@ import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
@ -86,10 +83,7 @@ import static javafx.scene.input.KeyCode.UP;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Border;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.BorderStroke;
|
||||
import javafx.scene.layout.BorderStrokeStyle;
|
||||
import javafx.scene.layout.BorderWidths;
|
||||
import javafx.scene.layout.CornerRadii;
|
||||
import javafx.scene.layout.HBox;
|
||||
@ -111,7 +105,6 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.ContextMenuActionsProvider;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
||||
@ -134,6 +127,7 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||
import static org.sleuthkit.autopsy.imagegallery.gui.GuiUtils.createAutoAssigningMenuItem;
|
||||
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
|
||||
import static org.sleuthkit.autopsy.imagegallery.utils.TaskUtils.addFXCallback;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -176,19 +170,6 @@ public class GroupPane extends BorderPane {
|
||||
private SplitMenuButton tagSelectedSplitMenu;
|
||||
@FXML
|
||||
private ToolBar headerToolBar;
|
||||
@FXML
|
||||
private ToggleButton cat0Toggle;
|
||||
@FXML
|
||||
private ToggleButton cat1Toggle;
|
||||
@FXML
|
||||
private ToggleButton cat2Toggle;
|
||||
@FXML
|
||||
private ToggleButton cat3Toggle;
|
||||
@FXML
|
||||
private ToggleButton cat4Toggle;
|
||||
@FXML
|
||||
private ToggleButton cat5Toggle;
|
||||
|
||||
@FXML
|
||||
private SegmentedButton segButton;
|
||||
|
||||
@ -220,11 +201,6 @@ public class GroupPane extends BorderPane {
|
||||
@FXML
|
||||
private Label catContainerLabel;
|
||||
@FXML
|
||||
private Label catHeadingLabel;
|
||||
|
||||
@FXML
|
||||
private HBox catSegmentedContainer;
|
||||
@FXML
|
||||
private HBox catSplitMenuContainer;
|
||||
|
||||
private final ListeningExecutorService exec = TaskUtils.getExecutorForClass(GroupPane.class);
|
||||
@ -244,12 +220,18 @@ public class GroupPane extends BorderPane {
|
||||
|
||||
private ContextMenu contextMenu;
|
||||
|
||||
/** the current GroupViewMode of this GroupPane */
|
||||
/**
|
||||
* the current GroupViewMode of this GroupPane
|
||||
*/
|
||||
private final SimpleObjectProperty<GroupViewMode> groupViewMode = new SimpleObjectProperty<>(GroupViewMode.TILE);
|
||||
|
||||
/** the grouping this pane is currently the view for */
|
||||
/**
|
||||
* the grouping this pane is currently the view for
|
||||
*/
|
||||
private final ReadOnlyObjectWrapper<DrawableGroup> grouping = new ReadOnlyObjectWrapper<>();
|
||||
|
||||
private final Map<String, ToggleButton> toggleButtonMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Map from fileIDs to their assigned cells in the tile view. This is used
|
||||
* to determine whether fileIDs are visible or are offscreen. No entry
|
||||
@ -307,7 +289,35 @@ public class GroupPane extends BorderPane {
|
||||
}
|
||||
|
||||
void syncCatToggle(DrawableFile file) {
|
||||
getToggleForCategory(file.getCategory()).setSelected(true);
|
||||
TagName tagName = file.getCategory();
|
||||
if (tagName != null) {
|
||||
getToggleForCategory(tagName).setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a toggle button for the given TagName.
|
||||
*
|
||||
* @param tagName TagName to create a button for.
|
||||
*
|
||||
* @return A new instance of a ToggleButton.
|
||||
*/
|
||||
private ToggleButton getToggleForCategory(TagName tagName) {
|
||||
|
||||
ToggleButton button = toggleButtonMap.get(tagName.getDisplayName());
|
||||
|
||||
if (button == null) {
|
||||
String[] split = tagName.getDisplayName().split(":");
|
||||
split = split[0].split("-");
|
||||
|
||||
int category = Integer.parseInt(split[1]);
|
||||
|
||||
button = new ToggleButton();
|
||||
button.setText(Integer.toString(category));
|
||||
|
||||
toggleButtonMap.put(tagName.getDisplayName(), button);
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
public void activateTileViewer() {
|
||||
@ -353,25 +363,6 @@ public class GroupPane extends BorderPane {
|
||||
return grouping.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
private ToggleButton getToggleForCategory(DhsImageCategory category) {
|
||||
switch (category) {
|
||||
case ZERO:
|
||||
return cat0Toggle;
|
||||
case ONE:
|
||||
return cat1Toggle;
|
||||
case TWO:
|
||||
return cat2Toggle;
|
||||
case THREE:
|
||||
return cat3Toggle;
|
||||
case FOUR:
|
||||
return cat4Toggle;
|
||||
case FIVE:
|
||||
return cat5Toggle;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown category: " + category.name());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called automatically during constructor by FXMLConstructor.
|
||||
*
|
||||
@ -384,12 +375,6 @@ public class GroupPane extends BorderPane {
|
||||
"GroupPane.catContainerLabel.displayText=Categorize Selected File:",
|
||||
"GroupPane.catHeadingLabel.displayText=Category:"})
|
||||
void initialize() {
|
||||
assert cat0Toggle != null : "fx:id=\"cat0Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
assert cat1Toggle != null : "fx:id=\"cat1Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
assert cat2Toggle != null : "fx:id=\"cat2Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
assert cat3Toggle != null : "fx:id=\"cat3Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
assert cat4Toggle != null : "fx:id=\"cat4Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
assert cat5Toggle != null : "fx:id=\"cat5Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
assert gridView != null : "fx:id=\"tilePane\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
assert catSelectedSplitMenu != null : "fx:id=\"grpCatSplitMenu\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
assert tagSelectedSplitMenu != null : "fx:id=\"grpTagSplitMenu\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
@ -399,21 +384,6 @@ public class GroupPane extends BorderPane {
|
||||
assert tileToggle != null : "fx:id=\"tileToggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
assert seenByOtherExaminersCheckBox != null : "fx:id=\"seenByOtherExaminersCheckBox\" was not injected: check your FXML file 'GroupPane.fxml'.";
|
||||
|
||||
for (DhsImageCategory cat : DhsImageCategory.values()) {
|
||||
ToggleButton toggleForCategory = getToggleForCategory(cat);
|
||||
toggleForCategory.setBorder(new Border(new BorderStroke(cat.getColor(), BorderStrokeStyle.SOLID, CORNER_RADII_2, BORDER_WIDTHS_2)));
|
||||
toggleForCategory.getStyleClass().remove("radio-button");
|
||||
toggleForCategory.getStyleClass().add("toggle-button");
|
||||
toggleForCategory.selectedProperty().addListener((ov, wasSelected, toggleSelected) -> {
|
||||
if (toggleSelected && slideShowPane != null) {
|
||||
slideShowPane.getFileID().ifPresent(fileID -> {
|
||||
selectionModel.clearAndSelect(fileID);
|
||||
new CategorizeAction(controller, cat, ImmutableSet.of(fileID)).handle(null);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//configure flashing glow animation on next unseen group button
|
||||
flashAnimation.setCycleCount(Timeline.INDEFINITE);
|
||||
flashAnimation.setAutoReverse(true);
|
||||
@ -447,14 +417,14 @@ public class GroupPane extends BorderPane {
|
||||
},
|
||||
throwable -> logger.log(Level.SEVERE, "Error getting tag names.", throwable)//NON-NLS
|
||||
);
|
||||
CategorizeSelectedFilesAction cat5SelectedAction = new CategorizeSelectedFilesAction(DhsImageCategory.FIVE, controller);
|
||||
CategorizeSelectedFilesAction cat5SelectedAction = new CategorizeSelectedFilesAction(controller.getCategoryManager().getCategories().get(0), controller);
|
||||
|
||||
catSelectedSplitMenu.setOnAction(cat5SelectedAction);
|
||||
|
||||
catSelectedSplitMenu.setText(cat5SelectedAction.getText());
|
||||
catSelectedSplitMenu.setGraphic(cat5SelectedAction.getGraphic());
|
||||
|
||||
List<MenuItem> categoryMenues = transform(asList(DhsImageCategory.values()),
|
||||
List<MenuItem> categoryMenues = transform(controller.getCategoryManager().getCategories(),
|
||||
cat -> createAutoAssigningMenuItem(catSelectedSplitMenu, new CategorizeSelectedFilesAction(cat, controller)));
|
||||
catSelectedSplitMenu.getItems().setAll(categoryMenues);
|
||||
|
||||
@ -466,16 +436,21 @@ public class GroupPane extends BorderPane {
|
||||
bottomLabel.setText(Bundle.GroupPane_bottomLabel_displayText());
|
||||
headerLabel.setText(Bundle.GroupPane_hederLabel_displayText());
|
||||
catContainerLabel.setText(Bundle.GroupPane_catContainerLabel_displayText());
|
||||
catHeadingLabel.setText(Bundle.GroupPane_catHeadingLabel_displayText());
|
||||
//show categorization controls depending on group view mode
|
||||
headerToolBar.getItems().remove(catSegmentedContainer);
|
||||
|
||||
// This seems to be the only way to make sure the when the user switches
|
||||
// to SLIDE_SHOW the first time that the undo\redo buttons are removed.
|
||||
headerToolBar.getItems().remove(undoButton);
|
||||
headerToolBar.getItems().remove(redoButton);
|
||||
headerToolBar.getItems().add(undoButton);
|
||||
headerToolBar.getItems().add(redoButton);
|
||||
|
||||
groupViewMode.addListener((ObservableValue<? extends GroupViewMode> observable, GroupViewMode oldValue, GroupViewMode newValue) -> {
|
||||
if (newValue == GroupViewMode.SLIDE_SHOW) {
|
||||
headerToolBar.getItems().remove(catSplitMenuContainer);
|
||||
headerToolBar.getItems().add(catSegmentedContainer);
|
||||
headerToolBar.getItems().remove(undoButton);
|
||||
headerToolBar.getItems().remove(redoButton);
|
||||
} else {
|
||||
headerToolBar.getItems().remove(catSegmentedContainer);
|
||||
headerToolBar.getItems().add(catSplitMenuContainer);
|
||||
headerToolBar.getItems().add(undoButton);
|
||||
headerToolBar.getItems().add(redoButton);
|
||||
}
|
||||
});
|
||||
|
||||
@ -527,7 +502,7 @@ public class GroupPane extends BorderPane {
|
||||
//listen to tile selection and make sure it is visible in scroll area
|
||||
selectionModel.lastSelectedProperty().addListener((observable, oldFileID, newFileId) -> {
|
||||
if (groupViewMode.get() == GroupViewMode.SLIDE_SHOW
|
||||
&& slideShowPane != null) {
|
||||
&& slideShowPane != null) {
|
||||
slideShowPane.setFile(newFileId);
|
||||
} else {
|
||||
scrollToFileID(newFileId);
|
||||
@ -775,42 +750,9 @@ public class GroupPane extends BorderPane {
|
||||
selectAllFiles();
|
||||
t.consume();
|
||||
}
|
||||
ObservableSet<Long> selected = selectionModel.getSelected();
|
||||
if (selected.isEmpty() == false) {
|
||||
DhsImageCategory cat = keyCodeToCat(t.getCode());
|
||||
if (cat != null) {
|
||||
new CategorizeAction(controller, cat, selected).handle(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DhsImageCategory keyCodeToCat(KeyCode t) {
|
||||
if (t != null) {
|
||||
switch (t) {
|
||||
case NUMPAD0:
|
||||
case DIGIT0:
|
||||
return DhsImageCategory.ZERO;
|
||||
case NUMPAD1:
|
||||
case DIGIT1:
|
||||
return DhsImageCategory.ONE;
|
||||
case NUMPAD2:
|
||||
case DIGIT2:
|
||||
return DhsImageCategory.TWO;
|
||||
case NUMPAD3:
|
||||
case DIGIT3:
|
||||
return DhsImageCategory.THREE;
|
||||
case NUMPAD4:
|
||||
case DIGIT4:
|
||||
return DhsImageCategory.FOUR;
|
||||
case NUMPAD5:
|
||||
case DIGIT5:
|
||||
return DhsImageCategory.FIVE;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void handleArrows(KeyEvent t) {
|
||||
Long lastSelectFileId = selectionModel.lastSelectedProperty().get();
|
||||
|
||||
|
@ -19,9 +19,9 @@
|
||||
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -56,7 +56,6 @@ import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||
@ -165,20 +164,50 @@ public class MetaDataPane extends DrawableUIBase {
|
||||
titledPane.setText(Bundle.MetaDataPane_titledPane_displayName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display string for the given pair.
|
||||
*
|
||||
* @param p A DrawableAttribute and its collection.
|
||||
*
|
||||
* @return The string to display.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static private String getValueDisplayString(Pair<DrawableAttribute<?>, Collection<?>> p) {
|
||||
if (p.getKey() == DrawableAttribute.TAGS) {
|
||||
return ((Collection<TagName>) p.getValue()).stream()
|
||||
.map(TagName::getDisplayName)
|
||||
.filter(DhsImageCategory::isNotCategoryName)
|
||||
.collect(Collectors.joining(" ; "));
|
||||
private String getValueDisplayString(Pair<DrawableAttribute<?>, Collection<?>> p) {
|
||||
if (p.getKey() == DrawableAttribute.TAGS || p.getKey() == DrawableAttribute.CATEGORY) {
|
||||
return getTagDisplayNames((Collection<TagName>) p.getValue(), p.getKey());
|
||||
} else {
|
||||
return p.getValue().stream()
|
||||
.map(value -> Objects.toString(value, ""))
|
||||
.collect(Collectors.joining(" ; "));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the list of TagName displayNames for either Tags or Categories.
|
||||
*
|
||||
* @param tagNameList List of TagName values
|
||||
* @param attribute A DrawableAttribute value either CATEGORY or TAGS
|
||||
*
|
||||
* @return A list of TagNames separated by ; or an empty string.
|
||||
*/
|
||||
private String getTagDisplayNames(Collection<TagName> tagNameList, DrawableAttribute<?> attribute) {
|
||||
String displayStr = "";
|
||||
CategoryManager controller = getController().getCategoryManager();
|
||||
List<String> nameList = new ArrayList<>();
|
||||
if (tagNameList != null && !tagNameList.isEmpty()) {
|
||||
for (TagName tagName : tagNameList) {
|
||||
if ((attribute == DrawableAttribute.CATEGORY && controller.isCategoryTagName(tagName))
|
||||
|| (attribute == DrawableAttribute.TAGS && !controller.isCategoryTagName(tagName))) {
|
||||
nameList.add(tagName.getDisplayName());
|
||||
}
|
||||
}
|
||||
displayStr = String.join(";", nameList);
|
||||
}
|
||||
|
||||
return displayStr;
|
||||
}
|
||||
|
||||
@Override
|
||||
synchronized protected void setFileHelper(Long newFileID) {
|
||||
setFileIDOpt(Optional.ofNullable(newFileID));
|
||||
|
@ -50,12 +50,12 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
|
||||
import org.sleuthkit.autopsy.imagegallery.gui.VideoPlayer;
|
||||
import static org.sleuthkit.autopsy.imagegallery.gui.drawableviews.DrawableUIBase.exec;
|
||||
import static org.sleuthkit.autopsy.imagegallery.gui.drawableviews.DrawableView.CAT_BORDER_WIDTH;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
|
||||
/**
|
||||
* Displays the files of a group one at a time. Designed to be embedded in a
|
||||
@ -297,14 +297,14 @@ public class SlideShowView extends DrawableTileBase {
|
||||
|
||||
@Override
|
||||
@ThreadConfined(type = ThreadType.ANY)
|
||||
public DhsImageCategory updateCategory() {
|
||||
public TagName updateCategory() {
|
||||
Optional<DrawableFile> file = getFile();
|
||||
if (file.isPresent()) {
|
||||
DhsImageCategory updateCategory = super.updateCategory();
|
||||
TagName updateCategory = super.updateCategory();
|
||||
Platform.runLater(() -> getGroupPane().syncCatToggle(file.get()));
|
||||
return updateCategory;
|
||||
} else {
|
||||
return DhsImageCategory.ZERO;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,6 +117,7 @@ class GroupCellFactory {
|
||||
final Node graphic = (group.getGroupByAttribute() == DrawableAttribute.TAGS)
|
||||
? controller.getTagsManager().getGraphic((TagName) group.getGroupByValue())
|
||||
: group.getGroupKey().getGraphic();
|
||||
|
||||
final String text = getCellText(cell);
|
||||
final String style = getSeenStyleClass(cell);
|
||||
|
||||
@ -157,10 +158,10 @@ class GroupCellFactory {
|
||||
*/
|
||||
private String getCountsText(GroupCell<?> cell) {
|
||||
return cell.getGroup()
|
||||
.map(group ->
|
||||
" (" + (sortOrder.get() == GroupComparators.ALPHABETICAL
|
||||
? group.getSize()
|
||||
: sortOrder.get().getFormattedValueOfGroup(group)) + ")"
|
||||
.map(group
|
||||
-> " (" + (sortOrder.get() == GroupComparators.ALPHABETICAL
|
||||
? group.getSize()
|
||||
: sortOrder.get().getFormattedValueOfGroup(group)) + ")"
|
||||
).orElse(""); //if item is null or group is null
|
||||
}
|
||||
|
||||
|
@ -114,12 +114,8 @@ final class ExtractSru extends Extract {
|
||||
|
||||
AbstractFile sruAbstractFile = getSruFile(dataSource, tempDirPath);
|
||||
|
||||
String sruFileName = tempDirPath + File.separator + sruAbstractFile.getId() + "_" + sruAbstractFile.getName();
|
||||
|
||||
if (sruFileName == null) {
|
||||
this.addErrorMessage(Bundle.ExtractSru_process_errormsg_find_srudb_dat());
|
||||
logger.log(Level.SEVERE, "SRUDB.dat file not found"); //NON-NLS
|
||||
return; //If we cannot find the srudb.dat file we cannot proceed
|
||||
if (sruAbstractFile == null) {
|
||||
return; //If we cannot find the srudb.dat file we cannot proceed which is ok
|
||||
}
|
||||
|
||||
final String sruDumper = getPathForSruDumper();
|
||||
@ -135,6 +131,7 @@ final class ExtractSru extends Extract {
|
||||
|
||||
try {
|
||||
String modOutFile = modOutPath + File.separator + sruAbstractFile.getId() + "_srudb.db3";
|
||||
String sruFileName = tempDirPath + File.separator + sruAbstractFile.getId() + "_" + sruAbstractFile.getName();
|
||||
|
||||
extractSruFiles(sruDumper, sruFileName, modOutFile, tempDirPath, softwareHiveFileName);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user