mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
Merge pull request #3364 from wschaeferB/3408-MultiUserCaseExplorer-2.11.0
3408 multi user case explorer 2.11.0
This commit is contained in:
commit
5ad6c4b1ab
@ -223,17 +223,11 @@ ReviewModeCasePanel.MetadataFileHeaderText=Metadata File
|
||||
OpenMultiUserCasePanel.jLabel1.text=Recent Cases
|
||||
OpenMultiUserCasePanel.openButton.text=Open
|
||||
OpenMultiUserCasePanel.cancelButton.text=Cancel
|
||||
MultiUserCasesPanel.rbWeeks.text=Weeks
|
||||
MultiUserCasesPanel.rbDays.text=Days
|
||||
MultiUserCasesPanel.bnShowLog.toolTipText=Display case log file for selected case
|
||||
MultiUserCasesPanel.bnShowLog.text=&Show Auto Ingest Case Log
|
||||
MultiUserCasesPanel.rbAllCases.text=Everything
|
||||
MultiUserCasesPanel.bnRefresh.text=&Refresh
|
||||
MultiUserCasesPanel.bnOpen.text=&Open
|
||||
MultiUserCasesPanel.rbGroupLabel.text=Show cases created in the last 10:
|
||||
MultiUserCasesPanel.rbMonths.text=Months
|
||||
CueBannerPanel.newCaseLabel.text=New Case
|
||||
CueBannerPanel.openCaseButton.text=
|
||||
CueBannerPanel.openCaseLabel.text=Open Case
|
||||
MultiUserCasesPanel.bnOpenSingleUserCase.text=Open Single-User Case...
|
||||
CueBannerPanel.newCaseButton.text=
|
||||
MultiUserCasesPanel.searchLabel.text=Start typing to search by case name
|
||||
MultiUserCasesPanel.cancelButton.text=Cancel
|
||||
|
47
Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.form
Normal file
47
Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.form
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 5]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[5, 5]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<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"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="caseTableScrollPane">
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 5]"/>
|
||||
</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]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
296
Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.java
Normal file
296
Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.java
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2018 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.awt.Component;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
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.nodes.Node;
|
||||
import java.awt.EventQueue;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.EmptyNode;
|
||||
|
||||
/**
|
||||
* A Swing JPanel with a scroll pane child component. The scroll pane contain
|
||||
* the table of cases.
|
||||
*
|
||||
* Used to display a list of multi user cases and allow the user to open one of
|
||||
* them.
|
||||
*
|
||||
*/
|
||||
class CaseBrowser extends javax.swing.JPanel implements ExplorerManager.Provider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Outline outline;
|
||||
private ExplorerManager em;
|
||||
private final org.openide.explorer.view.OutlineView outlineView;
|
||||
private int originalPathColumnIndex = 0;
|
||||
private static final Logger LOGGER = Logger.getLogger(CaseBrowser.class.getName());
|
||||
private LoadCaseMapWorker tableWorker;
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new CaseBrowser
|
||||
*/
|
||||
CaseBrowser() {
|
||||
outlineView = new org.openide.explorer.view.OutlineView();
|
||||
initComponents();
|
||||
|
||||
outline = outlineView.getOutline();
|
||||
outlineView.setPropertyColumns(
|
||||
Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(),
|
||||
Bundle.CaseNode_column_status(), Bundle.CaseNode_column_status(),
|
||||
Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath());
|
||||
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.CaseNode_column_name());
|
||||
customize();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the the table of cases and its columns.
|
||||
*/
|
||||
private void customize() {
|
||||
outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
TableColumnModel columnModel = outline.getColumnModel();
|
||||
int dateColumnIndex = 0;
|
||||
for (int index = 0; index < columnModel.getColumnCount(); index++) {
|
||||
//get indexes for created date column and path column
|
||||
if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_metadataFilePath())) {
|
||||
originalPathColumnIndex = index;
|
||||
} else if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_createdTime())) {
|
||||
dateColumnIndex = index;
|
||||
}
|
||||
}
|
||||
//Hide path column by default will need to
|
||||
ETableColumn column = (ETableColumn) columnModel.getColumn(originalPathColumnIndex);
|
||||
((ETableColumnModel) columnModel).setColumnHidden(column, true);
|
||||
outline.setRootVisible(false);
|
||||
|
||||
//Sort on Created date column in descending order by default
|
||||
outline.setColumnSorted(dateColumnIndex, false, 1);
|
||||
if (null == em) {
|
||||
em = new ExplorerManager();
|
||||
}
|
||||
caseTableScrollPane.setViewportView(outlineView);
|
||||
this.setVisible(true);
|
||||
outline.setRowSelectionAllowed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener to changes in case selections in the table
|
||||
*
|
||||
* @param listener the ListSelectionListener to add
|
||||
*/
|
||||
void addListSelectionListener(ListSelectionListener listener) {
|
||||
outline.getSelectionModel().addListSelectionListener(listener);
|
||||
}
|
||||
|
||||
String getCasePath() {
|
||||
int[] selectedRows = outline.getSelectedRows();
|
||||
if (selectedRows.length == 1) {
|
||||
try {
|
||||
return ((Node.Property) outline.getModel().getValueAt(outline.convertRowIndexToModel(selectedRows[0]), originalPathColumnIndex)).getValue().toString();
|
||||
} catch (IllegalAccessException | InvocationTargetException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to get case path from table.", ex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a row could be and is selected.
|
||||
*
|
||||
* @return true if a row is selected, false if no row is selected
|
||||
*/
|
||||
boolean isRowSelected() {
|
||||
return outline.getRowSelectionAllowed() && outline.getSelectedRows().length > 0;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"CaseBrowser.caseListLoading.message=Please Wait..."})
|
||||
/**
|
||||
* Gets the list of cases known to the review mode cases manager and
|
||||
* refreshes the cases table.
|
||||
*/
|
||||
void refresh() {
|
||||
if (tableWorker == null || tableWorker.isDone()) {
|
||||
outline.setRowSelectionAllowed(false);
|
||||
//create a new TableWorker to and execute it in a background thread if one is not currently working
|
||||
//set the table to display text informing the user that the list is being retreived and disable case selection
|
||||
EmptyNode emptyNode = new EmptyNode(Bundle.CaseBrowser_caseListLoading_message());
|
||||
em.setRootContext(emptyNode);
|
||||
tableWorker = new LoadCaseMapWorker();
|
||||
tableWorker.execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
caseTableScrollPane = new javax.swing.JScrollPane();
|
||||
|
||||
setMinimumSize(new java.awt.Dimension(0, 5));
|
||||
setPreferredSize(new java.awt.Dimension(5, 5));
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
|
||||
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));
|
||||
add(caseTableScrollPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JScrollPane caseTableScrollPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* Swingworker to fetch the updated map of cases and their status in a
|
||||
* background thread
|
||||
*/
|
||||
private class LoadCaseMapWorker extends SwingWorker<Void, Void> {
|
||||
|
||||
private static final String ALERT_FILE_NAME = "autoingest.alert";
|
||||
private Map<CaseMetadata, Boolean> cases;
|
||||
|
||||
/**
|
||||
* Gets a list of the cases in the top level case folder
|
||||
*
|
||||
* @return List of cases.
|
||||
*
|
||||
* @throws CoordinationServiceException
|
||||
*/
|
||||
private Map<CaseMetadata, Boolean> getCases() throws CoordinationService.CoordinationServiceException {
|
||||
Map<CaseMetadata, Boolean> casesMap = new HashMap<>();
|
||||
List<String> nodeList = CoordinationService.getInstance().getNodeList(CoordinationService.CategoryNode.CASES);
|
||||
|
||||
for (String node : nodeList) {
|
||||
Path casePath = Paths.get(node);
|
||||
File caseFolder = casePath.toFile();
|
||||
if (caseFolder.exists()) {
|
||||
/*
|
||||
* Search for '*.aut' and 'autoingest.alert' files.
|
||||
*/
|
||||
File[] fileArray = caseFolder.listFiles();
|
||||
if (fileArray == null) {
|
||||
continue;
|
||||
}
|
||||
String autFilePath = null;
|
||||
boolean alertFileFound = false;
|
||||
for (File file : fileArray) {
|
||||
String name = file.getName().toLowerCase();
|
||||
if (autFilePath == null && name.endsWith(".aut")) {
|
||||
autFilePath = file.getAbsolutePath();
|
||||
if (!alertFileFound) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!alertFileFound && name.endsWith(ALERT_FILE_NAME)) {
|
||||
alertFileFound = true;
|
||||
}
|
||||
if (autFilePath != null && alertFileFound) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (autFilePath != null) {
|
||||
try {
|
||||
boolean hasAlertStatus = false;
|
||||
if (alertFileFound) {
|
||||
/*
|
||||
* When an alert file exists, ignore the node
|
||||
* data and use the ALERT status.
|
||||
*/
|
||||
hasAlertStatus = true;
|
||||
} else {
|
||||
byte[] rawData = CoordinationService.getInstance().getNodeData(CoordinationService.CategoryNode.CASES, node);
|
||||
if (rawData != null && rawData.length > 0) {
|
||||
/*
|
||||
* When node data exists, use the status
|
||||
* stored in the node data.
|
||||
*/
|
||||
CaseNodeData caseNodeData = new CaseNodeData(rawData);
|
||||
if (caseNodeData.getErrorsOccurred()) {
|
||||
hasAlertStatus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CaseMetadata caseMetadata = new CaseMetadata(Paths.get(autFilePath));
|
||||
casesMap.put(caseMetadata, hasAlertStatus);
|
||||
} catch (CaseMetadata.CaseMetadataException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Error reading case metadata file '%s'.", autFilePath), ex);
|
||||
} catch (InterruptedException | CaseNodeData.InvalidDataException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Error reading case node data for '%s'.", node), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return casesMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
|
||||
try {
|
||||
cases = getCases();
|
||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unexpected exception while refreshing the table.", ex); //NON-NLS
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
EventQueue.invokeLater(() -> {
|
||||
MultiUserNode caseListNode = new MultiUserNode(cases);
|
||||
em.setRootContext(caseListNode);
|
||||
outline.setRowSelectionAllowed(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,442 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2017 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.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Handles locating and opening multi-user cases.
|
||||
*/
|
||||
final class MultiUserCaseManager {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(MultiUserCaseManager.class.getName());
|
||||
private static final String ALERT_FILE_NAME = "autoingest.alert";
|
||||
private static MultiUserCaseManager instance;
|
||||
private CoordinationService coordinationService;
|
||||
|
||||
/**
|
||||
* Gets the multi-user case manager.
|
||||
*
|
||||
* @return The multi-user case manager singleton.
|
||||
*
|
||||
* @throws MultiUserCaseManagerException
|
||||
*/
|
||||
synchronized static MultiUserCaseManager getInstance() throws MultiUserCaseManager.MultiUserCaseManagerException {
|
||||
if (null == instance) {
|
||||
instance = new MultiUserCaseManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an object that handles locating and opening multi-user cases.
|
||||
*
|
||||
* @throws MultiUserCaseManagerException
|
||||
*/
|
||||
private MultiUserCaseManager() throws MultiUserCaseManagerException {
|
||||
try {
|
||||
coordinationService = CoordinationService.getInstance();
|
||||
} catch (CoordinationServiceException ex) {
|
||||
throw new MultiUserCaseManager.MultiUserCaseManagerException("Failed to get the coordination service.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the cases in the top level case folder
|
||||
*
|
||||
* @return List of cases.
|
||||
*
|
||||
* @throws CoordinationServiceException
|
||||
*/
|
||||
List<MultiUserCase> getCases() throws CoordinationServiceException {
|
||||
List<MultiUserCase> cases = new ArrayList<>();
|
||||
List<String> nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
|
||||
for (String node : nodeList) {
|
||||
Path casePath = Paths.get(node);
|
||||
File caseFolder = casePath.toFile();
|
||||
if (caseFolder.exists()) {
|
||||
/*
|
||||
* Search for '*.aut' and 'autoingest.alert' files.
|
||||
*/
|
||||
File[] fileArray = caseFolder.listFiles();
|
||||
if (fileArray == null) {
|
||||
continue;
|
||||
}
|
||||
String autFilePath = null;
|
||||
boolean alertFileFound = false;
|
||||
for (File file : fileArray) {
|
||||
String name = file.getName().toLowerCase();
|
||||
if (autFilePath == null && name.endsWith(".aut")) {
|
||||
autFilePath = file.getAbsolutePath();
|
||||
if (!alertFileFound) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!alertFileFound && name.endsWith(ALERT_FILE_NAME)) {
|
||||
alertFileFound = true;
|
||||
}
|
||||
if (autFilePath != null && alertFileFound) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (autFilePath != null) {
|
||||
try {
|
||||
CaseStatus caseStatus;
|
||||
if (alertFileFound) {
|
||||
/*
|
||||
* When an alert file exists, ignore the node data
|
||||
* and use the ALERT status.
|
||||
*/
|
||||
caseStatus = CaseStatus.ALERT;
|
||||
} else {
|
||||
byte[] rawData = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, node);
|
||||
if (rawData != null && rawData.length > 0) {
|
||||
/*
|
||||
* When node data exists, use the status stored
|
||||
* in the node data.
|
||||
*/
|
||||
CaseNodeData caseNodeData = new CaseNodeData(rawData);
|
||||
if (caseNodeData.getErrorsOccurred()) {
|
||||
caseStatus = CaseStatus.ALERT;
|
||||
} else {
|
||||
caseStatus = CaseStatus.OK;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* When no node data is available, use the 'OK'
|
||||
* status to avoid confusing the end-user.
|
||||
*/
|
||||
caseStatus = CaseStatus.OK;
|
||||
}
|
||||
}
|
||||
|
||||
CaseMetadata caseMetadata = new CaseMetadata(Paths.get(autFilePath));
|
||||
cases.add(new MultiUserCase(casePath, caseMetadata, caseStatus));
|
||||
} catch (CaseMetadata.CaseMetadataException | MultiUserCase.MultiUserCaseException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Error reading case metadata file '%s'.", autFilePath), ex);
|
||||
} catch (InterruptedException | CaseNodeData.InvalidDataException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Error reading case node data for '%s'.", node), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a multi-user case.
|
||||
*
|
||||
* @param caseMetadataFilePath Path to the case metadata file.
|
||||
*
|
||||
* @throws CaseActionException
|
||||
*/
|
||||
synchronized void openCase(Path caseMetadataFilePath) throws CaseActionException {
|
||||
/*
|
||||
* Open the case.
|
||||
*/
|
||||
Case.openAsCurrentCase(caseMetadataFilePath.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception type thrown when there is an error completing a multi-user case
|
||||
* manager operation.
|
||||
*/
|
||||
static final class MultiUserCaseManagerException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the exception type thrown when there is an
|
||||
* error completing a multi-user case manager operation.
|
||||
*
|
||||
* @param message The exception message.
|
||||
*/
|
||||
private MultiUserCaseManagerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the exception type thrown when there is an
|
||||
* error completing a multi-user case manager operation.
|
||||
*
|
||||
* @param message The exception message.
|
||||
* @param cause A Throwable cause for the error.
|
||||
*/
|
||||
private MultiUserCaseManagerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of a multi-user case.
|
||||
*/
|
||||
static class MultiUserCase implements Comparable<MultiUserCase> {
|
||||
|
||||
private final Path caseDirectoryPath;
|
||||
private final String caseDisplayName;
|
||||
private final String metadataFileName;
|
||||
private final Date createDate;
|
||||
private final Date lastAccessedDate;
|
||||
private CaseStatus status;
|
||||
|
||||
/**
|
||||
* Constructs a representation of a multi-user case
|
||||
*
|
||||
* @param caseDirectoryPath The case directory path.
|
||||
* @param caseMetadata The case metadata.
|
||||
*
|
||||
* @throws MultiUserCaseException If no case metadata (.aut) file is
|
||||
* found in the case directory.
|
||||
*/
|
||||
MultiUserCase(Path caseDirectoryPath, CaseMetadata caseMetadata, CaseStatus status) throws MultiUserCaseException {
|
||||
this.caseDirectoryPath = caseDirectoryPath;
|
||||
caseDisplayName = caseMetadata.getCaseDisplayName();
|
||||
metadataFileName = caseMetadata.getFilePath().getFileName().toString();
|
||||
this.status = status;
|
||||
BasicFileAttributes fileAttrs = null;
|
||||
try {
|
||||
fileAttrs = Files.readAttributes(Paths.get(caseDirectoryPath.toString(), metadataFileName), BasicFileAttributes.class);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Error reading file attributes of case metadata file in %s, will use current time for case createDate/lastModfiedDate", caseDirectoryPath), ex);
|
||||
}
|
||||
if (null != fileAttrs) {
|
||||
createDate = new Date(fileAttrs.creationTime().toMillis());
|
||||
lastAccessedDate = new Date(fileAttrs.lastAccessTime().toMillis());
|
||||
} else {
|
||||
createDate = new Date();
|
||||
lastAccessedDate = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the case directory path.
|
||||
*
|
||||
* @return The case directory path.
|
||||
*/
|
||||
Path getCaseDirectoryPath() {
|
||||
return this.caseDirectoryPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the case display name. This may differ from the name supplied to
|
||||
* the directory or metadata file names if a case has been renamed.
|
||||
*
|
||||
* @return The case display name.
|
||||
*/
|
||||
String getCaseDisplayName() {
|
||||
return this.caseDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the creation date for the case, defined as the create time of
|
||||
* the case metadata file.
|
||||
*
|
||||
* @return The case creation date.
|
||||
*/
|
||||
Date getCreationDate() {
|
||||
return this.createDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last accessed date for the case, defined as the last
|
||||
* accessed time of the case metadata file.
|
||||
*
|
||||
* @return The last accessed date.
|
||||
*/
|
||||
Date getLastAccessedDate() {
|
||||
return this.lastAccessedDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets metadata (.aut) file name.
|
||||
*
|
||||
* @return The metadata file name.
|
||||
*/
|
||||
String getMetadataFileName() {
|
||||
return this.metadataFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status of this case.
|
||||
*
|
||||
* @return See CaseStatus enum definition.
|
||||
*/
|
||||
CaseStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the case metadata from a case directory path.
|
||||
*
|
||||
* @param caseDirectoryPath The case directory path.
|
||||
*
|
||||
* @return Case metadata.
|
||||
*
|
||||
* @throws CaseMetadata.CaseMetadataException If the CaseMetadata object
|
||||
* cannot be constructed.
|
||||
* @throws MultiUserCaseException If no case metadata (.aut)
|
||||
* file is found in the case
|
||||
* directory.
|
||||
*/
|
||||
private CaseMetadata getCaseMetadataFromCaseDirectoryPath(Path caseDirectoryPath) throws CaseMetadata.CaseMetadataException, MultiUserCaseException {
|
||||
CaseMetadata caseMetadata = null;
|
||||
|
||||
File directory = new File(caseDirectoryPath.toString());
|
||||
if (directory.isDirectory()) {
|
||||
File autFile = null;
|
||||
|
||||
/*
|
||||
* Attempt to find an AUT file via a directory scan.
|
||||
*/
|
||||
for (File file : directory.listFiles()) {
|
||||
if (file.getName().toLowerCase().endsWith(CaseMetadata.getFileExtension()) && file.isFile()) {
|
||||
autFile = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (autFile == null || !autFile.isFile()) {
|
||||
throw new MultiUserCaseException(String.format("No case metadata (.aut) file found in the case directory '%s'.", caseDirectoryPath.toString()));
|
||||
}
|
||||
|
||||
caseMetadata = new CaseMetadata(Paths.get(autFile.getAbsolutePath()));
|
||||
}
|
||||
|
||||
return caseMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not some other object is "equal to" this
|
||||
* MultiUserCase object.
|
||||
*
|
||||
* @param other The other object.
|
||||
*
|
||||
* @return True or false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof MultiUserCase)) {
|
||||
return false;
|
||||
}
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
return this.caseDirectoryPath.toString().equals(((MultiUserCase) other).caseDirectoryPath.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code value for this MultiUserCase object.
|
||||
*
|
||||
* @return The has code.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 71 * hash + Objects.hashCode(this.caseDirectoryPath);
|
||||
hash = 71 * hash + Objects.hashCode(this.createDate);
|
||||
hash = 71 * hash + Objects.hashCode(this.caseDisplayName);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this MultiUserCase object with another MultiUserCase object
|
||||
* for order.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(MultiUserCase other) {
|
||||
return -this.lastAccessedDate.compareTo(other.getLastAccessedDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator for a descending order sort on date created.
|
||||
*/
|
||||
static class LastAccessedDateDescendingComparator implements Comparator<MultiUserCase> {
|
||||
|
||||
/**
|
||||
* Compares two MultiUserCase objects for order based on last
|
||||
* accessed date (descending).
|
||||
*
|
||||
* @param object The first MultiUserCase object
|
||||
* @param otherObject The second MultiUserCase object.
|
||||
*
|
||||
* @return A negative integer, zero, or a positive integer as the
|
||||
* first argument is less than, equal to, or greater than
|
||||
* the second.
|
||||
*/
|
||||
@Override
|
||||
public int compare(MultiUserCase object, MultiUserCase otherObject) {
|
||||
return -object.getLastAccessedDate().compareTo(otherObject.getLastAccessedDate());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when there is a problem creating a multi-user case.
|
||||
*/
|
||||
final class MultiUserCaseException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructs an exception to throw when there is a problem creating
|
||||
* a multi-user case.
|
||||
*
|
||||
* @param message The exception message.
|
||||
*/
|
||||
private MultiUserCaseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an exception to throw when there is a problem creating
|
||||
* a multi-user case.
|
||||
*
|
||||
* @param message The exception message.
|
||||
* @param cause The cause of the exception, if it was an
|
||||
* exception.
|
||||
*/
|
||||
private MultiUserCaseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static enum CaseStatus {
|
||||
OK,
|
||||
ALERT
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Component class="javax.swing.ButtonGroup" name="rbGroupHistoryLength">
|
||||
</Component>
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="name" type="java.lang.String" value="Completed Cases" noResource="true"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
@ -26,30 +22,19 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="caseExplorerScrollPane" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="4" max="-2" attributes="0"/>
|
||||
<Component id="searchLabel" min="-2" pref="555" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="175" max="32767" attributes="0"/>
|
||||
<Component id="bnOpen" min="-2" pref="80" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="bnOpenSingleUserCase" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="bnShowLog" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="rbGroupLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="rbDays" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="rbWeeks" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="rbMonths" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="panelFilter" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
|
||||
<Component id="bnRefresh" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="scrollPaneTable" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -59,26 +44,15 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
|
||||
<Component id="scrollPaneTable" min="-2" pref="450" max="-2" attributes="0"/>
|
||||
<Component id="caseExplorerScrollPane" min="-2" pref="450" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="bnOpen" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnOpenSingleUserCase" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnShowLog" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Group type="103" alignment="1" groupAlignment="3" attributes="0">
|
||||
<Component id="rbDays" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="rbWeeks" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="rbMonths" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="rbGroupLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="panelFilter" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="bnRefresh" alignment="1" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnOpen" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnOpenSingleUserCase" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="searchLabel" alignment="3" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -95,140 +69,6 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOpenActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="scrollPaneTable">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="casesTable">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="caseTableModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="rowHeight" type="int" value="20"/>
|
||||
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||
<JTableSelectionModel selectionMode="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="casesTableMouseClicked"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JButton" name="bnRefresh">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.bnRefresh.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnRefreshActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="panelFilter">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="rbAllCases" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="rbAllCases" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JRadioButton" name="rbAllCases">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="rbGroupHistoryLength"/>
|
||||
</Property>
|
||||
<Property name="selected" type="boolean" value="true"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.rbAllCases.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="rbAllCasesItemStateChanged"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JButton" name="bnShowLog">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.bnShowLog.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.bnShowLog.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnShowLogActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JRadioButton" name="rbDays">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="rbGroupHistoryLength"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.rbDays.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="name" type="java.lang.String" value="" noResource="true"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="rbDaysItemStateChanged"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JRadioButton" name="rbWeeks">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="rbGroupHistoryLength"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.rbWeeks.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="rbWeeksItemStateChanged"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JRadioButton" name="rbMonths">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="rbGroupHistoryLength"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.rbMonths.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="rbMonthsItemStateChanged"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="rbGroupLabel">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Tahoma" size="12" style="0"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.rbGroupLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="bnOpenSingleUserCase">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
@ -239,5 +79,26 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOpenSingleUserCaseActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="cancelButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="searchLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.searchLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="caseExplorerScrollPane">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -19,79 +19,29 @@
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Desktop;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SortOrder;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.MultiUserCaseManager.MultiUserCase;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.guiutils.LongDateCellRenderer;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.guiutils.GrayableCellRenderer;
|
||||
import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer;
|
||||
|
||||
/**
|
||||
* A panel that allows a user to open cases created by auto ingest.
|
||||
*/
|
||||
@NbBundle.Messages({"MultiUSerCasesPanel.caseListLoading.message=Retrieving list of cases, please wait..."})
|
||||
final class MultiUserCasesPanel extends javax.swing.JPanel {
|
||||
final class MultiUserCasesPanel extends JPanel{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOGGER = Logger.getLogger(MultiUserCasesPanel.class.getName());
|
||||
private static final String LOG_FILE_NAME = "auto_ingest_log.txt";
|
||||
private static final int CASE_COL_MIN_WIDTH = 30;
|
||||
private static final int CASE_COL_MAX_WIDTH = 2000;
|
||||
private static final int CASE_COL_PREFERRED_WIDTH = 300;
|
||||
private static final int TIME_COL_MIN_WIDTH = 40;
|
||||
private static final int TIME_COL_MAX_WIDTH = 250;
|
||||
private static final int TIME_COL_PREFERRED_WIDTH = 160;
|
||||
private static final int STATUS_COL_MIN_WIDTH = 55;
|
||||
private static final int STATUS_COL_MAX_WIDTH = 250;
|
||||
private static final int STATUS_COL_PREFERRED_WIDTH = 60;
|
||||
private static final String CASES_POPULATING_MESSAGE = NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUSerCasesPanel.caseListLoading.message");
|
||||
|
||||
/*
|
||||
* The JTable table model for the cases table presented by this view is
|
||||
* defined by the following string, enum, and array.
|
||||
*
|
||||
* TODO (RC): Consider unifying this stuff in an enum as in
|
||||
* AutoIngestDashboard to make it less error prone.
|
||||
*/
|
||||
private static final String CASE_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.CaseHeaderText");
|
||||
private static final String CREATEDTIME_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.CreatedTimeHeaderText");
|
||||
private static final String STATUS_ICON_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.StatusIconHeaderText");
|
||||
private static final String OUTPUT_FOLDER_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.OutputFolderHeaderText");
|
||||
private static final String METADATA_FILE_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.MetadataFileHeaderText");
|
||||
|
||||
enum COLUMN_HEADERS {
|
||||
|
||||
CASE,
|
||||
CREATEDTIME,
|
||||
STATUS_ICON,
|
||||
OUTPUTFOLDER,
|
||||
METADATA_FILE
|
||||
}
|
||||
private final String[] columnNames = {CASE_HEADER, CREATEDTIME_HEADER, STATUS_ICON_HEADER, OUTPUT_FOLDER_HEADER, METADATA_FILE_HEADER};
|
||||
private DefaultTableModel caseTableModel;
|
||||
private JDialog parentDialog;
|
||||
private LoadTableWorker tableWorker;
|
||||
private Path currentlySelectedCase;
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final JDialog parentDialog;
|
||||
private final CaseBrowser caseBrowserPanel;
|
||||
|
||||
/**
|
||||
* Constructs a panel that allows a user to open cases created by automated
|
||||
@ -99,66 +49,19 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
|
||||
*/
|
||||
MultiUserCasesPanel(JDialog parentDialog) {
|
||||
this.parentDialog = parentDialog;
|
||||
caseTableModel = new DefaultTableModel(columnNames, 0) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int row, int column) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int col) {
|
||||
if (this.getColumnName(col).equals(CREATEDTIME_HEADER)) {
|
||||
return Date.class;
|
||||
} else {
|
||||
return super.getColumnClass(col);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
initComponents();
|
||||
|
||||
/*
|
||||
* Configure the columns of the cases table.
|
||||
*/
|
||||
TableColumn theColumn;
|
||||
theColumn = casesTable.getColumn(CASE_HEADER);
|
||||
theColumn.setCellRenderer(new GrayableCellRenderer());
|
||||
theColumn.setMinWidth(CASE_COL_MIN_WIDTH);
|
||||
theColumn.setMaxWidth(CASE_COL_MAX_WIDTH);
|
||||
theColumn.setPreferredWidth(CASE_COL_PREFERRED_WIDTH);
|
||||
theColumn.setWidth(CASE_COL_PREFERRED_WIDTH);
|
||||
|
||||
theColumn = casesTable.getColumn(CREATEDTIME_HEADER);
|
||||
theColumn.setCellRenderer(new LongDateCellRenderer());
|
||||
theColumn.setMinWidth(TIME_COL_MIN_WIDTH);
|
||||
theColumn.setMaxWidth(TIME_COL_MAX_WIDTH);
|
||||
theColumn.setPreferredWidth(TIME_COL_PREFERRED_WIDTH);
|
||||
theColumn.setWidth(TIME_COL_PREFERRED_WIDTH);
|
||||
|
||||
theColumn = casesTable.getColumn(STATUS_ICON_HEADER);
|
||||
theColumn.setCellRenderer(new StatusIconCellRenderer());
|
||||
theColumn.setMinWidth(STATUS_COL_MIN_WIDTH);
|
||||
theColumn.setMaxWidth(STATUS_COL_MAX_WIDTH);
|
||||
theColumn.setPreferredWidth(STATUS_COL_PREFERRED_WIDTH);
|
||||
theColumn.setWidth(STATUS_COL_PREFERRED_WIDTH);
|
||||
|
||||
casesTable.removeColumn(casesTable.getColumn(OUTPUT_FOLDER_HEADER));
|
||||
casesTable.removeColumn(casesTable.getColumn(METADATA_FILE_HEADER));
|
||||
casesTable.setRowSorter(new RowSorter<>(caseTableModel));
|
||||
casesTable.getRowSorter().toggleSortOrder(casesTable.getColumn(CREATEDTIME_HEADER).getModelIndex());
|
||||
caseBrowserPanel = new CaseBrowser();
|
||||
caseExplorerScrollPane.add(caseBrowserPanel);
|
||||
caseExplorerScrollPane.setViewportView(caseBrowserPanel);
|
||||
/*
|
||||
* Listen for row selection changes and set button state for the current
|
||||
* selection.
|
||||
*/
|
||||
casesTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
|
||||
//Ignore extra messages.
|
||||
if (e.getValueIsAdjusting()) {
|
||||
return;
|
||||
}
|
||||
caseBrowserPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
||||
setButtons();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,87 +69,15 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
|
||||
* refreshes the cases table.
|
||||
*/
|
||||
void refresh() {
|
||||
if (tableWorker == null || tableWorker.isDone()) {
|
||||
//create a new TableWorker to and execute it in a background thread if one is not currently working
|
||||
currentlySelectedCase = getSelectedCase();
|
||||
//set the table to display text informing the user that the list is being retreived and disable case selection
|
||||
caseTableModel.setRowCount(0);
|
||||
casesTable.setRowSelectionAllowed(false);
|
||||
caseTableModel.addRow(new Object[]{CASES_POPULATING_MESSAGE, null, null, "", ""});
|
||||
tableWorker = new LoadTableWorker();
|
||||
tableWorker.execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current selection in the cases table.
|
||||
*
|
||||
* @return A path representing the current selected case, null if there is
|
||||
* no selection.
|
||||
*/
|
||||
private Path getSelectedCase() {
|
||||
try {
|
||||
int selectedRow = casesTable.getSelectedRow();
|
||||
if (selectedRow >= 0 && selectedRow < casesTable.getRowCount()) {
|
||||
return Paths.get(caseTableModel.getValueAt(casesTable.convertRowIndexToModel(selectedRow), COLUMN_HEADERS.CASE.ordinal()).toString());
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current selection in the cases table.
|
||||
*
|
||||
* @param path The case folder path of the case to select.
|
||||
*/
|
||||
private void setSelectedCase(Path path) {
|
||||
if (path != null) {
|
||||
try {
|
||||
for (int row = 0; row < casesTable.getRowCount(); ++row) {
|
||||
Path temp = Paths.get(caseTableModel.getValueAt(casesTable.convertRowIndexToModel(row), COLUMN_HEADERS.CASE.ordinal()).toString());
|
||||
if (temp.compareTo(path) == 0) { // found it
|
||||
casesTable.setRowSelectionInterval(row, row);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
casesTable.clearSelection();
|
||||
}
|
||||
}
|
||||
casesTable.clearSelection();
|
||||
caseBrowserPanel.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables the Open and Show Log buttons based on the case selected
|
||||
* in the cases table.
|
||||
*/
|
||||
private void setButtons() {
|
||||
boolean openEnabled = casesTable.getRowSelectionAllowed() && casesTable.getSelectedRow() >= 0 && casesTable.getSelectedRow() < casesTable.getRowCount();
|
||||
bnOpen.setEnabled(openEnabled);
|
||||
|
||||
Path pathToLog = getSelectedCaseLogFilePath();
|
||||
boolean showLogEnabled = openEnabled && pathToLog != null && pathToLog.toFile().exists();
|
||||
bnShowLog.setEnabled(showLogEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the log file path for the selected case in the cases table.
|
||||
*
|
||||
* @return The case log path.
|
||||
*/
|
||||
private Path getSelectedCaseLogFilePath() {
|
||||
Path retValue = null;
|
||||
|
||||
int selectedRow = casesTable.getSelectedRow();
|
||||
int rowCount = casesTable.getRowCount();
|
||||
if (selectedRow >= 0 && selectedRow < rowCount) {
|
||||
String caseDirectory = (String) caseTableModel.getValueAt(casesTable.convertRowIndexToModel(selectedRow), COLUMN_HEADERS.OUTPUTFOLDER.ordinal());
|
||||
retValue = Paths.get(caseDirectory, LOG_FILE_NAME);
|
||||
}
|
||||
|
||||
return retValue;
|
||||
void setButtons() {
|
||||
bnOpen.setEnabled(caseBrowserPanel.isRowSelected());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -254,56 +85,34 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
|
||||
*
|
||||
* @param caseMetadataFilePath The path to the case metadata file.
|
||||
*/
|
||||
private void openCase(Path caseMetadataFilePath) {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
private void openCase(String caseMetadataFilePath) {
|
||||
if (caseMetadataFilePath != null) {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
StartupWindowProvider.getInstance().close();
|
||||
if (parentDialog != null) {
|
||||
parentDialog.setVisible(false);
|
||||
}
|
||||
new Thread(() -> {
|
||||
try {
|
||||
MultiUserCaseManager.getInstance().openCase(caseMetadataFilePath);
|
||||
} catch (CaseActionException | MultiUserCaseManager.MultiUserCaseManagerException 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
|
||||
MessageNotifyUtil.Message.error(ex.getCause().getLocalizedMessage());
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
//GUI changes done back on the EDT
|
||||
StartupWindowProvider.getInstance().open();
|
||||
});
|
||||
} finally {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
//GUI changes done back on the EDT
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
});
|
||||
StartupWindowProvider.getInstance().close();
|
||||
if (parentDialog != null) {
|
||||
parentDialog.setVisible(false);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not a time satisfies a time filter defined by this
|
||||
* panel's time filter radio buttons.
|
||||
*
|
||||
* @param currentTime The current date and time in milliseconds from the
|
||||
* Unix epoch.
|
||||
* @param inputTime The date and time to be tested as milliseconds from
|
||||
* the Unix epoch.
|
||||
*/
|
||||
private boolean passesTimeFilter(long currentTime, long inputTime) {
|
||||
long numberOfUnits = 10;
|
||||
long multiplier = 1;
|
||||
if (rbAllCases.isSelected()) {
|
||||
return true;
|
||||
} else if (rbMonths.isSelected()) {
|
||||
multiplier = 31;
|
||||
} else if (rbWeeks.isSelected()) {
|
||||
multiplier = 7;
|
||||
} else if (rbDays.isSelected()) {
|
||||
multiplier = 1;
|
||||
new Thread(() -> {
|
||||
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
|
||||
MessageNotifyUtil.Message.error(ex.getCause().getLocalizedMessage());
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
//GUI changes done back on the EDT
|
||||
StartupWindowProvider.getInstance().open();
|
||||
});
|
||||
} finally {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
//GUI changes done back on the EDT
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
return ((currentTime - inputTime) / (1000 * 60 * 60 * 24)) < (numberOfUnits * multiplier);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -345,19 +154,11 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
rbGroupHistoryLength = new javax.swing.ButtonGroup();
|
||||
bnOpen = new javax.swing.JButton();
|
||||
scrollPaneTable = new javax.swing.JScrollPane();
|
||||
casesTable = new javax.swing.JTable();
|
||||
bnRefresh = new javax.swing.JButton();
|
||||
panelFilter = new javax.swing.JPanel();
|
||||
rbAllCases = new javax.swing.JRadioButton();
|
||||
bnShowLog = new javax.swing.JButton();
|
||||
rbDays = new javax.swing.JRadioButton();
|
||||
rbWeeks = new javax.swing.JRadioButton();
|
||||
rbMonths = new javax.swing.JRadioButton();
|
||||
rbGroupLabel = new javax.swing.JLabel();
|
||||
bnOpenSingleUserCase = new javax.swing.JButton();
|
||||
cancelButton = new javax.swing.JButton();
|
||||
searchLabel = new javax.swing.JLabel();
|
||||
caseExplorerScrollPane = new javax.swing.JScrollPane();
|
||||
|
||||
setName("Completed Cases"); // NOI18N
|
||||
setPreferredSize(new java.awt.Dimension(960, 485));
|
||||
@ -370,84 +171,6 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
|
||||
}
|
||||
});
|
||||
|
||||
casesTable.setModel(caseTableModel);
|
||||
casesTable.setRowHeight(20);
|
||||
casesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
casesTable.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||
casesTableMouseClicked(evt);
|
||||
}
|
||||
});
|
||||
scrollPaneTable.setViewportView(casesTable);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnRefresh, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnRefresh.text")); // NOI18N
|
||||
bnRefresh.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
bnRefreshActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
rbGroupHistoryLength.add(rbAllCases);
|
||||
rbAllCases.setSelected(true);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(rbAllCases, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbAllCases.text")); // NOI18N
|
||||
rbAllCases.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
rbAllCasesItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout panelFilterLayout = new javax.swing.GroupLayout(panelFilter);
|
||||
panelFilter.setLayout(panelFilterLayout);
|
||||
panelFilterLayout.setHorizontalGroup(
|
||||
panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(panelFilterLayout.createSequentialGroup()
|
||||
.addComponent(rbAllCases)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
);
|
||||
panelFilterLayout.setVerticalGroup(
|
||||
panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelFilterLayout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(rbAllCases))
|
||||
);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnShowLog, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnShowLog.text")); // NOI18N
|
||||
bnShowLog.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnShowLog.toolTipText")); // NOI18N
|
||||
bnShowLog.setEnabled(false);
|
||||
bnShowLog.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
bnShowLogActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
rbGroupHistoryLength.add(rbDays);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(rbDays, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbDays.text")); // NOI18N
|
||||
rbDays.setName(""); // NOI18N
|
||||
rbDays.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
rbDaysItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
rbGroupHistoryLength.add(rbWeeks);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(rbWeeks, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbWeeks.text")); // NOI18N
|
||||
rbWeeks.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
rbWeeksItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
rbGroupHistoryLength.add(rbMonths);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(rbMonths, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbMonths.text")); // NOI18N
|
||||
rbMonths.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
rbMonthsItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
rbGroupLabel.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(rbGroupLabel, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbGroupLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnOpenSingleUserCase, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnOpenSingleUserCase.text")); // NOI18N
|
||||
bnOpenSingleUserCase.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
@ -455,55 +178,45 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.cancelButton.text")); // NOI18N
|
||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cancelButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(searchLabel, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.searchLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(caseExplorerScrollPane)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(4, 4, 4)
|
||||
.addComponent(searchLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 555, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 175, Short.MAX_VALUE)
|
||||
.addComponent(bnOpen, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(bnOpenSingleUserCase)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(bnShowLog)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 13, Short.MAX_VALUE)
|
||||
.addComponent(rbGroupLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(rbDays)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(rbWeeks)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(rbMonths)
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(14, 14, 14)
|
||||
.addComponent(bnRefresh))
|
||||
.addComponent(scrollPaneTable))
|
||||
.addComponent(cancelButton)))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(6, 6, 6)
|
||||
.addComponent(scrollPaneTable, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(caseExplorerScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(bnOpen)
|
||||
.addComponent(bnOpenSingleUserCase)
|
||||
.addComponent(bnShowLog))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(rbDays)
|
||||
.addComponent(rbWeeks)
|
||||
.addComponent(rbMonths)
|
||||
.addComponent(rbGroupLabel))
|
||||
.addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(bnRefresh, javax.swing.GroupLayout.Alignment.TRAILING))
|
||||
.addGap(0, 0, 0))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(cancelButton)
|
||||
.addComponent(bnOpen)
|
||||
.addComponent(bnOpenSingleUserCase)
|
||||
.addComponent(searchLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@ -513,129 +226,24 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
|
||||
* @param evt -- The event that caused this to be called
|
||||
*/
|
||||
private void bnOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenActionPerformed
|
||||
int modelRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow());
|
||||
String caseDirectory = (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal());
|
||||
Path caseMetadataFilePath = Paths.get(caseDirectory, (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.METADATA_FILE.ordinal()));
|
||||
openCase(caseMetadataFilePath);
|
||||
openCase(caseBrowserPanel.getCasePath());
|
||||
}//GEN-LAST:event_bnOpenActionPerformed
|
||||
|
||||
/**
|
||||
* Refresh button action
|
||||
*
|
||||
* @param evt -- The event that caused this to be called
|
||||
*/
|
||||
private void bnRefreshActionPerformed(java.awt.event.ActionEvent evt) {
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void rbDaysItemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
if (rbDays.isSelected()) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void rbAllCasesItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbAllCasesItemStateChanged
|
||||
if (rbAllCases.isSelected()) {
|
||||
refresh();
|
||||
}
|
||||
}//GEN-LAST:event_rbAllCasesItemStateChanged
|
||||
|
||||
private void rbMonthsItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbMonthsItemStateChanged
|
||||
if (rbMonths.isSelected()) {
|
||||
refresh();
|
||||
}
|
||||
}//GEN-LAST:event_rbMonthsItemStateChanged
|
||||
|
||||
private void rbWeeksItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbWeeksItemStateChanged
|
||||
if (rbWeeks.isSelected()) {
|
||||
refresh();
|
||||
}
|
||||
}//GEN-LAST:event_rbWeeksItemStateChanged
|
||||
|
||||
private void bnShowLogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnShowLogActionPerformed
|
||||
Path pathToLog = getSelectedCaseLogFilePath();
|
||||
if (pathToLog != null) {
|
||||
try {
|
||||
if (pathToLog.toFile().exists()) {
|
||||
Desktop.getDesktop().edit(pathToLog.toFile());
|
||||
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(this, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.cannotFindLog"),
|
||||
org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.unableToShowLogFile"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Error attempting to open case auto ingest log file %s", pathToLog), ex);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.cannotOpenLog"),
|
||||
org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.unableToShowLogFile"),
|
||||
JOptionPane.PLAIN_MESSAGE);
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_bnShowLogActionPerformed
|
||||
|
||||
private void casesTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_casesTableMouseClicked
|
||||
if (evt.getClickCount() == 2 && casesTable.getRowSelectionAllowed() && casesTable.getSelectedRow() >= 0 && casesTable.getSelectedRow() < casesTable.getRowCount()) {
|
||||
int modelRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow());
|
||||
String caseDirectory = (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal());
|
||||
Path caseMetadataFilePath = Paths.get(caseDirectory, (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.METADATA_FILE.ordinal()));
|
||||
openCase(caseMetadataFilePath);
|
||||
}
|
||||
}//GEN-LAST:event_casesTableMouseClicked
|
||||
|
||||
private void bnOpenSingleUserCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenSingleUserCaseActionPerformed
|
||||
Lookup.getDefault().lookup(CaseOpenAction.class).openCaseSelectionWindow();
|
||||
}//GEN-LAST:event_bnOpenSingleUserCaseActionPerformed
|
||||
|
||||
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||
if (parentDialog != null) {
|
||||
parentDialog.setVisible(false);
|
||||
}
|
||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton bnOpen;
|
||||
private javax.swing.JButton bnOpenSingleUserCase;
|
||||
private javax.swing.JButton bnRefresh;
|
||||
private javax.swing.JButton bnShowLog;
|
||||
private javax.swing.JTable casesTable;
|
||||
private javax.swing.JPanel panelFilter;
|
||||
private javax.swing.JRadioButton rbAllCases;
|
||||
private javax.swing.JRadioButton rbDays;
|
||||
private javax.swing.ButtonGroup rbGroupHistoryLength;
|
||||
private javax.swing.JLabel rbGroupLabel;
|
||||
private javax.swing.JRadioButton rbMonths;
|
||||
private javax.swing.JRadioButton rbWeeks;
|
||||
private javax.swing.JScrollPane scrollPaneTable;
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JScrollPane caseExplorerScrollPane;
|
||||
private javax.swing.JLabel searchLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
private class LoadTableWorker extends SwingWorker<Void, Void> {
|
||||
|
||||
private List<MultiUserCase> cases;
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
|
||||
try {
|
||||
MultiUserCaseManager manager = MultiUserCaseManager.getInstance();
|
||||
cases = manager.getCases();
|
||||
} catch (MultiUserCaseManager.MultiUserCaseManagerException | CoordinationService.CoordinationServiceException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unexpected exception while refreshing the table.", ex); //NON-NLS
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
caseTableModel.setRowCount(0);
|
||||
long now = new Date().getTime();
|
||||
for (MultiUserCase autoIngestCase : cases) {
|
||||
if (autoIngestCase.getCreationDate() != null && passesTimeFilter(now, autoIngestCase.getCreationDate().getTime())) {
|
||||
caseTableModel.addRow(new Object[]{
|
||||
autoIngestCase.getCaseDisplayName(),
|
||||
autoIngestCase.getCreationDate(),
|
||||
(MultiUserCaseManager.CaseStatus.OK != autoIngestCase.getStatus()) ? StatusIconCellRenderer.Status.WARNING : StatusIconCellRenderer.Status.OK,
|
||||
autoIngestCase.getCaseDirectoryPath().toString(),
|
||||
autoIngestCase.getMetadataFileName()});
|
||||
}
|
||||
}
|
||||
//ensure the cases are able to be selected
|
||||
casesTable.setRowSelectionAllowed(true);
|
||||
setSelectedCase(currentlySelectedCase);
|
||||
setButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
241
Core/src/org/sleuthkit/autopsy/casemodule/MultiUserNode.java
Normal file
241
Core/src/org/sleuthkit/autopsy/casemodule/MultiUserNode.java
Normal file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2018 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.awt.Desktop;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
|
||||
/**
|
||||
* A root node containing child nodes of the multi user cases
|
||||
*/
|
||||
final class MultiUserNode extends AbstractNode {
|
||||
|
||||
@Messages({"CaseNode.column.name=Name",
|
||||
"CaseNode.column.createdTime=Created Time",
|
||||
"CaseNode.column.status=Status",
|
||||
"CaseNode.column.metadataFilePath=Path"})
|
||||
private static final Logger LOGGER = Logger.getLogger(MultiUserNode.class.getName());
|
||||
private static final String LOG_FILE_NAME = "auto_ingest_log.txt";
|
||||
|
||||
/**
|
||||
* Provides a root node with children which each represent a case.
|
||||
*
|
||||
* @param caseMap the map of cases and a boolean indicating if they have an
|
||||
* alert
|
||||
*/
|
||||
MultiUserNode(Map<CaseMetadata, Boolean> caseMap) {
|
||||
super(Children.create(new MultiUserNodeChildren(caseMap), true));
|
||||
}
|
||||
|
||||
static class MultiUserNodeChildren extends ChildFactory<Entry<CaseMetadata, Boolean>> {
|
||||
|
||||
private final Map<CaseMetadata, Boolean> caseMap;
|
||||
|
||||
MultiUserNodeChildren(Map<CaseMetadata, Boolean> caseMap) {
|
||||
this.caseMap = caseMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<Entry<CaseMetadata, Boolean>> list) {
|
||||
if (caseMap != null && caseMap.size() > 0) {
|
||||
list.addAll(caseMap.entrySet());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(Entry<CaseMetadata, Boolean> key) {
|
||||
return new MultiUserCaseNode(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A node which represents a single multi user case.
|
||||
*/
|
||||
static final class MultiUserCaseNode extends AbstractNode {
|
||||
|
||||
private final String caseName;
|
||||
private final String caseCreatedDate;
|
||||
private final String caseMetadataFilePath;
|
||||
private final boolean caseHasAlert;
|
||||
private final Path caseLogFilePath;
|
||||
|
||||
MultiUserCaseNode(Entry<CaseMetadata, Boolean> multiUserCase) {
|
||||
super(Children.LEAF);
|
||||
caseName = multiUserCase.getKey().getCaseDisplayName();
|
||||
caseCreatedDate = multiUserCase.getKey().getCreatedDate();
|
||||
caseHasAlert = multiUserCase.getValue();
|
||||
super.setName(caseName);
|
||||
setName(caseName);
|
||||
setDisplayName(caseName);
|
||||
caseMetadataFilePath = multiUserCase.getKey().getFilePath().toString();
|
||||
caseLogFilePath = Paths.get(multiUserCase.getKey().getCaseDirectory(), LOG_FILE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns action to open the Case represented by this node
|
||||
* @return an action which will open the current case
|
||||
*/
|
||||
@Override
|
||||
public Action getPreferredAction() {
|
||||
return new OpenMultiUserCaseAction(caseMetadataFilePath);
|
||||
}
|
||||
|
||||
@Messages({"MultiUserNode.AlertColumn.text=Alert"}) //text to display when there is an alert present
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet s = super.createSheet();
|
||||
Sheet.Set ss = s.get(Sheet.PROPERTIES);
|
||||
if (ss == null) {
|
||||
ss = Sheet.createPropertiesSet();
|
||||
s.put(ss);
|
||||
}
|
||||
ss.put(new NodeProperty<>(Bundle.CaseNode_column_name(), Bundle.CaseNode_column_name(), Bundle.CaseNode_column_name(),
|
||||
caseName));
|
||||
ss.put(new NodeProperty<>(Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(),
|
||||
caseCreatedDate));
|
||||
ss.put(new NodeProperty<>(Bundle.CaseNode_column_status(), Bundle.CaseNode_column_status(), Bundle.CaseNode_column_status(),
|
||||
(caseHasAlert == true ? Bundle.MultiUserNode_AlertColumn_text() : "")));
|
||||
ss.put(new NodeProperty<>(Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath(),
|
||||
caseMetadataFilePath));
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action[] getActions(boolean context) {
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new OpenMultiUserCaseAction(caseMetadataFilePath)); //open case context menu option
|
||||
actions.add(new OpenCaseLogAction(caseLogFilePath));
|
||||
return actions.toArray(new Action[actions.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
@Messages({"MultiUserNode.OpenMultiUserCaseAction.text=Open Case"})
|
||||
/**
|
||||
* An action that opens the specified case and hides the multi user case
|
||||
* panel.
|
||||
*/
|
||||
private static final class OpenMultiUserCaseAction extends AbstractAction {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String caseMetadataFilePath;
|
||||
|
||||
OpenMultiUserCaseAction(String path) {
|
||||
super(Bundle.MultiUserNode_OpenMultiUserCaseAction_text());
|
||||
caseMetadataFilePath = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
StartupWindowProvider.getInstance().close();
|
||||
MultiUserCasesDialog.getInstance().setVisible(false);
|
||||
new Thread(
|
||||
() -> {
|
||||
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
|
||||
MessageNotifyUtil.Message.error(ex.getCause().getLocalizedMessage());
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
//GUI changes done back on the EDT
|
||||
StartupWindowProvider.getInstance().open();
|
||||
MultiUserCasesDialog.getInstance().setVisible(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone(); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
}
|
||||
|
||||
@Messages({"MultiUserNode.OpenCaseLogAction.text=Open Log File"})
|
||||
/**
|
||||
* An action that opens the specified case and hides the multi user case
|
||||
* panel.
|
||||
*/
|
||||
private static final class OpenCaseLogAction extends AbstractAction {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Path pathToLog;
|
||||
|
||||
OpenCaseLogAction(Path caseLogFilePath) {
|
||||
super(Bundle.MultiUserNode_OpenCaseLogAction_text());
|
||||
pathToLog = caseLogFilePath;
|
||||
this.setEnabled(caseLogFilePath != null && caseLogFilePath.toFile().exists());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
||||
if (pathToLog != null) {
|
||||
try {
|
||||
if (pathToLog.toFile().exists()) {
|
||||
Desktop.getDesktop().edit(pathToLog.toFile());
|
||||
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(MultiUserCasesDialog.getInstance(), org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.cannotFindLog"),
|
||||
org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.unableToShowLogFile"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Error attempting to open case auto ingest log file %s", pathToLog), ex);
|
||||
JOptionPane.showMessageDialog(MultiUserCasesDialog.getInstance(),
|
||||
org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.cannotOpenLog"),
|
||||
org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.unableToShowLogFile"),
|
||||
JOptionPane.PLAIN_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone(); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -41,7 +41,7 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
T visit(LocalFileNode dfn);
|
||||
|
||||
T visit(VirtualDirectoryNode ldn);
|
||||
|
||||
|
||||
T visit(LocalDirectoryNode ldn);
|
||||
|
||||
T visit(DirectoryNode dn);
|
||||
|
Loading…
x
Reference in New Issue
Block a user