mirror of
https://github.com/elisspace/autopsy.git
synced 2026-10-05 00:36:16 +00:00
simple undo implementation to test it is feasible.
This commit is contained in:
@@ -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<GroupViewState> historyManager = new History<>();
|
||||
private final History<CategorizationChangeSet> 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<CategorizationChangeSet> 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 '") + "' ";
|
||||
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.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<Long, TagName> 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<Long, TagName> getOldCategories() {
|
||||
return ImmutableMap.copyOf(oldCategories);
|
||||
}
|
||||
}
|
||||
@@ -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<CategorizationChangeSet> 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<Long> 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<Long> 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<Long> 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<Long> 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<Long> 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<ContentTag> 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<ContentTag> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.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<Long, TagName> entry : retreat.getOldCategories().entrySet()) {
|
||||
categorizeAction.addTagsToFiles(entry.getValue(), "", Collections.singleton(entry.getKey()), false);
|
||||
}
|
||||
controller.getUndoHistory().retreat();
|
||||
});
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -8,7 +8,7 @@
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import org.controlsfx.control.*?>
|
||||
|
||||
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
|
||||
|
||||
<center>
|
||||
<GridView fx:id="gridView" BorderPane.alignment="CENTER" />
|
||||
@@ -136,6 +136,7 @@
|
||||
</HBox>
|
||||
<Region fx:id="spacer" prefHeight="-1.0" prefWidth="-1.0" />
|
||||
<Separator prefWidth="30.0" />
|
||||
<Button fx:id="undoButton" mnemonicParsing="false" text="Undo" />
|
||||
<SegmentedButton fx:id="segButton">
|
||||
<buttons>
|
||||
<ToggleButton id="" fx:id="tileToggle" alignment="CENTER" contentDisplay="GRAPHIC_ONLY" graphicTextGap="0.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="32.0" mnemonicParsing="false" prefWidth="-1.0" scaleX="1.0" selected="true" text="" textAlignment="CENTER">
|
||||
|
||||
+15
-16
@@ -118,6 +118,7 @@ import org.sleuthkit.autopsy.imagegallery.actions.Forward;
|
||||
import org.sleuthkit.autopsy.imagegallery.actions.NextUnseenGroup;
|
||||
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
|
||||
import org.sleuthkit.autopsy.imagegallery.actions.TagSelectedFilesAction;
|
||||
import org.sleuthkit.autopsy.imagegallery.actions.Undo;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||
@@ -159,6 +160,9 @@ public class GroupPane extends BorderPane {
|
||||
|
||||
private final Forward forwardAction;
|
||||
|
||||
@FXML
|
||||
private Button undoButton;
|
||||
|
||||
@FXML
|
||||
private SplitMenuButton catSelectedSplitMenu;
|
||||
|
||||
@@ -219,6 +223,7 @@ public class GroupPane extends BorderPane {
|
||||
private ContextMenu contextMenu;
|
||||
|
||||
private Integer selectionAnchorIndex;
|
||||
private final Undo undoAction;
|
||||
|
||||
GroupViewMode getGroupViewMode() {
|
||||
return groupViewMode.get();
|
||||
@@ -261,6 +266,8 @@ public class GroupPane extends BorderPane {
|
||||
nextGroupAction = new NextUnseenGroup(controller);
|
||||
backAction = new Back(controller);
|
||||
forwardAction = new Forward(controller);
|
||||
undoAction = new Undo(controller);
|
||||
|
||||
FXMLConstructor.construct(this, "GroupPane.fxml");
|
||||
}
|
||||
|
||||
@@ -478,9 +485,7 @@ public class GroupPane extends BorderPane {
|
||||
};
|
||||
syncMode.run();
|
||||
//make togle states match view state
|
||||
groupViewMode.addListener((o) -> {
|
||||
syncMode.run();
|
||||
});
|
||||
groupViewMode.addListener(o -> syncMode.run());
|
||||
|
||||
slideShowToggle.toggleGroupProperty().addListener((o) -> {
|
||||
slideShowToggle.getToggleGroup().selectedToggleProperty().addListener((observable, oldToggle, newToggle) -> {
|
||||
@@ -491,16 +496,12 @@ public class GroupPane extends BorderPane {
|
||||
});
|
||||
|
||||
//listen to toggles and update view state
|
||||
slideShowToggle.setOnAction((ActionEvent t) -> {
|
||||
activateSlideShowViewer(selectionModel.lastSelectedProperty().get());
|
||||
});
|
||||
slideShowToggle.setOnAction(onAction -> activateSlideShowViewer(selectionModel.lastSelectedProperty().get()));
|
||||
|
||||
tileToggle.setOnAction((ActionEvent t) -> {
|
||||
activateTileViewer();
|
||||
});
|
||||
tileToggle.setOnAction(onAction -> activateTileViewer());
|
||||
|
||||
controller.viewState().addListener((ObservableValue<? extends GroupViewState> observable, GroupViewState oldValue, GroupViewState newValue) -> {
|
||||
setViewState(newValue);
|
||||
controller.viewState().addListener((observable, oldViewState, newViewState) -> {
|
||||
setViewState(newViewState);
|
||||
});
|
||||
|
||||
addEventFilter(KeyEvent.KEY_PRESSED, tileKeyboardNavigationHandler);
|
||||
@@ -515,12 +516,9 @@ public class GroupPane extends BorderPane {
|
||||
Collection<? extends ContextMenuActionsProvider> menuProviders = Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class);
|
||||
|
||||
for (ContextMenuActionsProvider provider : menuProviders) {
|
||||
|
||||
for (final Action act : provider.getActions()) {
|
||||
|
||||
if (act instanceof Presenter.Popup) {
|
||||
Presenter.Popup aact = (Presenter.Popup) act;
|
||||
|
||||
menuItems.add(SwingMenuItemAdapter.create(aact.getPopupPresenter()));
|
||||
}
|
||||
}
|
||||
@@ -571,12 +569,13 @@ public class GroupPane extends BorderPane {
|
||||
|
||||
ActionUtils.configureButton(nextGroupAction, nextButton);
|
||||
final EventHandler<ActionEvent> onAction = nextButton.getOnAction();
|
||||
nextButton.setOnAction((ActionEvent event) -> {
|
||||
nextButton.setOnAction(actionEvent -> {
|
||||
flashAnimation.stop();
|
||||
nextButton.setEffect(null);
|
||||
onAction.handle(event);
|
||||
onAction.handle(actionEvent);
|
||||
});
|
||||
|
||||
ActionUtils.configureButton(undoAction, undoButton);
|
||||
ActionUtils.configureButton(forwardAction, forwardButton);
|
||||
ActionUtils.configureButton(backAction, backButton);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user