Added delete tag capability to new tags api

This commit is contained in:
Richard Cordovano 2013-10-16 18:26:32 -04:00
parent d74fa2e894
commit b65a9e6d5a
19 changed files with 432 additions and 636 deletions

View File

@ -46,6 +46,7 @@ public class AddBlackboardArtifactTagAction extends AddTagAction {
} }
private AddBlackboardArtifactTagAction() { private AddBlackboardArtifactTagAction() {
super("");
} }
@Override @Override
@ -57,7 +58,6 @@ public class AddBlackboardArtifactTagAction extends AddTagAction {
protected void addTag(TagName tagName, String comment) { protected void addTag(TagName tagName, String comment) {
Collection<? extends BlackboardArtifact> selectedArtifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class); Collection<? extends BlackboardArtifact> selectedArtifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class);
for (BlackboardArtifact artifact : selectedArtifacts) { for (BlackboardArtifact artifact : selectedArtifacts) {
Tags.createTag(artifact, tagName.getDisplayName(), comment); //RJCTODO: Jettision this
try { try {
Case.getCurrentCase().getServices().getTagsManager().addBlackboardArtifactTag(artifact, tagName, comment); Case.getCurrentCase().getServices().getTagsManager().addBlackboardArtifactTag(artifact, tagName, comment);
} }

View File

@ -46,6 +46,7 @@ public class AddContentTagAction extends AddTagAction {
} }
private AddContentTagAction() { private AddContentTagAction() {
super("");
} }
@Override @Override
@ -57,7 +58,6 @@ public class AddContentTagAction extends AddTagAction {
protected void addTag(TagName tagName, String comment) { protected void addTag(TagName tagName, String comment) {
Collection<? extends AbstractFile> selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); Collection<? extends AbstractFile> selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class);
for (AbstractFile file : selectedFiles) { for (AbstractFile file : selectedFiles) {
Tags.createTag(file, tagName.getDisplayName(), comment); //RJCTODO: Jettision this
try { try {
Case.getCurrentCase().getServices().getTagsManager().addContentTag(file, tagName, comment); Case.getCurrentCase().getServices().getTagsManager().addContentTag(file, tagName, comment);
} }

View File

@ -35,16 +35,20 @@ import org.sleuthkit.datamodel.TagName;
* An abstract base class for Actions that allow users to tag SleuthKit data * An abstract base class for Actions that allow users to tag SleuthKit data
* model objects. * model objects.
*/ */
abstract class AddTagAction extends AbstractAction implements Presenter.Popup { abstract class AddTagAction extends TagAction implements Presenter.Popup {
private static final String NO_COMMENT = ""; private static final String NO_COMMENT = "";
AddTagAction(String menuText) {
super(menuText);
}
@Override @Override
public JMenuItem getPopupPresenter() { public JMenuItem getPopupPresenter() {
return new TagMenu(); return new TagMenu();
} }
@Override @Override
public void actionPerformed(ActionEvent e) { protected void doAction(ActionEvent event) {
} }
/** /**
@ -59,14 +63,6 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
*/ */
abstract protected void addTag(TagName tagName, String comment); abstract protected void addTag(TagName tagName, String comment);
private void refreshDirectoryTree() {
//TODO instead should send event to node children, which will call its refresh() / refreshKeys()
// RJCTODO: Explain what is going on here and pare to one refreshTree() call.
DirectoryTreeTopComponent viewer = DirectoryTreeTopComponent.findInstance();
viewer.refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE);
viewer.refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT);
}
/** /**
* Instances of this class implement a context menu user interface for * Instances of this class implement a context menu user interface for
* creating or selecting a tag name for a tag and specifying an optional tag * creating or selecting a tag name for a tag and specifying an optional tag
@ -90,7 +86,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
// Each tag name in the current set of tags gets its own menu item in // Each tag name in the current set of tags gets its own menu item in
// the "Quick Tags" sub-menu. Selecting one of these menu items adds // the "Quick Tags" sub-menu. Selecting one of these menu items adds
// a tag with the associated tag name. // a tag with the associated tag name.
if (tagNames.isEmpty()) { if (!tagNames.isEmpty()) {
for (final TagName tagName : tagNames) { for (final TagName tagName : tagNames) {
JMenuItem tagNameItem = new JMenuItem(tagName.getDisplayName()); JMenuItem tagNameItem = new JMenuItem(tagName.getDisplayName());
tagNameItem.addActionListener(new ActionListener() { tagNameItem.addActionListener(new ActionListener() {
@ -114,7 +110,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
// The "Quick Tag" menu also gets an "Choose Tag..." menu item. // The "Quick Tag" menu also gets an "Choose Tag..." menu item.
// Selecting this item initiates a dialog that can be used to create // Selecting this item initiates a dialog that can be used to create
// or select a tag name and adds a tag with the resulting name. // or select a tag name and adds a tag with the resulting name.
JMenuItem newTagMenuItem = new JMenuItem("Choose Tag..."); JMenuItem newTagMenuItem = new JMenuItem("New Tag...");
newTagMenuItem.addActionListener(new ActionListener() { newTagMenuItem.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@ -127,10 +123,10 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
}); });
quickTagMenu.add(newTagMenuItem); quickTagMenu.add(newTagMenuItem);
// Create a "Choose Tag and Comment..." menu item. Selecting this itme initiates // Create a "Choose Tag and Comment..." menu item. Selecting this item initiates
// a dialog that can be used to create or select a tag name with an // a dialog that can be used to create or select a tag name with an
// optional comment and adds a tag with the resulting name. // optional comment and adds a tag with the resulting name.
JMenuItem tagAndCommentItem = new JMenuItem("Choose Tag and Comment..."); JMenuItem tagAndCommentItem = new JMenuItem("Tag and Comment...");
tagAndCommentItem.addActionListener(new ActionListener() { tagAndCommentItem.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {

View File

@ -0,0 +1,67 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.actions;
import java.awt.event.ActionEvent;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Instances of this Action allow users to delete tags applied to blackboard artifacts.
*/
public class DeleteBlackboardArtifactTagAction extends TagAction {
private static final String MENU_TEXT = "Delete Tag(s)";
// This class is a singleton to support multi-selection of nodes, since
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
// node in the array returns a reference to the same action object from Node.getActions(boolean).
private static DeleteBlackboardArtifactTagAction instance;
public static synchronized DeleteBlackboardArtifactTagAction getInstance() {
if (null == instance) {
instance = new DeleteBlackboardArtifactTagAction();
}
return instance;
}
private DeleteBlackboardArtifactTagAction() {
super(MENU_TEXT);
}
@Override
protected void doAction(ActionEvent event) {
Collection<? extends BlackboardArtifactTag> selectedTags = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactTag.class);
for (BlackboardArtifactTag tag : selectedTags) {
try {
Case.getCurrentCase().getServices().getTagsManager().deleteBlackboardArtifactTag(tag);
}
catch (TskCoreException ex) {
Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex);
JOptionPane.showMessageDialog(null, "Unable to delete tag " + tag.getName() + ".", "Tag Deletion Error", JOptionPane.ERROR_MESSAGE);
}
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.actions;
import java.awt.event.ActionEvent;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Instances of this Action allow users to delete tags applied to content.
*/
public class DeleteContentTagAction extends TagAction {
private static final String MENU_TEXT = "Delete Tag(s)";
// This class is a singleton to support multi-selection of nodes, since
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
// node in the array returns a reference to the same action object from Node.getActions(boolean).
private static DeleteContentTagAction instance;
public static synchronized DeleteContentTagAction getInstance() {
if (null == instance) {
instance = new DeleteContentTagAction();
}
return instance;
}
private DeleteContentTagAction() {
super(MENU_TEXT);
}
@Override
protected void doAction(ActionEvent e) {
Collection<? extends ContentTag> selectedTags = Utilities.actionsGlobalContext().lookupAll(ContentTag.class);
for (ContentTag tag : selectedTags) {
try {
Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(tag);
}
catch (TskCoreException ex) {
Logger.getLogger(AddContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex);
JOptionPane.showMessageDialog(null, "Unable to delete tag " + tag.getName() + ".", "Tag Deletion Error", JOptionPane.ERROR_MESSAGE);
}
}
}
}

View File

@ -0,0 +1,62 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.actions;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Abstract base class for Actions involving tags.
*/
public abstract class TagAction extends AbstractAction {
public TagAction(String menuText) {
super(menuText);
}
@Override
public void actionPerformed(ActionEvent event) {
doAction(event);
refreshDirectoryTree();
}
/**
* Derived classes must implement this Template Method for actionPerformed().
* @param event ActionEvent object passed to actionPerformed()
*/
abstract protected void doAction(ActionEvent event);
/**
* Derived classes should call this method any time a tag is created, updated
* or deleted outside of an actionPerformed() call.
*/
protected void refreshDirectoryTree() {
// The way the "directory tree" currently works, a new tags sub-tree
// needs to be made to reflect the results of invoking tag Actions. The
// way to do this is to call DirectoryTreeTopComponent.refreshTree(),
// which calls RootContentChildren.refreshKeys(BlackboardArtifact.ARTIFACT_TYPE... types)
// for the RootContentChildren object that is the child factory for the
// ResultsNode that is the root of the tags sub-tree. There is a switch
// statement in RootContentChildren.refreshKeys() that maps both
// BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE and BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT
// to making a call to refreshKey(TagsNodeKey).
DirectoryTreeTopComponent.findInstance().refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE);
}
}

View File

@ -37,20 +37,105 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A singleton instance of this class functions as an Autopsy service that * A singleton instance of this class functions as an Autopsy service that
* manages the creation, updating, and deletion of tags applied to Content and * manages the creation, updating, and deletion of tags applied to content and
* BlackboardArtifacts objects by users. * blackboard artifacts by users.
*/ */
public class TagsManager implements Closeable { public class TagsManager implements Closeable {
private static final String TAGS_SETTINGS_FILE_NAME = "tags"; private static final String TAGS_SETTINGS_NAME = "Tags";
private static final String TAG_NAMES_SETTING_KEY = "tagNames"; private static final String TAG_NAMES_SETTING_KEY = "TagNames";
private static final TagName[] predefinedTagNames = new TagName[]{new TagName("Bookmark", "", TagName.HTML_COLOR.NONE)};
private final SleuthkitCase tskCase; private final SleuthkitCase tskCase;
private final HashMap<String, TagName> tagNames = new HashMap<>(); private final HashMap<String, TagName> tagNames = new HashMap<>();
private final Object lock = new Object();
// Use this exception and the member hash map to manage uniqueness of hash
// names. This is deemed more proactive and informative than leaving this to
// the UNIQUE constraint on the display_name field of the tag_names table in
// the case database.
public class TagNameAlreadyExistsException extends Exception {
}
/**
* Package-scope constructor for use of Services class. An instance of
* TagsManager should be created for each case that is opened.
* @param [in] tskCase The SleuthkitCase object for the current case.
*/
TagsManager(SleuthkitCase tskCase) { TagsManager(SleuthkitCase tskCase) {
this.tskCase = tskCase; this.tskCase = tskCase;
loadTagNamesFromTagSettings(); getExistingTagNames();
saveTagNamesToTagsSettings();
} }
private void getExistingTagNames() {
getTagNamesFromCurrentCase();
getTagNamesFromTagsSettings();
getPredefinedTagNames();
}
private void getTagNamesFromCurrentCase() {
try {
ArrayList<TagName> currentTagNames = new ArrayList<>();
tskCase.getAllTagNames(currentTagNames);
for (TagName tagName : currentTagNames) {
tagNames.put(tagName.getDisplayName(), tagName);
}
}
catch (TskCoreException ex) {
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag types from the current case", ex);
}
}
private void getTagNamesFromTagsSettings() {
String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY);
if (null != setting && !setting.isEmpty()) {
// Read the tag name setting and break it into tag name tuples.
List<String> tagNameTuples = Arrays.asList(setting.split(";"));
// Parse each tuple and add the tag names to the current case, one
// at a time to gracefully discard any duplicates or corrupt tuples.
for (String tagNameTuple : tagNameTuples) {
String[] tagNameAttributes = tagNameTuple.split(",");
if (!tagNames.containsKey(tagNameAttributes[0])) {
TagName tagName = new TagName(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.getColorByName(tagNameAttributes[2]));
addTagName(tagName, "Failed to add " + tagName.getDisplayName() + " tag name from tag settings to the current case");
}
}
}
}
private void getPredefinedTagNames() {
for (TagName tagName : predefinedTagNames) {
if (!tagNames.containsKey(tagName.getDisplayName())) {
addTagName(tagName, "Failed to add predefined " + tagName.getDisplayName() + " tag name to the current case");
}
}
}
private void addTagName(TagName tagName, String errorMessage) {
try {
tskCase.addTagName(tagName);
tagNames.put(tagName.getDisplayName(), tagName);
}
catch(TskCoreException ex) {
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, errorMessage, ex);
}
}
private void saveTagNamesToTagsSettings() {
if (!tagNames.isEmpty()) {
StringBuilder setting = new StringBuilder();
for (TagName tagName : tagNames.values()) {
if (setting.length() != 0) {
setting.append(";");
}
setting.append(tagName.getDisplayName()).append(",");
setting.append(tagName.getDescription()).append(",");
setting.append(tagName.getColor().name());
}
ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString());
}
}
/** /**
* Gets a list of all tag names currently available for tagging content or * Gets a list of all tag names currently available for tagging content or
* blackboard artifacts. * blackboard artifacts.
@ -67,27 +152,35 @@ public class TagsManager implements Closeable {
} }
/** /**
* RJCTODO: Discard or properly comment * Gets a list of all tag names currently used for tagging content or
* blackboard artifacts.
* @return [out] A list, possibly empty, of TagName data transfer objects (DTOs).
*/
public void getTagNamesInUse(List<TagName> tagNames) {
try {
tagNames.clear();
tskCase.getTagNamesInUse(tagNames);
}
catch (TskCoreException ex) {
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names from the current case", ex);
}
}
/**
* Checks whether a tag name with a given display name exists.
* @param [in] tagDisplayName The display name for which to check.
* @return True if the tag name exists, false otherwise.
*/ */
public boolean tagNameExists(String tagDisplayName) { public boolean tagNameExists(String tagDisplayName) {
return tagNames.containsKey(tagDisplayName); synchronized(lock) {
return tagNames.containsKey(tagDisplayName);
}
} }
/**
* RJCTODO: Discard or properly comment
*/
public TagName getTagName(String tagDisplayName) {
if (!tagNames.containsKey(tagDisplayName)) {
// RJCTODO: Throw exception
}
return tagNames.get(tagDisplayName);
}
/** /**
* Adds a new tag name to the current case and to the tags settings file. * Adds a new tag name to the current case and to the tags settings file.
* @param displayName The display name for the new tag name. * @param [in] displayName The display name for the new tag name.
* @return A TagName object representing the new tag name on success, null on failure. * @return A TagName data transfer object (DTO) representing the new tag name.
* @throws TskCoreException * @throws TskCoreException
*/ */
public TagName addTagName(String displayName) throws TagNameAlreadyExistsException, TskCoreException { public TagName addTagName(String displayName) throws TagNameAlreadyExistsException, TskCoreException {
@ -96,9 +189,9 @@ public class TagsManager implements Closeable {
/** /**
* Adds a new tag name to the current case and to the tags settings file. * Adds a new tag name to the current case and to the tags settings file.
* @param displayName The display name for the new tag name. * @param [in] displayName The display name for the new tag name.
* @param description The description for the new tag name. * @param [in] description The description for the new tag name.
* @return A TagName object representing the new tag name on success, null on failure. * @return A TagName data transfer object (DTO) representing the new tag name.
* @throws TskCoreException * @throws TskCoreException
*/ */
public TagName addTagName(String displayName, String description) throws TagNameAlreadyExistsException, TskCoreException { public TagName addTagName(String displayName, String description) throws TagNameAlreadyExistsException, TskCoreException {
@ -107,31 +200,34 @@ public class TagsManager implements Closeable {
/** /**
* Adds a new tag name to the current case and to the tags settings file. * Adds a new tag name to the current case and to the tags settings file.
* @param displayName The display name for the new tag name. * @param [in] displayName The display name for the new tag name.
* @param description The description for the new tag name. * @param [in] description The description for the new tag name.
* @param color The HTML color to associate with the new tag name. * @param [in] color The HTML color to associate with the new tag name.
* @return A TagName object representing the new tag name. * @return A TagName data transfer object (DTO) representing the new tag name.
* @throws TskCoreException * @throws TskCoreException
*/ */
public synchronized TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TagNameAlreadyExistsException, TskCoreException { public synchronized TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TagNameAlreadyExistsException, TskCoreException {
if (tagNames.containsKey(displayName)) { synchronized(lock) {
throw new TagNameAlreadyExistsException(); if (tagNames.containsKey(displayName)) {
} throw new TagNameAlreadyExistsException();
}
// Add the tag name to the case.
TagName newTagName = new TagName(displayName, description, color);
tskCase.addTagName(newTagName);
// Add the tag name to the tags settings.
tagNames.put(newTagName.getDisplayName(), newTagName);
saveTagNamesToTagsSettings();
return newTagName;
}
}
TagName newTagName = new TagName(displayName, description, color);
tskCase.addTagName(newTagName);
tagNames.put(newTagName.getDisplayName(), newTagName);
saveTagNamesToTagsSettings();
return newTagName;
}
public class TagNameAlreadyExistsException extends Exception {
}
/** /**
* Tags a Content object. * Tags a content object.
* @param content The Content to tag. * @param [in] content The content to tag.
* @param tagName The type of tag to add. * @param [in] tagName The name to use for the tag.
* @throws TskCoreException * @throws TskCoreException
*/ */
public void addContentTag(Content content, TagName tagName) throws TskCoreException { public void addContentTag(Content content, TagName tagName) throws TskCoreException {
@ -139,10 +235,10 @@ public class TagsManager implements Closeable {
} }
/** /**
* Tags a Content object. * Tags a content object.
* @param content The Content to tag. * @param [in] content The content to tag.
* @param tagName The name to use for the tag. * @param [in] tagName The name to use for the tag.
* @param comment A comment to store with the tag. * @param [in] comment A comment to store with the tag.
* @throws TskCoreException * @throws TskCoreException
*/ */
public void addContentTag(Content content, TagName tagName, String comment) throws TskCoreException { public void addContentTag(Content content, TagName tagName, String comment) throws TskCoreException {
@ -150,12 +246,12 @@ public class TagsManager implements Closeable {
} }
/** /**
* Tags a Content object or a portion of a content object. * Tags a content object or a portion of a content object.
* @param content The Content to tag. * @param [in] content The content to tag.
* @param tagName The name to use for the tag. * @param [in] tagName The name to use for the tag.
* @param comment A comment to store with the tag. * @param [in] comment A comment to store with the tag.
* @param beginByteOffset Designates the beginning of a tagged extent. * @param [in] beginByteOffset Designates the beginning of a tagged extent.
* @param endByteOffset Designates the end of a tagged extent. * @param [in] endByteOffset Designates the end of a tagged extent.
* @throws TskCoreException * @throws TskCoreException
*/ */
public void addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws IllegalArgumentException, TskCoreException { public void addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws IllegalArgumentException, TskCoreException {
@ -176,7 +272,7 @@ public class TagsManager implements Closeable {
/** /**
* Deletes a content tag. * Deletes a content tag.
* @param tag The tag to delete. * @param [in] tag The tag to delete.
* @throws TskCoreException * @throws TskCoreException
*/ */
public void deleteContentTag(ContentTag tag) throws TskCoreException { public void deleteContentTag(ContentTag tag) throws TskCoreException {
@ -184,36 +280,11 @@ public class TagsManager implements Closeable {
} }
/** /**
* Tags a BlackboardArtifact object. * Gets content tags by tag name.
* @param artifact The BlackboardArtifact to tag. * @param [in] tagName The tag name of interest.
* @param tagName The name to use for the tag. * @return A list, possibly empty, of the content tags with the specified tag name.
* @throws TskCoreException
*/ */
public void addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName) throws TskCoreException { public void getContentTagsByTagName(TagName tagName, List<ContentTag> tags) {
addBlackboardArtifactTag(artifact, tagName, "");
}
/**
* Tags a BlackboardArtifact object.
* @param artifact The BlackboardArtifact to tag.
* @param tagName The name to use for the tag.
* @param comment A comment to store with the tag.
* @throws TskCoreException
*/
public void addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException {
tskCase.addBlackboardArtifactTag(new BlackboardArtifactTag(artifact, tskCase.getContentById(artifact.getObjectID()), tagName, comment));
}
void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException {
tskCase.deleteBlackboardArtifactTag(tag);
}
/**
* RJCTODO
* @param tagName
* @return
*/
public void getContentTags(TagName tagName, List<ContentTag> tags) {
try { try {
tskCase.getContentTagsByTagName(tagName, tags); tskCase.getContentTagsByTagName(tagName, tags);
} }
@ -221,13 +292,43 @@ public class TagsManager implements Closeable {
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get content tags from the current case", ex); Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get content tags from the current case", ex);
} }
} }
/** /**
* RJCTODO * Tags a blackboard artifact object.
* @param tagName * @param [in] artifact The blackboard artifact to tag.
* @return * @param [in] tagName The name to use for the tag.
* @throws TskCoreException
*/ */
public void getBlackboardArtifactTags(TagName tagName, List<BlackboardArtifactTag> tags) { public void addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName) throws TskCoreException {
addBlackboardArtifactTag(artifact, tagName, "");
}
/**
* Tags a blackboard artifact object.
* @param [in] artifact The blackboard artifact to tag.
* @param [in] tagName The name to use for the tag.
* @param [in] comment A comment to store with the tag.
* @throws TskCoreException
*/
public void addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException {
tskCase.addBlackboardArtifactTag(new BlackboardArtifactTag(artifact, tskCase.getContentById(artifact.getObjectID()), tagName, comment));
}
/**
* Deletes a blackboard artifact tag.
* @param [in] tag The tag to delete.
* @throws TskCoreException
*/
public void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException {
tskCase.deleteBlackboardArtifactTag(tag);
}
/**
* Gets blackboard artifact tags by tag name.
* @param [in] tagName The tag name of interest.
* @return A list, possibly empty, of the content tags with the specified tag name.
*/
public void getBlackboardArtifactTagsByTagName(TagName tagName, List<BlackboardArtifactTag> tags) {
try { try {
tskCase.getBlackboardArtifactTagsByTagName(tagName, tags); tskCase.getBlackboardArtifactTagsByTagName(tagName, tags);
} }
@ -239,62 +340,5 @@ public class TagsManager implements Closeable {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
saveTagNamesToTagsSettings(); saveTagNamesToTagsSettings();
}
private void loadTagNamesFromTagSettings() {
// Get any tag names already defined for the current case.
try {
ArrayList<TagName> currentTagNames = new ArrayList<>();
tskCase.getAllTagNames(currentTagNames);
for (TagName tagName : currentTagNames) {
tagNames.put(tagName.getDisplayName(), tagName);
}
}
catch (TskCoreException ex) {
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag types from the current case", ex);
}
// Read the saved tag names, if any, from the tags settings file and
// add them to the current case if they haven't already been added, e.g,
// when the case was last opened.
String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_FILE_NAME, TAG_NAMES_SETTING_KEY);
if (null != setting && !setting.isEmpty()) {
// Read the tag types setting and break in into tag type tuples.
List<String> tagNameTuples = Arrays.asList(setting.split(";"));
// Parse each tuple and add the tag types to the current case, one
// at a time to gracefully discard any duplicates or corrupt tuples.
for (String tagNameTuple : tagNameTuples) {
String[] tagNameAttributes = tagNameTuple.split(",");
if (!tagNames.containsKey(tagNameAttributes[0])) {
TagName tagName = new TagName(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.getColorByName(tagNameAttributes[2]));
try {
tskCase.addTagName(tagName);
tagNames.put(tagName.getDisplayName(),tagName);
}
catch(TskCoreException ex) {
Logger.getLogger(TagsManager.class.getName()).log(Level.WARNING, "Failed to add saved " + tagName.getDisplayName() + " tag name to the current case", ex);
}
}
}
saveTagNamesToTagsSettings();
}
}
private void saveTagNamesToTagsSettings() {
if (!tagNames.isEmpty()) {
StringBuilder setting = new StringBuilder();
for (TagName tagName : tagNames.values()) {
if (setting.length() != 0) {
setting.append(";");
}
setting.append(tagName.getDisplayName()).append(",");
setting.append(tagName.getDescription()).append(",");
setting.append(tagName.getColor().name());
}
ModuleSettings.setConfigSetting(TAGS_SETTINGS_FILE_NAME, TAG_NAMES_SETTING_KEY, setting.toString());
}
} }
} }

View File

@ -155,11 +155,6 @@ abstract class AbstractContentChildren<T> extends Keys<T> {
return ee.new EmailExtractedRootNode(); return ee.new EmailExtractedRootNode();
} }
@Override
public AbstractNode visit(Tags t) {
return t.new TagsRootNode();
}
@Override @Override
public AbstractNode visit(TagsNodeKey tagsNodeKey) { public AbstractNode visit(TagsNodeKey tagsNodeKey) {
return new TagsNode(); return new TagsNode();

View File

@ -52,8 +52,6 @@ public interface AutopsyItemVisitor<T> {
T visit(EmailExtracted ee); T visit(EmailExtracted ee);
T visit(Tags t);
T visit(TagsNodeKey tagsNodeKey); T visit(TagsNodeKey tagsNodeKey);
T visit(DataSources i); T visit(DataSources i);
@ -136,11 +134,6 @@ public interface AutopsyItemVisitor<T> {
return defaultVisit(ee); return defaultVisit(ee);
} }
@Override
public T visit(Tags t) {
return defaultVisit(t);
}
@Override @Override
public T visit(TagsNodeKey tagsNodeKey) { public T visit(TagsNodeKey tagsNodeKey) {
return defaultVisit(tagsNodeKey); return defaultVisit(tagsNodeKey);

View File

@ -18,11 +18,15 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.swing.Action;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.actions.DeleteBlackboardArtifactTagAction;
import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -69,6 +73,13 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
return propertySheet; return propertySheet;
} }
@Override
public Action[] getActions(boolean context) {
List<Action> actions = new ArrayList<>();
actions.add(DeleteBlackboardArtifactTagAction.getInstance());
return actions.toArray(new Action[0]);
}
@Override @Override
public <T> T accept(DisplayableItemNodeVisitor<T> v) { public <T> T accept(DisplayableItemNodeVisitor<T> v) {
return v.visit(this); return v.visit(this);

View File

@ -19,11 +19,15 @@
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.swing.Action;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.actions.DeleteContentTagAction;
import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -68,6 +72,13 @@ public class ContentTagNode extends DisplayableItemNode {
return propertySheet; return propertySheet;
} }
@Override
public Action[] getActions(boolean context) {
List<Action> actions = new ArrayList<>();
actions.add(DeleteContentTagAction.getInstance());
return actions.toArray(new Action[0]);
}
@Override @Override
public <T> T accept(DisplayableItemNodeVisitor<T> v) { public <T> T accept(DisplayableItemNodeVisitor<T> v) {
return v.visit(this); return v.visit(this);

View File

@ -77,7 +77,7 @@ public class ContentTagTypeNode extends DisplayableItemNode {
@Override @Override
protected boolean createKeys(List<ContentTag> keys) { protected boolean createKeys(List<ContentTag> keys) {
// Use the content tags bearing the specified tag name as the keys. // Use the content tags bearing the specified tag name as the keys.
Case.getCurrentCase().getServices().getTagsManager().getContentTags(tagName, keys); Case.getCurrentCase().getServices().getTagsManager().getContentTagsByTagName(tagName, keys);
return true; return true;
} }

View File

@ -30,9 +30,6 @@ import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode;
import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode;
import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode;
import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode;
import org.sleuthkit.autopsy.datamodel.Tags.TagNodeRoot;
import org.sleuthkit.autopsy.datamodel.Tags.TagsNodeRoot;
import org.sleuthkit.autopsy.datamodel.Tags.TagsRootNode;
import org.sleuthkit.autopsy.directorytree.BlackboardArtifactTagTypeNode; import org.sleuthkit.autopsy.directorytree.BlackboardArtifactTagTypeNode;
/** /**
@ -86,12 +83,6 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(EmailExtractedFolderNode eefn); T visit(EmailExtractedFolderNode eefn);
T visit(TagsRootNode bksrn);
T visit(TagsNodeRoot bksrn);
T visit(TagNodeRoot tnr);
T visit(TagsNode node); T visit(TagsNode node);
T visit(TagNameNode node); T visit(TagNameNode node);
@ -277,21 +268,6 @@ public interface DisplayableItemNodeVisitor<T> {
return defaultVisit(ldn); return defaultVisit(ldn);
} }
@Override
public T visit(TagsRootNode bksrn) {
return defaultVisit(bksrn);
}
@Override
public T visit(TagsNodeRoot bksnr) {
return defaultVisit(bksnr);
}
@Override
public T visit(TagNodeRoot tnr) {
return defaultVisit(tnr);
}
@Override @Override
public T visit(TagsNode node) { public T visit(TagsNode node) {
return defaultVisit(node); return defaultVisit(node);

View File

@ -35,7 +35,6 @@ public class ResultsNode extends DisplayableItemNode {
new KeywordHits(sleuthkitCase), new KeywordHits(sleuthkitCase),
new HashsetHits(sleuthkitCase), new HashsetHits(sleuthkitCase),
new EmailExtracted(sleuthkitCase), new EmailExtracted(sleuthkitCase),
new Tags(sleuthkitCase), //TODO move to the top of the tree
new TagsNodeKey() new TagsNodeKey()
)), Lookups.singleton(NAME)); )), Lookups.singleton(NAME));
setName(NAME); setName(NAME);

View File

@ -79,23 +79,12 @@ public class RootContentChildren extends AbstractContentChildren<Object> {
case TSK_EMAIL_MSG: case TSK_EMAIL_MSG:
if (o instanceof EmailExtracted) if (o instanceof EmailExtracted)
this.refreshKey(o); this.refreshKey(o);
break; break;
//TODO check
case TSK_TAG_FILE: case TSK_TAG_FILE:
if (o instanceof Tags) case TSK_TAG_ARTIFACT:
this.refreshKey(o);
if (o instanceof TagsNodeKey) if (o instanceof TagsNodeKey)
this.refreshKey(o); this.refreshKey(o);
break; break;
//TODO check
case TSK_TAG_ARTIFACT:
if (o instanceof Tags)
this.refreshKey(o);
if (o instanceof TagsNodeKey)
this.refreshKey(o);
break;
default: default:
if (o instanceof ExtractedContent) if (o instanceof ExtractedContent)
this.refreshKey(o); this.refreshKey(o);

View File

@ -18,30 +18,15 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.awt.event.ActionEvent;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.Action;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponentinterfaces.BlackboardResultViewer;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
@ -50,382 +35,12 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** public class Tags {
*
* Support for tags in the directory tree. Tag nodes representing file and
* result tags, encapsulate TSK_TAG_FILE and TSK_TAG_ARTIFACT typed artifacts.
*
* The class implements querying of data model and populating node hierarchy
* using child factories.
*
*/
public class Tags implements AutopsyVisitableItem {
private static final Logger logger = Logger.getLogger(Tags.class.getName()); private static final Logger logger = Logger.getLogger(Tags.class.getName());
private static final String FILE_TAG_LABEL_NAME = "File Tags";
private static final String RESULT_TAG_LABEL_NAME = "Result Tags";
private SleuthkitCase skCase;
public static final String NAME = "Tags";
private static final String TAG_ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png";
//bookmarks are specializations of tags
public static final String BOOKMARK_TAG_NAME = "Bookmark"; public static final String BOOKMARK_TAG_NAME = "Bookmark";
private static final String BOOKMARK_ICON_PATH = "org/sleuthkit/autopsy/images/star-bookmark-icon-16.png";
private Map<BlackboardArtifact.ARTIFACT_TYPE, Map<String, List<BlackboardArtifact>>> tags;
private static final String EMPTY_COMMENT = ""; private static final String EMPTY_COMMENT = "";
private static final String APP_SETTINGS_FILE_NAME = "app"; // @@@ TODO: Need a general app settings or user preferences file, this will do for now.
private static final String TAG_NAMES_SETTING_KEY = "tag_names";
private static final HashSet<String> appSettingTagNames = new HashSet<>();
private static final StringBuilder tagNamesAppSetting = new StringBuilder();
// When this class is loaded, either create an new app settings file or
// get the tag names setting from the existing app settings file.
static {
String setting = ModuleSettings.getConfigSetting(APP_SETTINGS_FILE_NAME, TAG_NAMES_SETTING_KEY);
if (null != setting && !setting.isEmpty()) {
// Make a speedy lookup for the tag names in the setting to aid in the
// detection of new tag names.
List<String> tagNamesFromAppSettings = Arrays.asList(setting.split(","));
for (String tagName : tagNamesFromAppSettings) {
appSettingTagNames.add(tagName);
}
// Load the raw comma separated values list from the setting into a
// string builder to facilitate adding new tag names to the list and writing
// it back to the app settings file.
tagNamesAppSetting.append(setting);
}
}
Tags(SleuthkitCase skCase) {
this.skCase = skCase;
}
@Override
public <T> T accept(AutopsyItemVisitor<T> v) {
return v.visit(this);
}
/**
* Root of all Tag nodes. This node is shown directly under Results in the
* directory tree.
*/
public class TagsRootNode extends DisplayableItemNode {
public TagsRootNode() {
super(Children.create(new Tags.TagsRootChildren(), true), Lookups.singleton(NAME));
super.setName(NAME);
super.setDisplayName(NAME);
this.setIconBaseWithExtension(TAG_ICON_PATH);
initData();
}
private void initData() {
try {
// Get all file and artifact tags
//init data
tags = new EnumMap<>(BlackboardArtifact.ARTIFACT_TYPE.class);
tags.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE, new HashMap<String, List<BlackboardArtifact>>());
tags.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT, new HashMap<String, List<BlackboardArtifact>>());
//populate
for (BlackboardArtifact.ARTIFACT_TYPE artType : tags.keySet()) {
final Map<String, List<BlackboardArtifact>> artTags = tags.get(artType);
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(artType)) {
for (BlackboardAttribute attribute : artifact.getAttributes()) {
if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID()) {
String tagName = attribute.getValueString();
if (artTags.containsKey(tagName)) {
List<BlackboardArtifact> artifacts = artTags.get(tagName);
artifacts.add(artifact);
} else {
List<BlackboardArtifact> artifacts = new ArrayList<>();
artifacts.add(artifact);
artTags.put(tagName, artifacts);
}
break;
}
}
}
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Count not initialize tag nodes", ex);
}
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
return v.visit(this);
}
@Override
protected Sheet createSheet() {
Sheet s = super.createSheet();
Sheet.Set ss = s.get(Sheet.PROPERTIES);
if (ss == null) {
ss = Sheet.createPropertiesSet();
s.put(ss);
}
ss.put(new NodeProperty("Name",
"Name",
"no description",
getName()));
return s;
}
}
/**
* bookmarks root child node creating types of bookmarks nodes
*/
private class TagsRootChildren extends ChildFactory<BlackboardArtifact.ARTIFACT_TYPE> {
@Override
protected boolean createKeys(List<BlackboardArtifact.ARTIFACT_TYPE> list) {
for (BlackboardArtifact.ARTIFACT_TYPE artType : tags.keySet()) {
list.add(artType);
}
return true;
}
@Override
protected Node createNodeForKey(BlackboardArtifact.ARTIFACT_TYPE key) {
return new TagsNodeRoot(key, tags.get(key));
}
}
/**
* Tag node representation (file or result)
*/
public class TagsNodeRoot extends DisplayableItemNode {
TagsNodeRoot(BlackboardArtifact.ARTIFACT_TYPE tagType, Map<String, List<BlackboardArtifact>> subTags) {
super(Children.create(new TagRootChildren(tagType, subTags), true), Lookups.singleton(tagType.getDisplayName()));
String name = null;
if (tagType.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE)) {
name = FILE_TAG_LABEL_NAME;
} else if (tagType.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT)) {
name = RESULT_TAG_LABEL_NAME;
}
super.setName(name);
super.setDisplayName(name + " (" + subTags.values().size() + ")");
this.setIconBaseWithExtension(TAG_ICON_PATH);
}
@Override
protected Sheet createSheet() {
Sheet s = super.createSheet();
Sheet.Set ss = s.get(Sheet.PROPERTIES);
if (ss == null) {
ss = Sheet.createPropertiesSet();
s.put(ss);
}
ss.put(new NodeProperty("Name",
"Name",
"no description",
getName()));
return s;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
return v.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return false;
}
}
/**
* Child factory to add all the Tag artifacts to a TagsRootNode with the tag
* name.
*/
private class TagRootChildren extends ChildFactory<String> {
private Map<String, List<BlackboardArtifact>> subTags;
private BlackboardArtifact.ARTIFACT_TYPE tagType;
TagRootChildren(BlackboardArtifact.ARTIFACT_TYPE tagType, Map<String, List<BlackboardArtifact>> subTags) {
super();
this.tagType = tagType;
this.subTags = subTags;
}
@Override
protected boolean createKeys(List<String> list) {
list.addAll(subTags.keySet());
return true;
}
@Override
protected Node createNodeForKey(String key) {
return new Tags.TagNodeRoot(tagType, key, subTags.get(key));
}
}
/**
* Node for each unique tag name. Shown directly under Results > Tags.
*/
public class TagNodeRoot extends DisplayableItemNode {
TagNodeRoot(BlackboardArtifact.ARTIFACT_TYPE tagType, String tagName, List<BlackboardArtifact> artifacts) {
super(Children.create(new Tags.TagsChildrenNode(tagType, tagName, artifacts), true), Lookups.singleton(tagName));
super.setName(tagName);
super.setDisplayName(tagName + " (" + artifacts.size() + ")");
if (tagName.equals(BOOKMARK_TAG_NAME)) {
this.setIconBaseWithExtension(BOOKMARK_ICON_PATH);
} else {
this.setIconBaseWithExtension(TAG_ICON_PATH);
}
}
@Override
protected Sheet createSheet() {
Sheet s = super.createSheet();
Sheet.Set ss = s.get(Sheet.PROPERTIES);
if (ss == null) {
ss = Sheet.createPropertiesSet();
s.put(ss);
}
ss.put(new NodeProperty("Name",
"Name",
"no description",
getName()));
return s;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
return v.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return true;
}
}
/**
* Node representing an individual Tag artifact. For each TagsNodeRoot under
* Results > Tags, this is one of the nodes listed in the result viewer.
*/
private class TagsChildrenNode extends ChildFactory<BlackboardArtifact> {
private List<BlackboardArtifact> artifacts;
private BlackboardArtifact.ARTIFACT_TYPE tagType;
private String tagName;
private TagsChildrenNode(BlackboardArtifact.ARTIFACT_TYPE tagType, String tagName, List<BlackboardArtifact> artifacts) {
super();
this.tagType = tagType;
this.tagName = tagName;
this.artifacts = artifacts;
}
@Override
protected boolean createKeys(List<BlackboardArtifact> list) {
list.addAll(artifacts);
return true;
}
@Override
protected Node createNodeForKey(final BlackboardArtifact artifact) {
//create node with action
BlackboardArtifactNode tagNode = null;
String iconPath;
if (tagName.equals(BOOKMARK_TAG_NAME)) {
iconPath = BOOKMARK_ICON_PATH;
} else {
iconPath = TAG_ICON_PATH;
}
//create actions here where Tag logic belongs
//instead of DataResultFilterNode w/visitors, which is much less pluggable and cluttered
if (tagType.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT)) {
//in case of result tag, add a action by sublcassing bb art node
//this action will be merged with other actions set DataResultFIlterNode
//otherwise in case of
tagNode = new BlackboardArtifactNode(artifact, iconPath) {
@Override
public Action[] getActions(boolean bln) {
//Action [] actions = super.getActions(bln); //To change body of generated methods, choose Tools | Templates.
Action[] actions = new Action[1];
actions[0] = new AbstractAction("View Source Result") {
@Override
public void actionPerformed(ActionEvent e) {
//open the source artifact in dir tree
BlackboardArtifact sourceArt = Tags.getArtifactFromTag(artifact.getArtifactID());
if (sourceArt != null) {
BlackboardResultViewer v = Lookup.getDefault().lookup(BlackboardResultViewer.class);
v.viewArtifact(sourceArt);
}
}
};
return actions;
}
};
} else {
//for file tag, don't subclass to add the additional actions
tagNode = new BlackboardArtifactNode(artifact, iconPath);
}
//add some additional node properties
int artifactTypeID = artifact.getArtifactTypeID();
final String NO_DESCR = "no description";
if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) {
BlackboardArtifact sourceResult = Tags.getArtifactFromTag(artifact.getArtifactID());
String resultType = sourceResult.getDisplayName();
NodeProperty resultTypeProp = new NodeProperty("Source Result Type",
"Result Type",
NO_DESCR,
resultType);
tagNode.addNodeProperty(resultTypeProp);
}
try {
//add source path property
final AbstractFile sourceFile = skCase.getAbstractFileById(artifact.getObjectID());
final String sourcePath = sourceFile.getUniquePath();
NodeProperty sourcePathProp = new NodeProperty("Source File Path",
"Source File Path",
NO_DESCR,
sourcePath);
tagNode.addNodeProperty(sourcePathProp);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting a file from artifact to get source file path for a tag, ", ex);
}
return tagNode;
}
}
/** /**
* Create a tag for a file with TSK_TAG_NAME as tagName. * Create a tag for a file with TSK_TAG_NAME as tagName.
* *
@ -448,9 +63,7 @@ public class Tags implements AutopsyVisitableItem {
"", comment); "", comment);
attrs.add(attr2); attrs.add(attr2);
} }
bookArt.addAttributes(attrs); bookArt.addAttributes(attrs);
updateTagNamesAppSetting(tagName);
} }
catch (TskCoreException ex) { catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to create tag for " + file.getName(), ex); logger.log(Level.SEVERE, "Failed to create tag for " + file.getName(), ex);
@ -488,30 +101,13 @@ public class Tags implements AutopsyVisitableItem {
attrs.add(attr1); attrs.add(attr1);
attrs.add(attr3); attrs.add(attr3);
bookArt.addAttributes(attrs); bookArt.addAttributes(attrs);
updateTagNamesAppSetting(tagName);
} }
catch (TskCoreException ex) { catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to create tag for artifact " + artifact.getArtifactID(), ex); logger.log(Level.SEVERE, "Failed to create tag for artifact " + artifact.getArtifactID(), ex);
} }
} }
private static void updateTagNamesAppSetting(String tagName) {
// If this tag name is not in the current tag names app setting...
if (!appSettingTagNames.contains(tagName)) {
// Add it to the lookup.
appSettingTagNames.add(tagName);
// Add it to the setting and write the setting back to the app settings file.
if (tagNamesAppSetting.length() != 0) {
tagNamesAppSetting.append(",");
}
tagNamesAppSetting.append(tagName);
ModuleSettings.setConfigSetting(APP_SETTINGS_FILE_NAME, TAG_NAMES_SETTING_KEY, tagNamesAppSetting.toString());
}
}
/** /**
* Create a bookmark tag for a file. * Create a bookmark tag for a file.
* *
@ -557,7 +153,6 @@ public class Tags implements AutopsyVisitableItem {
public static TreeSet<String> getAllTagNames() { public static TreeSet<String> getAllTagNames() {
// Use a TreeSet<> so the union of the tag names from the two sources will be sorted. // Use a TreeSet<> so the union of the tag names from the two sources will be sorted.
TreeSet<String> tagNames = getTagNamesFromCurrentCase(); TreeSet<String> tagNames = getTagNamesFromCurrentCase();
tagNames.addAll(appSettingTagNames);
// Make sure the book mark tag is always included. // Make sure the book mark tag is always included.
tagNames.add(BOOKMARK_TAG_NAME); tagNames.add(BOOKMARK_TAG_NAME);

View File

@ -43,6 +43,10 @@ public class TagsNode extends DisplayableItemNode {
this.setIconBaseWithExtension(ICON_PATH); this.setIconBaseWithExtension(ICON_PATH);
} }
public static String getNodeName() {
return DISPLAY_NAME;
}
@Override @Override
public boolean isLeafTypeNode() { public boolean isLeafTypeNode() {
return false; return false;
@ -70,7 +74,7 @@ public class TagsNode extends DisplayableItemNode {
private static class TagNameNodeFactory extends ChildFactory<TagName> { private static class TagNameNodeFactory extends ChildFactory<TagName> {
@Override @Override
protected boolean createKeys(List<TagName> keys) { protected boolean createKeys(List<TagName> keys) {
Case.getCurrentCase().getServices().getTagsManager().getAllTagNames(keys); // RJCTODO: Change this call to filtered call Case.getCurrentCase().getServices().getTagsManager().getAllTagNames(keys);
return true; return true;
} }

View File

@ -81,7 +81,7 @@ public class BlackboardArtifactTagTypeNode extends DisplayableItemNode {
@Override @Override
protected boolean createKeys(List<BlackboardArtifactTag> keys) { protected boolean createKeys(List<BlackboardArtifactTag> keys) {
// Use the blackboard artifact tags bearing the specified tag name as the keys. // Use the blackboard artifact tags bearing the specified tag name as the keys.
Case.getCurrentCase().getServices().getTagsManager().getBlackboardArtifactTags(tagName, keys); Case.getCurrentCase().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, keys);
return true; return true;
} }

View File

@ -63,8 +63,6 @@ import org.sleuthkit.autopsy.datamodel.LayoutFileNode;
import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode; import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode;
import org.sleuthkit.autopsy.datamodel.RecentFilesNode; import org.sleuthkit.autopsy.datamodel.RecentFilesNode;
import org.sleuthkit.autopsy.datamodel.FileTypesNode; import org.sleuthkit.autopsy.datamodel.FileTypesNode;
import org.sleuthkit.autopsy.datamodel.Tags.TagNodeRoot;
import org.sleuthkit.autopsy.datamodel.Tags.TagsNodeRoot;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -405,16 +403,6 @@ public class DataResultFilterNode extends FilterNode {
return openChild(atn); return openChild(atn);
} }
@Override
public AbstractAction visit(TagNodeRoot tnr) {
return openChild(tnr);
}
@Override
public AbstractAction visit(TagsNodeRoot tnr) {
return openChild(tnr);
}
@Override @Override
public AbstractAction visit(DirectoryNode dn) { public AbstractAction visit(DirectoryNode dn) {
if (dn.getDisplayName().equals(DirectoryNode.DOTDOTDIR)) { if (dn.getDisplayName().equals(DirectoryNode.DOTDOTDIR)) {