Refactor multi-user case UI for reuse, create admin case dashboard UI

This commit is contained in:
Richard Cordovano 2019-02-21 13:23:34 -05:00
parent 52df9894ea
commit f3ddd206a2
29 changed files with 1103 additions and 172 deletions

View File

@ -313,6 +313,8 @@
<package>org.sleuthkit.autopsy.appservices</package>
<package>org.sleuthkit.autopsy.casemodule</package>
<package>org.sleuthkit.autopsy.casemodule.events</package>
<package>org.sleuthkit.autopsy.casemodule.multiusercases</package>
<package>org.sleuthkit.autopsy.casemodule.multiusercasesbrowser</package>
<package>org.sleuthkit.autopsy.casemodule.services</package>
<package>org.sleuthkit.autopsy.contentviewers</package>
<package>org.sleuthkit.autopsy.coordinationservice</package>

View File

@ -233,7 +233,10 @@ ImageFilePanel.md5HashTextField.text=
ImageFilePanel.errorLabel.text=Error Label
ImageFilePanel.hashValuesNoteLabel.text=NOTE: These values will not be validated when the data source is added.
ImageFilePanel.hashValuesLabel.text=Hash Values (optional):
OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name
OpenMultiUserCasePanel.cancelButton.text=Cancel
OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case
# To change this license header, choose License Headers in Project Properties.
# To change this template file, choose Tools | Templates
# and open the template in the editor.
OpenMultiUserCasePanel.openSingleUserCaseButton.text=Open Single-User Case...
OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case
OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name

View File

@ -18,7 +18,7 @@
*/
package org.sleuthkit.autopsy.casemodule;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

View File

@ -88,7 +88,7 @@ public final class CaseOpenAction extends CallableSystemAction implements Action
* metadata file (.aut file). Upon confirming the selection, it will attempt
* to open the case described by the file.
*/
void openCaseSelectionWindow() {
public void openCaseSelectionWindow() {
String optionsDlgTitle = NbBundle.getMessage(Case.class, "CloseCaseWhileIngesting.Warning.title");
String optionsDlgMessage = NbBundle.getMessage(Case.class, "CloseCaseWhileIngesting.Warning");
if (IngestRunningCheck.checkAndConfirmProceed(optionsDlgTitle, optionsDlgMessage)) {

View File

@ -24,7 +24,7 @@ import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
@ -32,7 +32,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
* An action that opens a multi-user case and hides the open multi-user case
* dialog given the coordination service node data for the case.
*/
final class OpenMultiUserCaseAction extends AbstractAction {
public final class OpenMultiUserCaseAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(OpenMultiUserCaseAction.class.getName());
@ -42,11 +42,14 @@ final class OpenMultiUserCaseAction extends AbstractAction {
* Constructs an action that opens a multi-user case and hides the open
* multi-user case dialog given the coordination service node data for the
* case.
*
* @param caseNodeData The coordination service node data for the case
* associated with this action.
*/
@NbBundle.Messages({
"OpenMultiUserCaseAction.menuItemText=Open Case"
})
OpenMultiUserCaseAction(CaseNodeData caseNodeData) {
public OpenMultiUserCaseAction(CaseNodeData caseNodeData) {
super(Bundle.OpenMultiUserCaseAction_menuItemText());
this.caseNodeData = caseNodeData;
}
@ -92,7 +95,6 @@ final class OpenMultiUserCaseAction extends AbstractAction {
@Override
public OpenMultiUserCaseAction clone() throws CloneNotSupportedException {
super.clone();
throw new CloneNotSupportedException();
}

View File

@ -26,7 +26,7 @@ import org.openide.windows.WindowManager;
/**
* A singleton JDialog that allows a user to open a multi-user case.
*/
final class OpenMultiUserCaseDialog extends JDialog {
public final class OpenMultiUserCaseDialog extends JDialog {
private static final long serialVersionUID = 1L;
private static OpenMultiUserCaseDialog instance;

View File

@ -0,0 +1,67 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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.casemodule;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercasesbrowser.MultiUserCaseBrowserCustomizer;
/**
* A customizer for a multi-user case browser panel that presents a tabular view
* of the multi-user cases known to the coordination service in the open
* multi-user case dialog.
*/
final class OpenMultiUserCaseNodeCustomizer implements MultiUserCaseBrowserCustomizer {
@Override
public List<Column> getColumns() {
List<Column> properties = new ArrayList<>();
// properties.add(Column.DISPLAY_NAME);
properties.add(Column.CREATE_DATE);
properties.add(Column.DIRECTORY);
return properties;
}
@Override
public List<SortColumn> getSortColumns() {
List<SortColumn> sortColumns = new ArrayList<>();
sortColumns.add(new SortColumn(Column.CREATE_DATE, false, 1));
return sortColumns;
}
@Override
public boolean allowMultiSelect() {
return false;
}
@Override
public List<Action> getActions(CaseNodeData nodeData) {
List<Action> actions = new ArrayList<>();
actions.add(new OpenMultiUserCaseAction(nodeData));
return actions;
}
@Override
public Action getPreferredAction(CaseNodeData nodeData) {
return new OpenMultiUserCaseAction(nodeData);
}
}

View File

@ -24,28 +24,28 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="caseExplorerScrollPane" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="searchLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
<Component id="openSingleUserCaseButton" linkSize="10" min="-2" pref="172" max="-2" attributes="0"/>
<EmptySpace pref="96" max="32767" attributes="0"/>
<Component id="openSelectedCaseButton" linkSize="10" min="-2" pref="160" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" linkSize="10" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="searchLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
<Component id="openSingleUserCaseButton" linkSize="10" min="-2" pref="172" max="-2" attributes="0"/>
<EmptySpace pref="74" max="32767" attributes="0"/>
<Component id="openSelectedCaseButton" linkSize="10" min="-2" pref="160" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" linkSize="10" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="caseBrowserScrollPane" pref="948" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="caseExplorerScrollPane" min="-2" pref="450" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" pref="462" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cancelButton" linkSize="7" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="openSingleUserCaseButton" linkSize="7" alignment="3" min="-2" max="-2" attributes="0"/>
@ -54,6 +54,13 @@
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="caseBrowserScrollPane" min="-2" pref="445" max="-2" attributes="0"/>
<EmptySpace pref="49" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
@ -100,10 +107,6 @@
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="caseExplorerScrollPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Container>
<Component class="javax.swing.JButton" name="openSelectedCaseButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
@ -114,5 +117,9 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openSelectedCaseButtonActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="caseBrowserScrollPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -18,13 +18,15 @@
*/
package org.sleuthkit.autopsy.casemodule;
import org.sleuthkit.autopsy.casemodule.multiusercasesbrowser.MultiUserCasesBrowserPanel;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.event.ListSelectionEvent;
import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercasesbrowser.MultiUserCaseNode;
/**
* A JPanel that allows a user to open a multi-user case.
@ -46,9 +48,9 @@ final class OpenMultiUserCasePanel extends JPanel {
OpenMultiUserCasePanel(JDialog parentDialog) {
this.parentDialog = parentDialog;
initComponents(); // Machine generated code
caseBrowserPanel = new MultiUserCasesBrowserPanel();
caseExplorerScrollPane.add(caseBrowserPanel);
caseExplorerScrollPane.setViewportView(caseBrowserPanel);
caseBrowserPanel = new MultiUserCasesBrowserPanel(new ExplorerManager(), new OpenMultiUserCaseNodeCustomizer());
caseBrowserScrollPane.add(caseBrowserPanel);
caseBrowserScrollPane.setViewportView(caseBrowserPanel);
openSelectedCaseButton.setEnabled(false);
caseBrowserPanel.addListSelectionListener((ListSelectionEvent event) -> {
openSelectedCaseButton.setEnabled(caseBrowserPanel.getExplorerManager().getSelectedNodes().length > 0);
@ -60,7 +62,7 @@ final class OpenMultiUserCasePanel extends JPanel {
* the coordination service..
*/
void refreshDisplay() {
caseBrowserPanel.refreshCases();
caseBrowserPanel.displayCases();
}
/**
@ -75,8 +77,8 @@ final class OpenMultiUserCasePanel extends JPanel {
openSingleUserCaseButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
searchLabel = new javax.swing.JLabel();
caseExplorerScrollPane = new javax.swing.JScrollPane();
openSelectedCaseButton = new javax.swing.JButton();
caseBrowserScrollPane = new javax.swing.JScrollPane();
setName("Completed Cases"); // NOI18N
setPreferredSize(new java.awt.Dimension(960, 485));
@ -115,17 +117,19 @@ final class OpenMultiUserCasePanel extends JPanel {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(caseExplorerScrollPane)
.addGroup(layout.createSequentialGroup()
.addComponent(searchLabel)
.addGap(32, 32, 32)
.addComponent(openSingleUserCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 172, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 96, Short.MAX_VALUE)
.addComponent(openSelectedCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 160, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addComponent(searchLabel)
.addGap(32, 32, 32)
.addComponent(openSingleUserCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 172, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 74, Short.MAX_VALUE)
.addComponent(openSelectedCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 160, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(caseBrowserScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 948, Short.MAX_VALUE)
.addContainerGap()))
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, openSelectedCaseButton, openSingleUserCaseButton});
@ -133,15 +137,18 @@ final class OpenMultiUserCasePanel extends JPanel {
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(caseExplorerScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGap(462, 462, 462)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(openSingleUserCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(searchLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(openSelectedCaseButton))
.addContainerGap())
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(caseBrowserScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 445, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(49, Short.MAX_VALUE)))
);
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {cancelButton, openSelectedCaseButton, openSingleUserCaseButton});
@ -179,14 +186,14 @@ final class OpenMultiUserCasePanel extends JPanel {
Node[] selectedNodes = explorerManager.getSelectedNodes();
if (selectedNodes.length > 0 && selectedNodes[0] instanceof MultiUserCaseNode) {
MultiUserCaseNode caseNode = (MultiUserCaseNode) selectedNodes[0];
CaseNodeData nodeData = caseNode.getCaseNodeData();
CaseNodeData nodeData = caseNode.getLookup().lookup(CaseNodeData.class);
new OpenMultiUserCaseAction(nodeData).actionPerformed(evt);
}
}//GEN-LAST:event_openSelectedCaseButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancelButton;
private javax.swing.JScrollPane caseExplorerScrollPane;
private javax.swing.JScrollPane caseBrowserScrollPane;
private javax.swing.JButton openSelectedCaseButton;
private javax.swing.JButton openSingleUserCaseButton;
private javax.swing.JLabel searchLabel;

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.coordinationservice;
package org.sleuthkit.autopsy.casemodule.multiusercases;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

View File

@ -16,9 +16,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
package org.sleuthkit.autopsy.casemodule.multiusercases;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import java.io.File;
import java.io.IOException;
import java.nio.file.LinkOption;
@ -28,6 +27,7 @@ import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -35,7 +35,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* Queries the coordination service to collect the multi-user case node data
* stored in the case directory lock ZooKeeper nodes.
*/
final class MultiUserCaseNodeDataCollector {
final public class MultiUserCaseNodeDataCollector {
private static final Logger logger = Logger.getLogger(MultiUserCaseNodeDataCollector.class.getName());
private static final String CASE_AUTO_INGEST_LOG_NAME = "AUTO_INGEST_LOG.TXT"; //NON-NLS

View File

@ -0,0 +1,161 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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.casemodule.multiusercasesbrowser;
import java.util.List;
import javax.swing.Action;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
/**
* The interface for defining a customizer for a multi-user case browser panel
* that presents a tabular view of the multi-user cases known to the
* coordination service.
*/
public interface MultiUserCaseBrowserCustomizer {
/**
* An enumeration of the columns that can be added to the browser's tabular
* view. NOTE THAT THE DISPLAY_NAME COLUMN IS ADDED AUTOMATICALLY BY THE
* BROWSER AND ALL OTHER COLUMNS ARE OPTIONAL.
*/
@NbBundle.Messages({
"MultiUserCaseBrowserCustomizer.column.displayName=Name",
"MultiUserCaseBrowserCustomizer.column.createTime=Create Time",
"MultiUserCaseBrowserCustomizer.column.directory=Directory"
})
public enum Column {
DISPLAY_NAME(Bundle.MultiUserCaseBrowserCustomizer_column_displayName()),
CREATE_DATE(Bundle.MultiUserCaseBrowserCustomizer_column_createTime()),
DIRECTORY(Bundle.MultiUserCaseBrowserCustomizer_column_directory());
private final String displayName;
private Column(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
/**
* Gets the columns the tabular view in the browser should display. The
* columns will be displayed in the order in which they appear in the list.
* NOTE THAT THE DISPLAY_NAME COLUMN IS ADDED AS THE FIRST COLUMN
* AUTOMATICALLY AND SHOULD NOT BE INCLUDED IN THIS LIST.
*
* @return A Column object.
*/
List<Column> getColumns();
/**
* Gets a specification of the columns, if any, that define the default
* sorting of the MultiUserCaseNodes in the browser.
*
* @return A list, possibly empty, of SortColumn objects.
*/
List<SortColumn> getSortColumns();
/**
* Whether or not the browser should allow multiple selection of the
* MultiUserCaseNodes displayed in the browser.
*
* @return True or false.
*/
boolean allowMultiSelect();
/**
* Gets the context menu Actions for the MultiUserCaseNodes diplayed in the
* browser. Can include actions that work with multiple node selection.
*
* @param nodeData The coordination service node data for a given
* MultiUserCaseNode. Ignored for multi-select actions.
*
* @return A list of Actions.
*/
List<Action> getActions(CaseNodeData nodeData);
/**
* Gets the preferred action for the MultiUserCaseNodes displayed in the
* browser, i.e., the action to be performed when a node is double clicked.
*
* @param nodeData The coordination service node data for a given
* MultiUserCaseNode.
*
* @return The preferred action.
*/
Action getPreferredAction(CaseNodeData nodeData);
/**
* A specification of the sorting details for a column in the browser's
* OutlineView.
*/
public final static class SortColumn {
private final Column column;
private final boolean sortAscending;
private final int sortRank;
/**
* Constucts a specification of the sorting details for a column in the
* browser's OutlineView.
*
* @param column
* @param sortAscending
* @param sortRank
*/
public SortColumn(Column column, boolean sortAscending, int sortRank) {
this.column = column;
this.sortAscending = sortAscending;
this.sortRank = sortRank;
}
/**
* Gets the column to be sorted.
*
* @return The column.
*/
public Column column() {
return column;
}
/**
* Indicates whether or not the sort should be ascending.
*
* @return True or false.
*/
public boolean sortAscending() {
return sortAscending;
}
/**
* Gets the rank of the sort, i.e., should it be the first column to be
* sorted, the second column, etc.
*
* @return The sort rank.
*/
public int sortRank() {
return sortRank;
}
}
}

View File

@ -16,44 +16,45 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
package org.sleuthkit.autopsy.casemodule.multiusercasesbrowser;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
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.casemodule.multiusercases.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercasesbrowser.MultiUserCaseBrowserCustomizer.Column;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* A NetBeans Explorer View node that represents a multi-user case.
* A NetBeans Explorer View node that represents a multi-user case in a
* multi-user cases browser panel.
*/
final class MultiUserCaseNode extends AbstractNode {
public final class MultiUserCaseNode extends AbstractNode {
private final CaseNodeData caseNodeData;
private final MultiUserCaseBrowserCustomizer customizer;
/**
* Constructs a NetBeans Explorer View node that represents a multi-user
* case.
* casein a multi-user cases browser panel.
*
* @param caseNodeData The coordination service node data for the case.
* @param customizer A browser customizer that supplies the actions and
* property sheet properties of the node.
*/
MultiUserCaseNode(CaseNodeData caseNodeData) {
super(Children.LEAF);
MultiUserCaseNode(CaseNodeData caseNodeData, MultiUserCaseBrowserCustomizer customizer) {
super(Children.LEAF, Lookups.fixed(caseNodeData));
super.setName(caseNodeData.getDisplayName());
setName(caseNodeData.getDisplayName());
setDisplayName(caseNodeData.getDisplayName());
super.setDisplayName(caseNodeData.getDisplayName());
this.caseNodeData = caseNodeData;
this.customizer = customizer;
}
@NbBundle.Messages({
"MultiUserCaseNode.column.name=Name",
"MultiUserCaseNode.column.createTime=Create Time",
"MultiUserCaseNode.column.path=Path"
})
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
@ -62,42 +63,33 @@ final class MultiUserCaseNode extends AbstractNode {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_name(),
Bundle.MultiUserCaseNode_column_name(),
Bundle.MultiUserCaseNode_column_name(),
caseNodeData.getDisplayName()));
sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_createTime(),
Bundle.MultiUserCaseNode_column_createTime(),
Bundle.MultiUserCaseNode_column_createTime(),
caseNodeData.getCreateDate()));
sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_path(),
Bundle.MultiUserCaseNode_column_path(),
Bundle.MultiUserCaseNode_column_path(),
caseNodeData.getDirectory().toString()));
for (Column property : customizer.getColumns()) {
String propName = property.getDisplayName();
switch (property) {
case CREATE_DATE:
sheetSet.put(new NodeProperty<>(propName, propName, propName, caseNodeData.getCreateDate()));
break;
case DIRECTORY:
sheetSet.put(new NodeProperty<>(propName, propName, propName, caseNodeData.getDirectory().toString()));
break;
default:
break;
}
}
return sheet;
}
@Override
public Action[] getActions(boolean context) {
List<Action> actions = new ArrayList<>();
actions.add(new OpenMultiUserCaseAction(this.caseNodeData));
actions.add(new OpenCaseAutoIngestLogAction(this.caseNodeData));
actions.addAll(customizer.getActions(caseNodeData));
actions.addAll(Arrays.asList(super.getActions(context)));
return actions.toArray(new Action[actions.size()]);
}
@Override
public Action getPreferredAction() {
return new OpenMultiUserCaseAction(this.caseNodeData);
return customizer.getPreferredAction(caseNodeData);
}
/**
* Gets the coordintaion service case node data this Explorer View node
* represents.
*
* @return The case node data.
*/
CaseNodeData getCaseNodeData() {
return this.caseNodeData;
}
}

View File

@ -32,7 +32,7 @@
</Property>
<Property name="opaque" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[5, 5]"/>
<Dimension value="[500, 500]"/>
</Property>
</Properties>
<Constraints>

View File

@ -16,80 +16,60 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
package org.sleuthkit.autopsy.casemodule.multiusercasesbrowser;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumnModel;
import org.netbeans.swing.etable.ETableColumn;
import org.netbeans.swing.etable.ETableColumnModel;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager;
import org.openide.util.NbBundle;
import org.openide.explorer.view.OutlineView;
import org.sleuthkit.autopsy.casemodule.multiusercasesbrowser.MultiUserCaseBrowserCustomizer.Column;
import org.sleuthkit.autopsy.casemodule.multiusercasesbrowser.MultiUserCaseBrowserCustomizer.SortColumn;
/**
* A JPanel with a scroll pane child component that contains a NetBeans
* OutlineView that can be used to display a list of the multi-user cases known
* to the coordination service.
* A JPanel that contains a NetBeans OutlineView that is used to provide a
* tabular view of the multi-user cases known to the coordination service. The
* outline view set up, including the property sheets and actions of the
* MultiUserCaseNodes it displays, are defined using a
* MultiUserCaseBrowserCustomizer. Each MultiUserCaseNode has a CaseNodeData
* object in its Lookup.
*/
@SuppressWarnings("PMD.SingularField") // Matisse-generated UI widgets cause lots of false positives
final class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerManager.Provider {
@SuppressWarnings("PMD.SingularField") // Matisse-generated UI widgets cause lots of false positives for this in PMD
public final class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerManager.Provider {
private static final long serialVersionUID = 1L;
private final ExplorerManager explorerManager;
private final MultiUserCaseBrowserCustomizer customizer;
private final OutlineView outlineView;
private final Outline outline;
/**
* Constructs a JPanel with a scroll pane child component that contains a
* NetBeans OutlineView that can be used to display a list of the multi-user
* cases known to the coordination service.
* Constructs a JPanel that contains a NetBeans OutlineView that is used to
* provide a tabular view of the multi-user cases known to the coordination
* service. The outline view set up, including the property sheets and
* actions of the MultiUserCaseNodes it displays, are defined using a
* MultiUserCaseBrowserCustomizer. Each MultiUserCaseNode has a CaseNodeData
* object in its Lookup.
*
* @param explorerManager The ExplorerManager for the browser's OutlineView.
* @param customizer A customizer for the browser.
*/
MultiUserCasesBrowserPanel() {
explorerManager = new ExplorerManager();
outlineView = new org.openide.explorer.view.OutlineView();
public MultiUserCasesBrowserPanel(ExplorerManager explorerManager, MultiUserCaseBrowserCustomizer customizer) {
this.explorerManager = explorerManager;
this.customizer = customizer;
initComponents();
outline = outlineView.getOutline();
outlineView = new org.openide.explorer.view.OutlineView();
outline = this.outlineView.getOutline();
configureOutlineView();
explorerManager.setRootContext(new MultiUserCasesRootNode());
}
/**
* Configures the child scroll pane component's child OutlineView component.
*/
private void configureOutlineView() {
outlineView.setPropertyColumns(
Bundle.MultiUserCaseNode_column_createTime(), Bundle.MultiUserCaseNode_column_createTime(),
Bundle.MultiUserCaseNode_column_path(), Bundle.MultiUserCaseNode_column_path());
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.MultiUserCaseNode_column_name());
outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
TableColumnModel columnModel = outline.getColumnModel();
int pathColumnIndex = 0;
int dateColumnIndex = 0;
for (int index = 0; index < columnModel.getColumnCount(); index++) {
if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.MultiUserCaseNode_column_path())) {
pathColumnIndex = index;
} else if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.MultiUserCaseNode_column_createTime())) {
dateColumnIndex = index;
}
}
/*
* Hide path column by default (user can unhide it)
*/
ETableColumn column = (ETableColumn) columnModel.getColumn(pathColumnIndex);
((ETableColumnModel) columnModel).setColumnHidden(column, true);
outline.setRootVisible(false);
/*
* Sort on Created date column in descending order by default.
*/
outline.setColumnSorted(dateColumnIndex, false, 1);
caseTableScrollPane.add(outlineView);
caseTableScrollPane.setViewportView(outlineView);
this.setVisible(true);
this.setVisible(true);
displayCases();
}
@Override
@ -97,23 +77,58 @@ final class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements Exp
return explorerManager;
}
/**
* Configures the child scroll pane component's child OutlineView component.
*/
private void configureOutlineView() {
/*
* Set up the outline view columns and sorting.
*/
Map<Column, SortColumn> sortColumns = new HashMap<>();
for (SortColumn sortColumn : customizer.getSortColumns()) {
sortColumns.put(sortColumn.column(), sortColumn);
}
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Column.DISPLAY_NAME.getDisplayName());
if (sortColumns.containsKey(Column.DISPLAY_NAME)) {
SortColumn sortColumn = sortColumns.get(Column.DISPLAY_NAME);
outline.setColumnSorted(0, sortColumn.sortAscending(), sortColumn.sortRank());
}
List<Column> sheetProperties = customizer.getColumns();
for (int index = 0; index < sheetProperties.size(); ++index) {
Column property = sheetProperties.get(index);
String propertyName = property.getDisplayName();
outlineView.addPropertyColumn(propertyName, propertyName, propertyName);
if (sortColumns.containsKey(property)) {
SortColumn sortColumn = sortColumns.get(property);
outline.setColumnSorted(index + 1, sortColumn.sortAscending(), sortColumn.sortRank());
}
}
/*
* Hide the root node and configure the node selection mode.
*/
outline.setRootVisible(false);
outline.setSelectionMode(customizer.allowMultiSelect() ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION);
}
/**
* Adds a listener to changes in case selection in this browser.
*
* @param listener the ListSelectionListener to add
*/
void addListSelectionListener(ListSelectionListener listener) {
public void addListSelectionListener(ListSelectionListener listener) {
outline.getSelectionModel().addListSelectionListener(listener);
}
}
/**
* Refreshes the list of multi-user cases in this browser.
* Fetches and displays the list of multi-user cases known to the
* coordination service in this browser.
*/
@NbBundle.Messages({
"MultiUserCasesBrowserPanel.waitNode.message=Please Wait..."
})
void refreshCases() {
explorerManager.setRootContext(new MultiUserCasesRootNode());
public void displayCases() {
explorerManager.setRootContext(new MultiUserCasesRootNode(customizer));
}
/**
@ -134,7 +149,7 @@ final class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements Exp
caseTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
caseTableScrollPane.setMinimumSize(new java.awt.Dimension(0, 5));
caseTableScrollPane.setOpaque(false);
caseTableScrollPane.setPreferredSize(new java.awt.Dimension(5, 5));
caseTableScrollPane.setPreferredSize(new java.awt.Dimension(500, 500));
add(caseTableScrollPane, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables

View File

@ -16,15 +16,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
package org.sleuthkit.autopsy.casemodule.multiusercasesbrowser;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercases.MultiUserCaseNodeDataCollector;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -42,8 +43,8 @@ final class MultiUserCasesRootNode extends AbstractNode {
* @param case A list of coordination service node data objects representing
* multi-user cases.
*/
MultiUserCasesRootNode() {
super(Children.create(new MultiUserCasesRootNodeChildren(), true));
MultiUserCasesRootNode(MultiUserCaseBrowserCustomizer childNodeCustomizer) {
super(Children.create(new MultiUserCasesRootNodeChildren(childNodeCustomizer), true));
}
/**
@ -53,6 +54,12 @@ final class MultiUserCasesRootNode extends AbstractNode {
*/
private static class MultiUserCasesRootNodeChildren extends ChildFactory<CaseNodeData> {
private MultiUserCaseBrowserCustomizer nodeCustomizer;
MultiUserCasesRootNodeChildren(MultiUserCaseBrowserCustomizer nodeCustomizer) {
this.nodeCustomizer = nodeCustomizer;
}
@Override
protected boolean createKeys(List<CaseNodeData> keys) {
try {
@ -66,7 +73,7 @@ final class MultiUserCasesRootNode extends AbstractNode {
@Override
protected Node createNodeForKey(CaseNodeData key) {
return new MultiUserCaseNode(key);
return new MultiUserCaseNode(key, this.nodeCustomizer);
}
}

View File

@ -4,4 +4,4 @@ OpenIDE-Module: org.sleuthkit.autopsy.experimental
OpenIDE-Module-Layer: org/sleuthkit/autopsy/experimental/autoingest/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties
OpenIDE-Module-Requires: org.openide.windows.WindowManager
OpenIDE-Module-Specification-Version: 1.0
OpenIDE-Module-Specification-Version: 1.0

View File

@ -0,0 +1,90 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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
* 9
* 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.experimental.autoingest;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercasesbrowser.MultiUserCaseBrowserCustomizer;
/**
* A customizer for a multi-user case browser panel that presents a tabular view
* of the multi-user cases known to the coordination service in the
* administrative dashboard for auto ingest cases.
*/
final class AutoIngestCasesDashboardNodeCustomizer implements MultiUserCaseBrowserCustomizer {
private final DeleteCaseInputDirectoriesAction deleteCaseInputAction;
private final DeleteCasesForReprocessingAction deleteCaseOutputAction;
private final DeleteCasesAction deleteCaseAction;
/**
* Constructs a customizer for a multi-user case browser panel that presents
* a tabular view of the multi-user cases known to the coordination service
* in the administrative dashboard for auto ingest cases.
*/
AutoIngestCasesDashboardNodeCustomizer() {
/*
* These actions are shared by all nodes in order to support multiple
* selection.
*/
deleteCaseInputAction = new DeleteCaseInputDirectoriesAction();
deleteCaseOutputAction = new DeleteCasesForReprocessingAction();
deleteCaseAction = new DeleteCasesAction();
}
@Override
public List<Column> getColumns() {
List<Column> properties = new ArrayList<>();
properties.add(Column.CREATE_DATE);
properties.add(Column.DIRECTORY);
return properties;
}
@Override
public List<SortColumn> getSortColumns() {
List<SortColumn> sortColumns = new ArrayList<>();
sortColumns.add(new SortColumn(Column.DISPLAY_NAME, true, 1));
return sortColumns;
}
@Override
public boolean allowMultiSelect() {
return true;
}
@Override
public List<Action> getActions(CaseNodeData nodeData) {
List<Action> actions = new ArrayList<>();
actions.add(new OpenCaseAction(nodeData));
actions.add(new OpenAutoIngestLogAction(nodeData));
actions.add(deleteCaseInputAction);
actions.add(deleteCaseOutputAction);
actions.add(deleteCaseAction);
actions.add(new ShowCaseDeletionStatusAction(nodeData));
return actions;
}
@Override
public Action getPreferredAction(CaseNodeData nodeData) {
return new OpenCaseAction(nodeData);
}
}

View File

@ -81,6 +81,7 @@ public final class AutoIngestDashboardTopComponent extends TopComponent {
if (AutoIngestDashboard.isAdminAutoIngestDashboard()) {
EventQueue.invokeLater(() -> {
AinStatusDashboardTopComponent.openTopComponent(dashboard.getMonitor());
MultiUserCasesDashboardTopComponent.openTopComponent();
});
}
tc.open();

View File

@ -63,7 +63,7 @@ import org.sleuthkit.autopsy.casemodule.Case.CaseType;
import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.casemodule.CaseDetails;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock;

View File

@ -262,3 +262,8 @@ AinStatusDashboard.refreshButton.text=&Refresh
AinStatusDashboard.clusterMetricsButton.text=Auto Ingest &Metrics
AinStatusDashboard.nodeStatusTableTitle.text=Auto Ingest Nodes
AinStatusDashboard.healthMonitorButton.text=Health Monitor
# To change this license header, choose License Headers in Project Properties.
# To change this template file, choose Tools | Templates
# and open the template in the editor.
MultiUserCasesDashboardTopComponent.refreshButton.text=Refresh

View File

@ -0,0 +1,69 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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.experimental.autoingest;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.AbstractAction;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
/**
* An action that deletes the auto ingest job input directories associated with
* one or more multi-user cases. The coordination service nodes for the auto
* ingest jobs are not deleted. This supports the use case where the directories
* may need to be directed to reclaim space, but the option to restore the
* directories without having the jobs be reprocessed is retained.
*
* This cases to delete are discovered by querying the actions global context
* lookup for CaseNodeData objects. See
* https://platform.netbeans.org/tutorials/nbm-selection-1.html and
* https://platform.netbeans.org/tutorials/nbm-selection-2.html for details.
*/
final class DeleteCaseInputDirectoriesAction extends AbstractAction {
private static final long serialVersionUID = 1L;
@NbBundle.Messages({
"DeleteCaseInputDirectoriesAction.menuItemText=Delete Input Directories"
})
DeleteCaseInputDirectoriesAction() {
super(Bundle.DeleteCaseInputDirectoriesAction_menuItemText());
setEnabled(false); // RJCTODO: Enable when implemented
}
@Override
public void actionPerformed(ActionEvent e) {
final Collection<CaseNodeData> selectedNodeData = new ArrayList<>(Utilities.actionsGlobalContext().lookupAll(CaseNodeData.class));
if (!selectedNodeData.isEmpty()) {
/*
* RJCTODO: Create a background task that does the deletion and
* displays results in a dialog with a scrolling text pane.
*/
}
}
@Override
public DeleteCaseInputDirectoriesAction clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}

View File

@ -0,0 +1,66 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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.experimental.autoingest;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.AbstractAction;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
/**
* An action that completely deletes one or more multi-user cases, including any
* associated auto ingest job input directories and coordination service nodes.
*
* This cases to delete are discovered by querying the actions global context
* lookup for CaseNodeData objects. See
* https://platform.netbeans.org/tutorials/nbm-selection-1.html and
* https://platform.netbeans.org/tutorials/nbm-selection-2.html for details.
*/
final class DeleteCasesAction extends AbstractAction {
private static final long serialVersionUID = 1L;
@NbBundle.Messages({
"DeleteCasesAction.menuItemText=Delete Case and Jobs"
})
DeleteCasesAction() {
super(Bundle.DeleteCasesAction_menuItemText());
setEnabled(false); // RJCTODO: Enable when implemented
}
@Override
public void actionPerformed(ActionEvent e) {
final Collection<CaseNodeData> selectedNodeData = new ArrayList<>(Utilities.actionsGlobalContext().lookupAll(CaseNodeData.class));
if (!selectedNodeData.isEmpty()) {
/*
* RJCTODO: Create a background task that does the deletion and
* displays results in a dialog with a scrolling text pane.
*/
}
}
@Override
public DeleteCasesAction clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}

View File

@ -0,0 +1,69 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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.experimental.autoingest;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.AbstractAction;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
/**
* An action that deletes everything except the auto ingest job input
* directories for one or more multi-user cases. This supports the use case
* where a case needs to be reprocessed, so the input directories are not
* deleted even though the coordination service nodes for the auto ingest jobs
* are deleted.
*
* This cases to delete are discovered by querying the actions global context
* lookup for CaseNodeData objects. See
* https://platform.netbeans.org/tutorials/nbm-selection-1.html and
* https://platform.netbeans.org/tutorials/nbm-selection-2.html for details.
*/
final class DeleteCasesForReprocessingAction extends AbstractAction {
private static final long serialVersionUID = 1L;
@NbBundle.Messages({
"DeleteCasesForReprocessingAction.menuItemText=Delete for Reprocessing"
})
DeleteCasesForReprocessingAction() {
super(Bundle.DeleteCasesForReprocessingAction_menuItemText());
setEnabled(false); // RJCTODO: Enable when implemented
}
@Override
public void actionPerformed(ActionEvent e) {
final Collection<CaseNodeData> selectedNodeData = new ArrayList<>(Utilities.actionsGlobalContext().lookupAll(CaseNodeData.class));
if (!selectedNodeData.isEmpty()) {
/*
* RJCTODO: Create a background task that does the deletion and
* displays results in a dialog with a scrolling text pane.
*/
}
}
@Override
public DeleteCasesForReprocessingAction clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="refreshButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="458" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="caseBrowserScrollPane" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="caseBrowserScrollPane" pref="246" max="32767" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="refreshButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="refreshButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="MultiUserCasesDashboardTopComponent.refreshButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="caseBrowserScrollPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,156 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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.experimental.autoingest;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.Mode;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.multiusercasesbrowser.MultiUserCasesBrowserPanel;
/**
* A top component that provides an adminstrative dashboard for multi-user
* cases.
*/
@TopComponent.Description(
preferredID = "MultiUserCasesDashboardTopComponent",
persistenceType = TopComponent.PERSISTENCE_ALWAYS
)
@TopComponent.Registration(
mode = "dashboard",
openAtStartup = false
)
@TopComponent.OpenActionRegistration(
displayName = "#CTL_MultiUserCasesDashboardAction",
preferredID = "MultiUserCasesDashboardTopComponent"
)
@Messages({
"CTL_MultiUserCasesDashboardAction=Multi-User Cases Dashboard",
"CTL_MultiUserCasesDashboardTopComponent=Cases",
"HINT_MultiUserCasesDashboardTopComponent=This is an adminstrative dashboard for multi-user cases"
})
@SuppressWarnings("PMD.SingularField") // Prevent these warnings about generated code
public final class MultiUserCasesDashboardTopComponent extends TopComponent implements ExplorerManager.Provider {
private static final long serialVersionUID = 1L;
private final ExplorerManager explorerManager;
private final MultiUserCasesBrowserPanel caseBrowserPanel;
/**
* Opens a singleton top component that provides an adminstrative dashboard
* for multi-user cases. The top component is docked into the "dashboard
* mode" defined by the auto ingest jobs top component.
*/
// RJCTODO: Consider moving all of the dashboard code into its own
// admindashboards or dashboards package.
public static void openTopComponent() {
MultiUserCasesDashboardTopComponent topComponent = (MultiUserCasesDashboardTopComponent) WindowManager.getDefault().findTopComponent("MultiUserCasesDashboardTopComponent");
if (topComponent != null) {
if (!topComponent.isOpened()) {
Mode mode = WindowManager.getDefault().findMode("dashboard"); // NON-NLS
if (mode != null) {
mode.dockInto(topComponent);
}
topComponent.open();
}
topComponent.toFront();
topComponent.requestActive();
}
}
public MultiUserCasesDashboardTopComponent() {
initComponents();
setName(Bundle.CTL_MultiUserCasesDashboardTopComponent());
setToolTipText(Bundle.HINT_MultiUserCasesDashboardTopComponent());
explorerManager = new ExplorerManager();
associateLookup(ExplorerUtils.createLookup(explorerManager, getActionMap()));
caseBrowserPanel = new MultiUserCasesBrowserPanel(explorerManager, new AutoIngestCasesDashboardNodeCustomizer());
caseBrowserScrollPane.add(caseBrowserPanel);
caseBrowserScrollPane.setViewportView(caseBrowserPanel);
}
@Override
public void componentOpened() {
//caseBrowserPanel.displayCases();
}
@Override
public void componentClosed() {
}
@Override
public ExplorerManager getExplorerManager() {
return explorerManager;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
refreshButton = new javax.swing.JButton();
caseBrowserScrollPane = new javax.swing.JScrollPane();
org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(MultiUserCasesDashboardTopComponent.class, "MultiUserCasesDashboardTopComponent.refreshButton.text")); // NOI18N
refreshButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
refreshButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(refreshButton)
.addGap(0, 458, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(caseBrowserScrollPane)
.addContainerGap())))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(caseBrowserScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 246, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(refreshButton)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed
caseBrowserPanel.displayCases();
}//GEN-LAST:event_refreshButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane caseBrowserScrollPane;
private javax.swing.JButton refreshButton;
// End of variables declaration//GEN-END:variables
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Desktop;
import java.awt.event.ActionEvent;
@ -26,7 +26,7 @@ import java.nio.file.Paths;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
@ -34,10 +34,10 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
* An action that opens a case auto ingest log given the coordination service
* node data for the case.
*/
final class OpenCaseAutoIngestLogAction extends AbstractAction {
public final class OpenAutoIngestLogAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(OpenCaseAutoIngestLogAction.class.getName());
private static final Logger logger = Logger.getLogger(OpenAutoIngestLogAction.class.getName());
private static final String CASE_AUTO_INGEST_LOG_FILE_NAME = "auto_ingest_log.txt";
private final Path caseAutoIngestLogFilePath;
@ -48,17 +48,17 @@ final class OpenCaseAutoIngestLogAction extends AbstractAction {
* @param caseNodeData The coordination service node data for the case.
*/
@NbBundle.Messages({
"OpenCaseAutoIngestLogAction.menuItemText=Open Auto Ingest Log File"
"OpenAutoIngestLogAction.menuItemText=Open Auto Ingest Log File"
})
OpenCaseAutoIngestLogAction(CaseNodeData caseNodeData) {
super(Bundle.OpenCaseAutoIngestLogAction_menuItemText());
public OpenAutoIngestLogAction(CaseNodeData caseNodeData) {
super(Bundle.OpenAutoIngestLogAction_menuItemText());
this.caseAutoIngestLogFilePath = Paths.get(caseNodeData.getDirectory().toString(), CASE_AUTO_INGEST_LOG_FILE_NAME);
this.setEnabled(caseAutoIngestLogFilePath.toFile().exists());
}
@NbBundle.Messages({
"OpenCaseAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted.",
"OpenCaseAutoIngestLogAction.logOpenFailedErrorMsg=Failed to open case auto ingest log. See application log for details."
"OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted.",
"OpenAutoIngestLogAction.logOpenFailedErrorMsg=Failed to open case auto ingest log. See application log for details."
})
@Override
public void actionPerformed(ActionEvent event) {
@ -66,16 +66,16 @@ final class OpenCaseAutoIngestLogAction extends AbstractAction {
if (caseAutoIngestLogFilePath.toFile().exists()) {
Desktop.getDesktop().edit(caseAutoIngestLogFilePath.toFile());
} else {
MessageNotifyUtil.Message.error(Bundle.OpenCaseAutoIngestLogAction_deletedLogErrorMsg());
MessageNotifyUtil.Message.error(Bundle.OpenAutoIngestLogAction_deletedLogErrorMsg());
}
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error opening case auto ingest log file at %s", caseAutoIngestLogFilePath), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.OpenCaseAutoIngestLogAction_logOpenFailedErrorMsg());
MessageNotifyUtil.Message.error(Bundle.OpenAutoIngestLogAction_logOpenFailedErrorMsg());
}
}
@Override
public OpenCaseAutoIngestLogAction clone() throws CloneNotSupportedException {
public OpenAutoIngestLogAction clone() throws CloneNotSupportedException {
super.clone();
throw new CloneNotSupportedException();
}

View File

@ -0,0 +1,102 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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.experimental.autoingest;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CaseActionCancelledException;
import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.casemodule.OpenMultiUserCaseDialog;
import org.sleuthkit.autopsy.casemodule.StartupWindowProvider;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
/**
* An action that opens a multi-user case known to the coordination service.
*/
final class OpenCaseAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(org.sleuthkit.autopsy.casemodule.OpenMultiUserCaseAction.class.getName());
private final CaseNodeData caseNodeData;
/**
* Constructs an action that opens a multi-user case known to the
* coordination service.
*
* @param caseNodeData The coordination service node data for the case.
*/
@NbBundle.Messages({
"OpenCaseAction.menuItemText=Open"
})
public OpenCaseAction(CaseNodeData caseNodeData) {
super(Bundle.OpenCaseAction_menuItemText());
this.caseNodeData = caseNodeData;
}
@NbBundle.Messages({
"# {0} - caseErrorMessage", "OpenCaseAction.errorMsg=Failed to open case: {0}"
})
@Override
public void actionPerformed(ActionEvent e) {
new Thread(() -> {
String caseMetadataFilePath = null;
File caseDirectory = caseNodeData.getDirectory().toFile();
File[] filesInDirectory = caseDirectory.listFiles();
if (filesInDirectory != null) {
for (File file : filesInDirectory) {
if (file.getName().toLowerCase().endsWith(CaseMetadata.getFileExtension()) && file.isFile()) {
caseMetadataFilePath = file.getPath();
}
}
}
if (caseMetadataFilePath != null) {
try {
Case.openAsCurrentCase(caseMetadataFilePath);
} catch (CaseActionException ex) {
if (null != ex.getCause() && !(ex.getCause() instanceof CaseActionCancelledException)) {
logger.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseMetadataFilePath), ex); //NON-NLS
}
SwingUtilities.invokeLater(() -> {
MessageNotifyUtil.Message.error(Bundle.OpenCaseAction_errorMsg(ex.getLocalizedMessage()));
StartupWindowProvider.getInstance().open();
OpenMultiUserCaseDialog.getInstance().setVisible(true);
});
}
} else {
SwingUtilities.invokeLater(() -> {
MessageNotifyUtil.Message.error(Bundle.OpenCaseAction_errorMsg("Could not locate case metadata file."));
});
}
}).start();
}
@Override
public OpenCaseAction clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}

View File

@ -0,0 +1,48 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
/**
* An action that shows a popup that enumerates the deletion status of the
* various parts of a multi-user case known to the coordination service.
*/
final class ShowCaseDeletionStatusAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private final CaseNodeData caseNodeData;
/**
* Constructs an action that shows a popup that enumerates the deletion
* status of the various parts of a multi-user case known to the
* coordination service.
*
* @param caseNodeData The coordination service node data for the case.
*/
@NbBundle.Messages({
"ShowCaseDeletionStatusAction.menuItemText=Show Deletion Status"
})
public ShowCaseDeletionStatusAction(CaseNodeData caseNodeData) {
super(Bundle.ShowCaseDeletionStatusAction_menuItemText());
this.caseNodeData = caseNodeData;
setEnabled(false); // RJCTODO: Enable when implemented
}
@Override
public void actionPerformed(ActionEvent e) {
// RJCTODO: Implement
}
@Override
public ShowCaseDeletionStatusAction clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}