wire seen by other examiners check box

This commit is contained in:
millmanorama 2018-09-16 12:24:24 +02:00
parent b2823dace1
commit 17bef5c68f
6 changed files with 121 additions and 80 deletions

View File

@ -634,8 +634,8 @@ public final class DrawableDB {
static private String getGroupIdQuery(GroupKey<?> groupKey) {
// query to find the group id from attribute/value
return String.format("( SELECT group_id FROM " + GROUPS_TABLENAME
+ " WHERE attribute = \'%s\' AND value = \'%s\' AND data_source_obj_id = %d)",
return String.format(" SELECT group_id FROM " + GROUPS_TABLENAME
+ " WHERE attribute = \'%s\' AND value = \'%s\' AND data_source_obj_id = %d",
groupKey.getAttribute().attrName.toString(),
groupKey.getValueDisplayName(),
(groupKey.getAttribute() == DrawableAttribute.PATH) ? groupKey.getDataSourceObjId() : 0);
@ -685,7 +685,8 @@ public final class DrawableDB {
try {
String groupSeenQueryStmt = "COUNT(*) as count FROM " + GROUPS_SEEN_TABLENAME
+ " WHERE group_id in ( " + getGroupIdQuery(groupKey) + ")"
+ " WHERE seen = 1 "
+ " AND group_id in ( " + getGroupIdQuery(groupKey) + ")"
+ (examinerId > 0 ? " AND examiner_id = " + examinerId : "");// query to find the group id from attribute/value
tskCase.getCaseDbAccessManager().select(groupSeenQueryStmt, queryResultProcessor);

View File

@ -26,6 +26,7 @@ import java.util.logging.Level;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyLongProperty;
import javafx.beans.property.ReadOnlyLongWrapper;
@ -165,8 +166,8 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
return seen.get();
}
public ReadOnlyBooleanWrapper seenProperty() {
return seen;
public ReadOnlyBooleanProperty seenProperty() {
return seen.getReadOnlyProperty();
}
@Subscribe

View File

@ -42,10 +42,12 @@ import java.util.TreeSet;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
@ -126,6 +128,7 @@ public class GroupManager {
private final ReadOnlyObjectWrapper< DrawableAttribute<?>> groupByProp = new ReadOnlyObjectWrapper<>(DrawableAttribute.PATH);
private final ReadOnlyObjectWrapper<SortOrder> sortOrderProp = new ReadOnlyObjectWrapper<>(SortOrder.ASCENDING);
private final ReadOnlyObjectWrapper<DataSource> dataSourceProp = new ReadOnlyObjectWrapper<>(null);//null indicates all datasources
private final ReadOnlyBooleanWrapper collaborativeModeProp = new ReadOnlyBooleanWrapper(false);
private final GroupingService regrouper;
@ -249,15 +252,15 @@ public class GroupManager {
Examiner examiner = controller.getSleuthKitCase().getCurrentExaminer();
getDrawableDB().markGroupSeen(group.getGroupKey(), seen, examiner.getId());
group.setSeen(seen);
updateUnSeenGroups(group, seen);
updateUnSeenGroups(group);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error marking group as seen", ex); //NON-NLS
}
});
}
synchronized private void updateUnSeenGroups(DrawableGroup group, boolean seen) {
if (seen) {
synchronized private void updateUnSeenGroups(DrawableGroup group) {
if (group.isSeen()) {
unSeenGroups.removeAll(group);
} else if (unSeenGroups.contains(group) == false) {
unSeenGroups.add(group);
@ -580,8 +583,8 @@ public class GroupManager {
Set<Long> fileIDs = getFileIDsInGroup(groupKey);
if (Objects.nonNull(fileIDs)) {
Examiner examiner = controller.getSleuthKitCase().getCurrentExaminer();
final boolean groupSeen = getDrawableDB().isGroupSeenByExaminer(groupKey, examiner.getId());
long examinerID = collaborativeModeProp.get() ? -1 : controller.getSleuthKitCase().getCurrentExaminer().getId();
final boolean groupSeen = getDrawableDB().isGroupSeenByExaminer(groupKey, examinerID);
DrawableGroup group;
if (groupMap.containsKey(groupKey)) {
@ -598,10 +601,9 @@ public class GroupManager {
analyzedGroups.add(group);
sortAnalyzedGroups();
}
updateUnSeenGroups(group, groupSeen);
updateUnSeenGroups(group);
return group;
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "failed to get files for group: " + groupKey.getAttribute().attrName.toString() + " = " + groupKey.getValue(), ex); //NON-NLS
@ -634,6 +636,29 @@ public class GroupManager {
}
}
synchronized public void setCollaborativeMode(Boolean newValue) {
collaborativeModeProp.set(newValue);
analyzedGroups.forEach(group -> {
try {
boolean groupSeenByExaminer = getDrawableDB().isGroupSeenByExaminer(
group.getGroupKey(),
newValue ? -1 : controller.getSleuthKitCase().getCurrentExaminer().getId()
);
group.setSeen(groupSeenByExaminer);
updateUnSeenGroups(group);
if (group.isSeen()) {
unSeenGroups.removeAll(group);
} else if (unSeenGroups.contains(group) == false) {
unSeenGroups.add(group);
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error checking seen state of group.", ex);
}
});
sortUnseenGroups();
}
/**
* Task to query database for files in sorted groups and build
* DrawableGroups for them.

View File

@ -2,6 +2,7 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.RadioButton?>
@ -11,6 +12,7 @@
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
@ -18,7 +20,7 @@
<?import org.controlsfx.control.GridView?>
<?import org.controlsfx.control.SegmentedButton?>
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1">
<center>
<GridView fx:id="gridView" BorderPane.alignment="CENTER" />
@ -29,7 +31,7 @@
<HBox alignment="CENTER_LEFT" spacing="5.0" BorderPane.alignment="TOP_LEFT">
<children>
<Label fx:id="bottomLabel" text="Group Viewing History: " />
<Button fx:id="backButton" mnemonicParsing="false" text="back">
<Button fx:id="backButton" mnemonicParsing="false" text="Back">
<graphic>
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
<image>
@ -38,7 +40,7 @@
</ImageView>
</graphic>
</Button>
<Button fx:id="forwardButton" mnemonicParsing="false" text="forward">
<Button fx:id="forwardButton" mnemonicParsing="false" text="Forward">
<graphic>
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
<image>
@ -56,18 +58,23 @@
<right>
<HBox alignment="CENTER_RIGHT" spacing="5.0" BorderPane.alignment="TOP_RIGHT">
<children>
<Button fx:id="nextButton" contentDisplay="RIGHT" mnemonicParsing="false" text="next unseen group" BorderPane.alignment="CENTER_RIGHT">
<BorderPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</BorderPane.margin>
<graphic>
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../../images/control-double.png" />
</image>
</ImageView>
</graphic>
</Button>
<CheckBox fx:id="seenByOtherExaminersCheckBox" mnemonicParsing="false" text="Don't show groups seen by other examiners" />
<AnchorPane fx:id="nextButtonPane" BorderPane.alignment="CENTER_RIGHT">
<BorderPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</BorderPane.margin>
<children>
<Button fx:id="nextButton" contentDisplay="RIGHT" mnemonicParsing="false" text="Next Unseen Group">
<graphic>
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../../images/control-double.png" />
</image>
</ImageView>
</graphic>
</Button>
</children>
</AnchorPane>
</children>
<BorderPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />

View File

@ -57,7 +57,9 @@ import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Bounds;
import javafx.scene.Cursor;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
@ -85,6 +87,7 @@ import static javafx.scene.input.KeyCode.RIGHT;
import static javafx.scene.input.KeyCode.UP;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.BorderStroke;
@ -95,6 +98,7 @@ import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.util.Duration;
import javax.swing.SwingUtilities;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import org.apache.commons.lang3.StringUtils;
import org.controlsfx.control.GridCell;
import org.controlsfx.control.GridView;
@ -135,9 +139,8 @@ import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A GroupPane displays the contents of a {@link DrawableGroup}. It supports
* both a {@link GridView} based view and a {@link SlideShowView} view by
* swapping out its internal components.
* A GroupPane displays the contents of a DrawableGroup. It supports both
* GridView and SlideShowView modes by swapping out its internal components.
*
*
* TODO: Extract the The GridView instance to a separate class analogous to the
@ -150,27 +153,21 @@ import org.sleuthkit.datamodel.TskCoreException;
public class GroupPane extends BorderPane {
private static final Logger logger = Logger.getLogger(GroupPane.class.getName());
private final ListeningExecutorService exec = TaskUtils.getExecutorForClass(GroupPane.class);
private static final BorderWidths BORDER_WIDTHS_2 = new BorderWidths(2);
private static final CornerRadii CORNER_RADII_2 = new CornerRadii(2);
private static final DropShadow DROP_SHADOW = new DropShadow(10, Color.BLUE);
private static final Timeline flashAnimation = new Timeline(new KeyFrame(Duration.millis(400), new KeyValue(DROP_SHADOW.radiusProperty(), 1, Interpolator.LINEAR)),
private static final Timeline flashAnimation = new Timeline(
new KeyFrame(Duration.millis(400), new KeyValue(DROP_SHADOW.radiusProperty(), 1, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(400), new KeyValue(DROP_SHADOW.radiusProperty(), 15, Interpolator.LINEAR))
);
private final FileIDSelectionModel selectionModel;
private static final List<KeyCode> categoryKeyCodes
= Arrays.asList(KeyCode.NUMPAD0, KeyCode.NUMPAD1, KeyCode.NUMPAD2, KeyCode.NUMPAD3, KeyCode.NUMPAD4, KeyCode.NUMPAD5,
KeyCode.DIGIT0, KeyCode.DIGIT1, KeyCode.DIGIT2, KeyCode.DIGIT3, KeyCode.DIGIT4, KeyCode.DIGIT5);
private final Back backAction;
private final Forward forwardAction;
@FXML
private Button undoButton;
@FXML
@ -178,13 +175,10 @@ public class GroupPane extends BorderPane {
@FXML
private SplitMenuButton catSelectedSplitMenu;
@FXML
private SplitMenuButton tagSelectedSplitMenu;
@FXML
private ToolBar headerToolBar;
@FXML
private ToggleButton cat0Toggle;
@FXML
@ -201,26 +195,25 @@ public class GroupPane extends BorderPane {
@FXML
private SegmentedButton segButton;
private SlideShowView slideShowPane;
@FXML
private ToggleButton slideShowToggle;
@FXML
private GridView<Long> gridView;
@FXML
private ToggleButton tileToggle;
private SlideShowView slideShowPane;
@FXML
private GridView<Long> gridView;
@FXML
private Button nextButton;
@FXML
private AnchorPane nextButtonPane;
@FXML
private CheckBox seenByOtherExaminersCheckBox;
@FXML
private Button backButton;
@FXML
private Button forwardButton;
@FXML
private Label groupLabel;
@FXML
@ -237,30 +230,27 @@ public class GroupPane extends BorderPane {
@FXML
private HBox catSplitMenuContainer;
private final KeyboardHandler tileKeyboardNavigationHandler = new KeyboardHandler();
private final NextUnseenGroup nextGroupAction;
private final ListeningExecutorService exec = TaskUtils.getExecutorForClass(GroupPane.class);
private final ImageGalleryController controller;
private ContextMenu contextMenu;
private final FileIDSelectionModel selectionModel;
private Integer selectionAnchorIndex;
private final UndoAction undoAction;
private final RedoAction redoAction;
private final Back backAction;
private final Forward forwardAction;
private final NextUnseenGroup nextGroupAction;
GroupViewMode getGroupViewMode() {
return groupViewMode.get();
}
private final KeyboardHandler tileKeyboardNavigationHandler = new KeyboardHandler();
/**
* the current GroupViewMode of this GroupPane
*/
private ContextMenu contextMenu;
/** the current GroupViewMode of this GroupPane */
private final SimpleObjectProperty<GroupViewMode> groupViewMode = new SimpleObjectProperty<>(GroupViewMode.TILE);
/**
* the grouping this pane is currently the view for
*/
/** the grouping this pane is currently the view for */
private final ReadOnlyObjectWrapper<DrawableGroup> grouping = new ReadOnlyObjectWrapper<>();
/**
@ -294,6 +284,10 @@ public class GroupPane extends BorderPane {
FXMLConstructor.construct(this, "GroupPane.fxml"); //NON-NLS
}
GroupViewMode getGroupViewMode() {
return groupViewMode.get();
}
@ThreadConfined(type = ThreadType.JFX)
public void activateSlideShowViewer(Long slideShowFileID) {
groupViewMode.set(GroupViewMode.SLIDE_SHOW);
@ -340,7 +334,9 @@ public class GroupPane extends BorderPane {
}
/**
* create the string to display in the group header
* Create the string to display in the group header.
*
* @return The string to display in the group header.
*/
@NbBundle.Messages({"# {0} - default group name",
"# {1} - hashset hits count",
@ -391,19 +387,20 @@ public class GroupPane extends BorderPane {
"GroupPane.catContainerLabel.displayText=Categorize Selected File:",
"GroupPane.catHeadingLabel.displayText=Category:"})
void initialize() {
assert cat0Toggle != null : "fx:id=\"cat0Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
assert cat1Toggle != null : "fx:id=\"cat1Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
assert cat2Toggle != null : "fx:id=\"cat2Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
assert cat3Toggle != null : "fx:id=\"cat3Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
assert cat4Toggle != null : "fx:id=\"cat4Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
assert cat5Toggle != null : "fx:id=\"cat5Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
assert cat0Toggle != null : "fx:id=\"cat0Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert cat1Toggle != null : "fx:id=\"cat1Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert cat2Toggle != null : "fx:id=\"cat2Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert cat3Toggle != null : "fx:id=\"cat3Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert cat4Toggle != null : "fx:id=\"cat4Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert cat5Toggle != null : "fx:id=\"cat5Toggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert gridView != null : "fx:id=\"tilePane\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert catSelectedSplitMenu != null : "fx:id=\"grpCatSplitMenu\" was not injected: check your FXML file 'GroupHeader.fxml'.";
assert tagSelectedSplitMenu != null : "fx:id=\"grpTagSplitMenu\" was not injected: check your FXML file 'GroupHeader.fxml'.";
assert headerToolBar != null : "fx:id=\"headerToolBar\" was not injected: check your FXML file 'GroupHeader.fxml'.";
assert segButton != null : "fx:id=\"previewList\" was not injected: check your FXML file 'GroupHeader.fxml'.";
assert slideShowToggle != null : "fx:id=\"segButton\" was not injected: check your FXML file 'GroupHeader.fxml'.";
assert tileToggle != null : "fx:id=\"tileToggle\" was not injected: check your FXML file 'GroupHeader.fxml'.";
assert catSelectedSplitMenu != null : "fx:id=\"grpCatSplitMenu\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert tagSelectedSplitMenu != null : "fx:id=\"grpTagSplitMenu\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert headerToolBar != null : "fx:id=\"headerToolBar\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert segButton != null : "fx:id=\"previewList\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert slideShowToggle != null : "fx:id=\"segButton\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert tileToggle != null : "fx:id=\"tileToggle\" was not injected: check your FXML file 'GroupPane.fxml'.";
assert seenByOtherExaminersCheckBox != null : "fx:id=\"seenByOtherExaminersCheckBox\" was not injected: check your FXML file 'GroupPane.fxml'.";
for (DhsImageCategory cat : DhsImageCategory.values()) {
ToggleButton toggleForCategory = getToggleForCategory(cat);
@ -530,6 +527,16 @@ public class GroupPane extends BorderPane {
}
});
seenByOtherExaminersCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
nextButtonPane.setDisable(true);
nextButtonPane.setCursor(Cursor.WAIT);
exec.submit(() -> controller.getGroupManager().setCollaborativeMode(newValue))
.addListener(() -> {
nextButtonPane.setDisable(false);
nextButtonPane.setCursor(Cursor.DEFAULT);
}, Platform::runLater);
});
//listen to tile selection and make sure it is visible in scroll area
selectionModel.lastSelectedProperty().addListener((observable, oldFileID, newFileId) -> {
if (groupViewMode.get() == GroupViewMode.SLIDE_SHOW
@ -607,7 +614,7 @@ public class GroupPane extends BorderPane {
* assigns a grouping for this pane to represent and initializes grouping
* specific properties and listeners
*
* @param grouping the new grouping assigned to this group
* @param newViewState
*/
void setViewState(GroupViewState newViewState) {
@ -894,7 +901,7 @@ public class GroupPane extends BorderPane {
if (t.getClickCount() == 1) {
selectAllFiles();
}
if (selectionModel.getSelected().isEmpty() == false) {
if (isNotEmpty(selectionModel.getSelected())) {
if (contextMenu == null) {
contextMenu = buildContextMenu();
}

View File

@ -173,7 +173,7 @@ class GroupCellFactory {
private final InvalidationListener groupListener = new GroupListener<>(this);
/**
* reference to group files listener that allows us to remove it from a
* Reference to group files listener that allows us to remove it from a
* group when a new group is assigned to this Cell
*/
@Override