diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java index 7ad185ac8e..9015d9a6ad 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java @@ -37,8 +37,8 @@ import org.sleuthkit.datamodel.TskCoreException; * Instances of this Action allow users to apply tags to blackboard artifacts. */ @NbBundle.Messages({ - "AddBlackboardArtifactTagAction.singularTagResult=Tag Result", - "AddBlackboardArtifactTagAction.pluralTagResult=Tag Results", + "AddBlackboardArtifactTagAction.singularTagResult=Add Result Tag", + "AddBlackboardArtifactTagAction.pluralTagResult=Add Result Tags", "# {0} - artifactName", "AddBlackboardArtifactTagAction.unableToTag.msg=Unable to tag {0}.", "AddBlackboardArtifactTagAction.taggingErr=Tagging Error" diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java index 602789735d..34e7b2a110 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddContentTagAction.java @@ -38,8 +38,8 @@ import org.sleuthkit.datamodel.TskCoreException; * Instances of this Action allow users to apply tags to content. */ @NbBundle.Messages({ - "AddContentTagAction.singularTagFile=Tag File", - "AddContentTagAction.pluralTagFile=Tag Files", + "AddContentTagAction.singularTagFile=Add File Tag", + "AddContentTagAction.pluralTagFile=Add File Tags", "# {0} - fileName", "AddContentTagAction.unableToTag.msg=Unable to tag {0}, not a regular file.", "AddContentTagAction.cannotApplyTagErr=Cannot Apply Tag", diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java index 6efb30e6a2..6606185e24 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java @@ -19,6 +19,8 @@ package org.sleuthkit.autopsy.actions; import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; @@ -92,6 +94,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { // Get the current set of tag names. Map tagNamesMap = null; + List standardTagNames = TagsManager.getStandardTagNames(); try { TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager(); tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap()); @@ -101,6 +104,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { // Create a menu item for each of the existing and visible tags. // Selecting one of these menu items adds a tag with the associated tag name. + List standardTagMenuitems = new ArrayList<>(); if (null != tagNamesMap && !tagNamesMap.isEmpty()) { for (Map.Entry entry : tagNamesMap.entrySet()) { String tagDisplayName = entry.getKey(); @@ -114,15 +118,26 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { tagNameItem.addActionListener((ActionEvent e) -> { getAndAddTag(entry.getKey(), entry.getValue(), NO_COMMENT); }); - - add(tagNameItem); + + // Show custom tags before predefined tags in the menu + if (standardTagNames.contains(tagDisplayName)) { + standardTagMenuitems.add(tagNameItem); + } else { + add(tagNameItem); + } } } if (getItemCount() > 0) { addSeparator(); } - + + standardTagMenuitems.forEach((menuItem) -> { + add(menuItem); + }); + + addSeparator(); + // 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 // optional comment and adds a tag with the resulting name. diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java index 18ae9ac7e5..c3878e6b59 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteBlackboardArtifactTagAction.java @@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.TskCoreException; * artifacts. */ @NbBundle.Messages({ - "DeleteBlackboardArtifactTagAction.deleteTag=Delete Tag", + "DeleteBlackboardArtifactTagAction.deleteTag=Remove Selected Tag(s)", "# {0} - tagName", "DeleteBlackboardArtifactTagAction.unableToDelTag.msg=Unable to delete tag {0}.", "DeleteBlackboardArtifactTagAction.tagDelErr=Tag Deletion Error" diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java index 07cce02bfc..b7ff3a73a0 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteContentTagAction.java @@ -37,7 +37,7 @@ import org.sleuthkit.datamodel.TskCoreException; * Instances of this Action allow users to delete tags applied to content. */ @NbBundle.Messages({ - "DeleteContentTagAction.deleteTag=Delete Tag", + "DeleteContentTagAction.deleteTag=Remove Selected Tag(s)", "# {0} - tagName", "DeleteContentTagAction.unableToDelTag.msg=Unable to delete tag {0}.", "DeleteContentTagAction.tagDelErr=Tag Deletion Error" diff --git a/Core/src/org/sleuthkit/autopsy/actions/ReplaceBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/ReplaceBlackboardArtifactTagAction.java new file mode 100644 index 0000000000..a014257daf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/actions/ReplaceBlackboardArtifactTagAction.java @@ -0,0 +1,126 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.util.Collection; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javax.swing.SwingWorker; +import org.openide.util.NbBundle; +import org.openide.util.Utilities; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.TagsManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * This Action allows users to replace a tag applied to blackboard + * artifacts, with another tag + */ +public final class ReplaceBlackboardArtifactTagAction extends ReplaceTagAction { + + private static final Logger logger = Logger.getLogger(ReplaceBlackboardArtifactTagAction.class.getName()); + private static final long serialVersionUID = 1L; + + // 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 ReplaceBlackboardArtifactTagAction instance; + + public static synchronized ReplaceBlackboardArtifactTagAction getInstance() { + if (null == instance) { + instance = new ReplaceBlackboardArtifactTagAction(); + } + return instance; + } + + private ReplaceBlackboardArtifactTagAction() { + super(MENU_TEXT); + } + + /** + * Replaces the specified tag on the given artifact with the new one + * + * @param oldArtifactTag tag to be replaced + * @param newTagName name of the tag to replace with + */ + @NbBundle.Messages({ + "# {0} - old tag name", + "# {1} - artifactID", + "ReplaceBlackboardArtifactTagAction.replaceTag.alert=Unable to replace tag {0} for artifact {1}."}) + @Override + protected void replaceTag( BlackboardArtifactTag oldArtifactTag, TagName newTagName) { + new SwingWorker() { + + @Override + protected Void doInBackground() throws Exception { + TagsManager tagsManager; + try { + tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Error replacing artifact tag. No open case found.", ex); //NON-NLS + Platform.runLater(() + -> new Alert(Alert.AlertType.ERROR, Bundle.ReplaceBlackboardArtifactTagAction_replaceTag_alert(oldArtifactTag.getName().getDisplayName(), oldArtifactTag.getArtifact().getArtifactID())).show() + ); + return null; + } + + try { + logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldArtifactTag.getName().getDisplayName(), newTagName.getDisplayName(), oldArtifactTag.getContent().getName()}); //NON-NLS + + tagsManager.deleteBlackboardArtifactTag(oldArtifactTag); + tagsManager.addBlackboardArtifactTag(oldArtifactTag.getArtifact(), newTagName); + + } catch (TskCoreException tskCoreException) { + logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS + Platform.runLater(() + -> new Alert(Alert.AlertType.ERROR, Bundle.ReplaceBlackboardArtifactTagAction_replaceTag_alert(oldArtifactTag.getName().getDisplayName(), oldArtifactTag.getArtifact().getArtifactID())).show() + ); + } + return null; + } + + @Override + protected void done() { + super.done(); + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, "Unexpected exception while replacing artifact tag", ex); //NON-NLS + } + } + }.execute(); + } + + /** + * Returns list of tags selected by user to replace + * + * @return a list of tags + */ + @Override + Collection getTagsToReplace() { + return Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactTag.class); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/actions/ReplaceContentTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/ReplaceContentTagAction.java new file mode 100644 index 0000000000..484a30178c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/actions/ReplaceContentTagAction.java @@ -0,0 +1,120 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.util.Collection; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javax.swing.SwingWorker; +import org.openide.util.NbBundle; +import org.openide.util.Utilities; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.TagsManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * This Action allow users to replace a content tag with another tag + */ +public final class ReplaceContentTagAction extends ReplaceTagAction { + + private static final Logger logger = Logger.getLogger(ReplaceContentTagAction.class.getName()); + + private static final long serialVersionUID = 1L; + + // 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 ReplaceContentTagAction instance; + + public static synchronized ReplaceContentTagAction getInstance() { + if (null == instance) { + instance = new ReplaceContentTagAction(); + } + return instance; + } + + private ReplaceContentTagAction() { + super(MENU_TEXT); + } + + @NbBundle.Messages({ + "# {0} - old tag name", + "# {1} - content obj id", + "ReplaceContentTagAction.replaceTag.alert=Unable to replace tag {0} for {1}."}) + @Override + protected void replaceTag(ContentTag oldTag, TagName newTagName) { + new SwingWorker() { + + @Override + protected Void doInBackground() throws Exception { + TagsManager tagsManager; + try { + tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Error replacing artifact tag. No open case found.", ex); //NON-NLS + Platform.runLater(() + -> new Alert(Alert.AlertType.ERROR, Bundle.ReplaceContentTagAction_replaceTag_alert(oldTag.getName().getDisplayName(), oldTag.getContent().getName())).show() + ); + return null; + } + + try { + logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldTag.getName().getDisplayName(), newTagName.getDisplayName(), oldTag.getContent().getName()}); //NON-NLS + + tagsManager.deleteContentTag(oldTag); + tagsManager.addContentTag(oldTag.getContent(), newTagName); + + } catch (TskCoreException tskCoreException) { + logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS + Platform.runLater(() + -> new Alert(Alert.AlertType.ERROR, Bundle.ReplaceContentTagAction_replaceTag_alert(oldTag.getName().getDisplayName(), oldTag.getContent().getName())).show() + ); + } + return null; + } + + @Override + protected void done() { + super.done(); + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, "Unexpected exception while replacing content tag", ex); //NON-NLS + } + } + }.execute(); + } + + /** + * Returns list of content tags selected by user to replace + * + * @return a list of tags + */ + @Override + Collection getTagsToReplace() { + return Utilities.actionsGlobalContext().lookupAll(ContentTag.class); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/actions/ReplaceTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/ReplaceTagAction.java new file mode 100644 index 0000000000..a5a0843965 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/actions/ReplaceTagAction.java @@ -0,0 +1,188 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.logging.Level; +import javax.swing.AbstractAction; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import org.openide.util.NbBundle; +import org.openide.util.actions.Presenter; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.TagsManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Tag; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * Abstract class to define context action to replace a tag with another + * + * @param tag type + */ +@NbBundle.Messages({ + "ReplaceTagAction.replaceTag=Replace Selected Tag(s) With" +}) +abstract class ReplaceTagAction extends AbstractAction implements Presenter.Popup { + + private static final long serialVersionUID = 1L; + protected static final String MENU_TEXT = NbBundle.getMessage(ReplaceTagAction.class, + "ReplaceTagAction.replaceTag"); + + ReplaceTagAction(String menuText) { + super(menuText); + } + + /** + * Subclasses of replaceTagAction should not override actionPerformed, + * but instead override replaceTag. + * + * @param event + */ + @Override + @SuppressWarnings("NoopMethodInAbstractClass") + public void actionPerformed(ActionEvent event) { + } + + protected String getActionDisplayName() { + return MENU_TEXT; + } + + /** + * Method to actually replace the selected tag with the given new tag + * + * @param oldTag + * @param newTagName + */ + abstract protected void replaceTag(T oldTag, TagName newTagName); + + /** + * Returns elected tags which are to be replaced + * + * @return + */ + abstract Collection getTagsToReplace(); + + + @Override + public JMenuItem getPopupPresenter() { + return new ReplaceTagMenu(); + } + + /** + * Instances of this class implement a context menu user interface for + * selecting a tag name to replace the tag with + */ + private final class ReplaceTagMenu extends JMenu { + + private static final long serialVersionUID = 1L; + + ReplaceTagMenu() { + super(getActionDisplayName()); + + final Collection selectedTags = getTagsToReplace(); + + // Get the current set of tag names. + Map tagNamesMap = null; + List standardTagNames = TagsManager.getStandardTagNames(); + try { + TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager(); + tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap()); + } catch (TskCoreException | NoCurrentCaseException ex) { + Logger.getLogger(ReplaceTagMenu.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS + } + + List standardTagMenuitems = new ArrayList<>(); + // Ideally we should'nt allow user to pick a replacement tag that's already been applied to an item + // In the very least we don't allow them to pick the same tag as the one they are trying to replace + Set existingTagNames = new HashSet<>(); + if (!selectedTags.isEmpty()) { + T firstTag = selectedTags.iterator().next(); + existingTagNames.add(firstTag.getName().getDisplayName()); + } + + if (null != tagNamesMap && !tagNamesMap.isEmpty()) { + for (Map.Entry entry : tagNamesMap.entrySet()) { + String tagDisplayName = entry.getKey(); + String notableString = entry.getValue().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : ""; + JMenuItem tagNameItem = new JMenuItem(tagDisplayName + notableString); + // for the bookmark tag name only, added shortcut label + if (tagDisplayName.equals(NbBundle.getMessage(AddTagAction.class, "AddBookmarkTagAction.bookmark.text"))) { + tagNameItem.setAccelerator(AddBookmarkTagAction.BOOKMARK_SHORTCUT); + } + + // Add action to replace the tag + tagNameItem.addActionListener((ActionEvent event) -> { + selectedTags.forEach((oldtag) -> { + replaceTag(oldtag, entry.getValue()); + }); + }); + + // Don't allow replacing a tag with same tag. + if (existingTagNames.contains(tagDisplayName)) { + tagNameItem.setEnabled(false); + } + + + // Show custom tags before predefined tags in the menu + if (standardTagNames.contains(tagDisplayName)) { + standardTagMenuitems.add(tagNameItem); + } else { + add(tagNameItem); + } + } + } else { + JMenuItem empty = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.noTags")); + empty.setEnabled(false); + add(empty); + } + + // + if (this.getItemCount() > 0) { + addSeparator(); + } + standardTagMenuitems.forEach((menuItem) -> { + add(menuItem); + }); + + addSeparator(); + JMenuItem newTagMenuItem = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.newTag")); + newTagMenuItem.addActionListener((ActionEvent event) -> { + TagName newTagName = GetTagNameDialog.doDialog(); + if (null != newTagName) { + selectedTags.forEach((oldtag) -> { + replaceTag(oldtag, newTagName); + }); + } + }); + add(newTagMenuItem); + + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index f113a622fe..522c439d26 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java @@ -121,6 +121,15 @@ public class TagsManager implements Closeable { return tagDisplayNames; } + /** + * Returns a list of names of standard/predefined tags + * + * @return list of predefined tag names + */ + public static List getStandardTagNames() { + return TagNameDefinition.getStandardTagNames(); + } + /** * Constructs a per case Autopsy service that manages the addition of * content and artifact tags to the case database. diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactTagNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactTagNode.java index 6e7a8205c7..ea944b6b43 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactTagNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactTagNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2016 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,7 +28,6 @@ import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.actions.DeleteBlackboardArtifactTagAction; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; @@ -132,8 +131,8 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode { actions.add(ViewFileInTimelineAction.createViewSourceFileAction(file)); } actions.add(new ViewTaggedArtifactAction(BlackboardArtifactTagNode_viewSourceArtifact_text(), artifact)); - actions.addAll(DataModelActionsFactory.getActions(tag.getContent(), true)); - actions.add(DeleteBlackboardArtifactTagAction.getInstance()); + actions.addAll(DataModelActionsFactory.getActions(tag, true)); + return actions.toArray(new Action[0]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ContentTagNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ContentTagNode.java index 59d7d21015..547303878b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ContentTagNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ContentTagNode.java @@ -29,7 +29,6 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.actions.DeleteContentTagAction; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.datamodel.AbstractFile; @@ -128,8 +127,9 @@ class ContentTagNode extends DisplayableItemNode { if (file != null) { actions.add(ViewFileInTimelineAction.createViewFileAction(file)); } - actions.addAll(DataModelActionsFactory.getActions(tag.getContent(), false)); - actions.add(DeleteContentTagAction.getInstance()); + + actions.addAll(DataModelActionsFactory.getActions(tag, false)); + return actions.toArray(new Action[actions.size()]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java index b4b88463c4..6b4b1c8faf 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java @@ -28,8 +28,12 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteBlackboardArtifactTagAction; +import org.sleuthkit.autopsy.actions.DeleteContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; +import org.sleuthkit.autopsy.actions.ReplaceBlackboardArtifactTagAction; +import org.sleuthkit.autopsy.actions.ReplaceContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.datamodel.Reports.ReportNode; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; @@ -39,7 +43,9 @@ import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.ViewContextAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.File; @@ -351,6 +357,80 @@ public class DataModelActionsFactory { return actionsList; } + public static List getActions(ContentTag contentTag, boolean isArtifactSource) { + + List actionsList = new ArrayList<>(); + actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), contentTag.getContent())); + final ContentTagNode tagNode = new ContentTagNode(contentTag); + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, tagNode)); + actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, tagNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, tagNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); + if (isArtifactSource) { + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); + } + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + if(isArtifactSource) { + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + } + + actionsList.add(DeleteContentTagAction.getInstance()); + actionsList.add(ReplaceContentTagAction.getInstance()); + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; + } + + + public static List getActions(BlackboardArtifactTag artifactTag, boolean isArtifactSource) { + List actionsList = new ArrayList<>(); + actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), artifactTag.getContent())); + final BlackboardArtifactTagNode tagNode = new BlackboardArtifactTagNode(artifactTag); + actionsList.add(null); // creates a menu separator + actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, tagNode)); + actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, tagNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, tagNode)); + actionsList.add(null); // creates a menu separator + actionsList.add(AddContentTagAction.getInstance()); + if (isArtifactSource) { + actionsList.add(AddBlackboardArtifactTagAction.getInstance()); + } + + final Collection selectedFilesList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if(selectedFilesList.size() == 1) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + if(isArtifactSource) { + final Collection selectedArtifactsList = + new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + if(selectedArtifactsList.size() == 1) { + actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance()); + } + } + + actionsList.add(DeleteBlackboardArtifactTagAction.getInstance()); + actionsList.add(ReplaceBlackboardArtifactTagAction.getInstance()); + + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + return actionsList; + } + public static List getActions(Content content, boolean isArtifactSource) { if (content instanceof File) { return getActions((File) content, isArtifactSource);