diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 1d7176421d..202845069a 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -63,6 +63,7 @@ import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.coreutils.History; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.imagegallery.actions.CategorizationChangeSet; import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile; @@ -92,8 +93,6 @@ public final class ImageGalleryController { private static final Logger LOGGER = Logger.getLogger(ImageGalleryController.class.getName()); - private static final String IMAGEGALLERY = "ImageGallery"; - private final Region infoOverLayBackground = new Region() { { setBackground(new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY))); @@ -111,6 +110,7 @@ public final class ImageGalleryController { } private final History historyManager = new History<>(); + private final History categoryUndoHistory = new History<>(); /** * true if Image Gallery should listen to ingest events, false if it should @@ -475,6 +475,10 @@ public final class ImageGalleryController { this.navPanel = navPanel; } + public History getUndoHistory() { + return categoryUndoHistory; + } + // @@@ REVIEW IF THIS SHOLD BE STATIC... //TODO: concept seems like the controller deal with how much work to do at a given time // @@@ review this class for synchronization issues (i.e. reset and cancel being called, add, etc.) @@ -702,8 +706,8 @@ public final class ImageGalleryController { + StringUtils.join(FileTypeUtils.getAllSupportedExtensions(), "' or name LIKE '%.") + "')"; - static private final String MIMETYPE_CLAUSE - = "blackboard_attributes.value_text LIKE '" + static private final String MIMETYPE_CLAUSE = + "blackboard_attributes.value_text LIKE '" + StringUtils.join(FileTypeUtils.getAllSupportedMimeTypes(), "' OR blackboard_attributes.value_text LIKE '") + "' "; diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizationChangeSet.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizationChangeSet.java new file mode 100644 index 0000000000..e9b688cf83 --- /dev/null +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizationChangeSet.java @@ -0,0 +1,49 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015 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.imagegallery.actions; + +import com.google.common.collect.ImmutableMap; +import java.util.HashMap; +import java.util.Map; +import org.sleuthkit.datamodel.TagName; + +/** + * + */ +final public class CategorizationChangeSet { + + private final TagName newCategory; + final private Map oldCategories = new HashMap<>(); + + public CategorizationChangeSet(TagName newCategory) { + this.newCategory = newCategory; + } + + public TagName getNewCategory() { + return newCategory; + } + + void add(long fileID, TagName old) { + oldCategories.put(fileID, old); + } + + Map getOldCategories() { + return ImmutableMap.copyOf(oldCategories); + } +} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java index 35c6d9e85a..b948b88ece 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java @@ -29,6 +29,7 @@ import javafx.scene.control.MenuItem; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javax.swing.JOptionPane; +import org.sleuthkit.autopsy.coreutils.History; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.datamodel.Category; @@ -51,10 +52,12 @@ public class CategorizeAction extends AddTagAction { private static final Logger LOGGER = Logger.getLogger(CategorizeAction.class.getName()); private final ImageGalleryController controller; + private final History undoHistory; public CategorizeAction(ImageGalleryController controller) { super(); this.controller = controller; + undoHistory = controller.getUndoHistory(); } public Menu getPopupMenu() { @@ -73,16 +76,13 @@ public class CategorizeAction extends AddTagAction { } @Override - public void addTagsToFiles(TagName tagName, String comment, Set selectedFiles) { - Logger.getAnonymousLogger().log(Level.INFO, "categorizing{0} as {1}", new Object[]{selectedFiles.toString(), tagName.getDisplayName()}); - - for (Long fileID : selectedFiles) { - controller.queueDBWorkerTask(new CategorizeTask(fileID, tagName, comment)); - } + protected void addTagsToFiles(TagName tagName, String comment, Set selectedFiles) { + addTagsToFiles(tagName, comment, selectedFiles, true); } - public void enforceOneCat(TagName name, String string) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + public void addTagsToFiles(TagName tagName, String comment, Set selectedFiles, boolean createUndo) { + Logger.getAnonymousLogger().log(Level.INFO, "categorizing{0} as {1}", new Object[]{selectedFiles.toString(), tagName.getDisplayName()}); + controller.queueDBWorkerTask(new CategorizeTask(selectedFiles, tagName, comment, createUndo)); } /** @@ -111,52 +111,66 @@ public class CategorizeAction extends AddTagAction { private class CategorizeTask extends ImageGalleryController.InnerTask { - private final long fileID; + private final Set fileIDs; private final TagName tagName; private final String comment; + private final CategorizationChangeSet categorizationChangeSet; + private final boolean createUndo; - public CategorizeTask(long fileID, TagName tagName, String comment) { + public CategorizeTask(Set fileIDs, TagName tagName, String comment, boolean createUndo) { super(); - this.fileID = fileID; + this.fileIDs = fileIDs; this.tagName = tagName; this.comment = comment; + this.createUndo = createUndo; + + categorizationChangeSet = new CategorizationChangeSet(tagName); } @Override public void run() { - final CategoryManager categoryManager = controller.getCategoryManager(); final DrawableTagsManager tagsManager = controller.getTagsManager(); + final CategoryManager categoryManager = controller.getCategoryManager(); - try { - DrawableFile file = controller.getFileFromId(fileID); //drawable db - final List fileTags = tagsManager.getContentTagsByContent(file); - if (tagName == categoryManager.getTagName(Category.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); - } - }); - } 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, comment); + for (long fileID : fileIDs) { + try { + DrawableFile file = controller.getFileFromId(fileID); //drawable db + if (createUndo) { + Category oldCat = file.getCategory(); + TagName oldCatTagName = categoryManager.getTagName(oldCat); + categorizationChangeSet.add(fileID, oldCatTagName); } - } - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error categorizing result", ex); - JOptionPane.showMessageDialog(null, "Unable to categorize " + fileID + ".", "Categorizing Error", JOptionPane.ERROR_MESSAGE); + final List fileTags = tagsManager.getContentTagsByContent(file); + if (tagName == categoryManager.getTagName(Category.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); + } + }); + } 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, comment); + } + } + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error categorizing result", ex); + JOptionPane.showMessageDialog(null, "Unable to categorize " + fileID + ".", "Categorizing Error", JOptionPane.ERROR_MESSAGE); + } } + if (createUndo) { + undoHistory.advance(categorizationChangeSet); + } } } - } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Undo.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Undo.java new file mode 100644 index 0000000000..ed0ce72858 --- /dev/null +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Undo.java @@ -0,0 +1,44 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015 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.imagegallery.actions; + +import java.util.Collections; +import java.util.Map; +import org.controlsfx.control.action.Action; +import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; +import org.sleuthkit.datamodel.TagName; + +/** + * + */ +public class Undo extends Action { + + public Undo(ImageGalleryController controller) { + super("Undo"); + + setEventHandler(actionEvent -> { + CategorizeAction categorizeAction = new CategorizeAction(controller); + CategorizationChangeSet retreat = controller.getUndoHistory().getCurrentState(); + for (Map.Entry entry : retreat.getOldCategories().entrySet()) { + categorizeAction.addTagsToFiles(entry.getValue(), "", Collections.singleton(entry.getKey()), false); + } + controller.getUndoHistory().retreat(); + }); + } +} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.fxml b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.fxml index 2972fe28af..0328f4b8d7 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.fxml +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/GroupPane.fxml @@ -8,7 +8,7 @@ - +
@@ -136,6 +136,7 @@ +