diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml
index 25e17dd3eb..993af4bf6e 100755
--- a/Core/nbproject/project.xml
+++ b/Core/nbproject/project.xml
@@ -269,6 +269,7 @@
org.sleuthkit.autopsy.events
org.sleuthkit.autopsy.externalresults
org.sleuthkit.autopsy.filesearch
+ org.sleuthkit.autopsy.guiutils
org.sleuthkit.autopsy.ingest
org.sleuthkit.autopsy.keywordsearchservice
org.sleuthkit.autopsy.menuactions
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AutoIngestCasePanelInterface.java b/Core/src/org/sleuthkit/autopsy/casemodule/AutoIngestCasePanelInterface.java
deleted file mode 100755
index 795b636b35..0000000000
--- a/Core/src/org/sleuthkit/autopsy/casemodule/AutoIngestCasePanelInterface.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Autopsy Forensic Browser
- *
- * Copyright 2011-2017 Basis Technology Corp.
- * Contact: carrier sleuthkit org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.autopsy.casemodule;
-
-import javax.swing.JDialog;
-
-/**
- * Interface for startup window implementations
- */
-public interface AutoIngestCasePanelInterface {
-
- public void addWindowStateListener(JDialog parent);
-}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties
index 25b43a423a..60db8edaf8 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties
@@ -1,10 +1,10 @@
-CTL_AddImage=Add Data Source...
+CTL_AddImage=Add Data Source
CTL_AddImageButton=Add Data Source
CTL_CaseCloseAct=Close Case
-CTL_CaseNewAction=New Case...
-CTL_CasePropertiesAction=Case Properties...
+CTL_CaseNewAction=New Case
+CTL_CasePropertiesAction=Case Properties
CTL_CaseDeleteAction=Delete Case
-CTL_OpenAction=Open Case...
+CTL_CaseOpenAction=Open Case
Menu/Case/OpenRecentCase=Open Recent Case
CTL_CaseDeleteAction=Delete Case
OpenIDE-Module-Name=Case
@@ -210,11 +210,32 @@ CasePropertiesPanel.lbDbName.text=Database Name:
CasePropertiesPanel.lbDbType.text=Case Type:
CasePropertiesPanel.caseNumberLabel.text=Case Number:
LocalDiskPanel.changeDatabasePathCheckbox.text=Update case to use VHD file upon completion
-CueBannerPanel.openAutoIngestCaseButton.text=
+CueBannerPanel.openMultiUserCaseButton.text=
CueBannerPanel.openExistingCaseButton.text=
CueBannerPanel.openRecentCaseButton.text=
CueBannerPanel.createNewCaseButton.text=
CueBannerPanel.createNewCaseLabel.text=Create New Case
CueBannerPanel.openRecentCaseLabel.text=Open Recent Case
CueBannerPanel.openExistingCaseLabel.text=Open Existing Case
-CueBannerPanel.openAutoIngestCaseLabel.text=Open Auto Ingest Case
\ No newline at end of file
+CueBannerPanel.openMultiUserCaseLabel.text=Open Multi-User Case
+ReviewModeCasePanel.cannotOpenCase=Cannot Open Case
+ReviewModeCasePanel.casePathNotFound=Case path not found
+ReviewModeCasePanel.caseIsLocked=Single-user case is locked.
+ReviewModeCasePanel.CaseHeaderText=Case
+ReviewModeCasePanel.CreatedTimeHeaderText=Created Time
+ReviewModeCasePanel.StatusIconHeaderText=Status
+ReviewModeCasePanel.OutputFolderHeaderText=Output Folder
+ReviewModeCasePanel.LastAccessedTimeHeaderText=Last Accessed Time
+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 accessed in the last 10:
+MultiUserCasesPanel.rbMonths.text=Months
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties
index e212a125e9..de333e790f 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties
@@ -1,9 +1,9 @@
CTL_AddImageButton=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0
CTL_CaseCloseAct=\u30b1\u30fc\u30b9\u3092\u9589\u3058\u308b
-CTL_CaseNewAction=\u65b0\u898f\u30b1\u30fc\u30b9...
-CTL_CasePropertiesAction=\u30b1\u30fc\u30b9\u30d7\u30ed\u30d1\u30c6\u30a3...
+CTL_CaseNewAction=\u65b0\u898f\u30b1\u30fc\u30b9
+CTL_CasePropertiesAction=\u30b1\u30fc\u30b9\u30d7\u30ed\u30d1\u30c6\u30a3
CTL_CaseDeleteAction=\u30b1\u30fc\u30b9\u3092\u524a\u9664
-CTL_OpenAction=\u30b1\u30fc\u30b9\u3092\u958b\u304f...
+CTL_CaseOpenAction=\u30b1\u30fc\u30b9\u3092\u958b\u304f
Menu/Case/OpenRecentCase=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f
CTL_CaseDeleteAction=\u30b1\u30fc\u30b9\u3092\u524a\u9664
OpenIDE-Module-Name=\u30b1\u30fc\u30b9
@@ -194,3 +194,6 @@ CueBannerPanel.createNewCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5
CueBannerPanel.openRecentCaseLabel.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f
CueBannerPanel.openExistingCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f
CueBannerPanel.openAutoIngestCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f
+OpenMultiUserCasePanel.openButton.text=\u958b\u304f
+OpenMultiUserCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb
+OpenMultiUserCasePanel.jLabel1.text=\u6700\u8fd1\u958b\u3044\u305f\u30d5\u30a1\u30a4\u30eb
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java
index 3ef30f2594..66e983b1ce 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java
@@ -33,7 +33,6 @@ import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.CallableSystemAction;
-import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
@@ -45,13 +44,16 @@ import org.sleuthkit.autopsy.coreutils.Logger;
final class CaseDeleteAction extends CallableSystemAction {
private static final long serialVersionUID = 1L;
- private static final Logger logger = Logger.getLogger(CaseDeleteAction.class.getName());
+ private static final Logger LOGGER = Logger.getLogger(CaseDeleteAction.class.getName());
CaseDeleteAction() {
putValue(Action.NAME, NbBundle.getMessage(CaseDeleteAction.class, "CTL_CaseDeleteAction"));
this.setEnabled(false);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
- setEnabled(null != evt.getNewValue() && UserPreferences.getMode() != UserPreferences.SelectedMode.REVIEW);
+ /*
+ * A value of 'null' signifies that there is no case open.
+ */
+ setEnabled(null != evt.getNewValue());
});
}
@@ -93,7 +95,7 @@ final class CaseDeleteAction extends CallableSystemAction {
try {
get();
} catch (InterruptedException | ExecutionException ex) {
- logger.log(Level.SEVERE, String.format("Failed to delete case %s at %s", caseName, caseDirectory), ex);
+ LOGGER.log(Level.SEVERE, String.format("Failed to delete case %s at %s", caseName, caseDirectory), ex);
JOptionPane.showMessageDialog(
null,
Bundle.Case_deleteCaseFailureMessageBox_message(ex.getLocalizedMessage()),
@@ -108,7 +110,7 @@ final class CaseDeleteAction extends CallableSystemAction {
}.execute();
}
} catch (IllegalStateException ex) {
- logger.log(Level.SEVERE, "Case delete action called with no current case", ex);
+ LOGGER.log(Level.SEVERE, "Case delete action called with no current case", ex);
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java
index 533cab58c5..e4ed92d897 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java
@@ -141,7 +141,7 @@ public final class CaseOpenAction extends CallableSystemAction implements Action
@Override
public String getName() {
- return NbBundle.getMessage(CaseOpenAction.class, "CTL_OpenAction");
+ return NbBundle.getMessage(CaseOpenAction.class, "CTL_CaseOpenAction");
}
@Override
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenMultiUserAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenMultiUserAction.java
new file mode 100755
index 0000000000..7a12b69cf7
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenMultiUserAction.java
@@ -0,0 +1,91 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011-2017 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.casemodule;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.JDialog;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionRegistration;
+import org.openide.util.HelpCtx;
+import org.openide.util.NbBundle;
+import org.openide.util.actions.CallableSystemAction;
+import org.openide.windows.WindowManager;
+import org.sleuthkit.autopsy.core.UserPreferences;
+
+/**
+ * The action associated with the Open Multi-User Case menu item via the
+ * layer.xml file.
+ *
+ * This action should only be invoked in the event dispatch thread (EDT).
+ */
+@ActionID(category = "Case", id = "org.sleuthkit.autopsy.casemodule.CaseOpenMultiUserAction")
+@ActionReference(path = "Menu/Case", position = 102)
+@ActionRegistration(displayName = "#CTL_CaseOpenMultiUserAction", lazy = false)
+@NbBundle.Messages({"CTL_CaseOpenMultiUserAction=Open Multi-User Case"})
+public final class CaseOpenMultiUserAction extends CallableSystemAction implements ActionListener {
+
+ private static final long serialVersionUID = 1L;
+ private static JDialog multiUserCaseWindow;
+
+ private static final String DISPLAY_NAME = Bundle.CTL_CaseOpenMultiUserAction();
+
+ public CaseOpenMultiUserAction() {}
+
+ @Override
+ public boolean isEnabled() {
+ return UserPreferences.getIsMultiUserModeEnabled();
+ }
+
+ /**
+ * Pops up a case selection panel to allow the user to select a multi-user
+ * case to open.
+ *
+ * @param event The action event.
+ */
+ @Override
+ public void actionPerformed(ActionEvent event) {
+ if(multiUserCaseWindow == null) {
+ multiUserCaseWindow = MultiUserCasesDialog.getInstance();
+ }
+ multiUserCaseWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
+ multiUserCaseWindow.setVisible(true);
+ }
+
+ @Override
+ public void performAction() {
+ actionPerformed(null);
+ }
+
+ @Override
+ public String getName() {
+ return DISPLAY_NAME;
+ }
+
+ @Override
+ public HelpCtx getHelpCtx() {
+ return HelpCtx.DEFAULT_HELP;
+ }
+
+ @Override
+ public boolean asynchronous() {
+ return false; // run on edt
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form
index 75819e41d0..0d6525db8b 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form
@@ -28,14 +28,14 @@
-
+
-
+
@@ -66,9 +66,9 @@
-
+
-
+
@@ -216,13 +216,13 @@
-
+
-
+
@@ -237,18 +237,18 @@
-
+
-
+
-
+
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java
index 2d7b17458e..2c81d100e4 100755
--- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java
@@ -26,14 +26,11 @@ import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JDialog;
-import javax.swing.JPanel;
import javax.swing.KeyStroke;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
-import org.sleuthkit.autopsy.casemodule.AutoIngestCasePanelInterface;
import org.sleuthkit.autopsy.core.UserPreferences;
-import org.sleuthkit.autopsy.coreutils.NetworkUtils;
/*
* The panel in the default Autopsy startup window.
@@ -41,14 +38,11 @@ import org.sleuthkit.autopsy.coreutils.NetworkUtils;
public class CueBannerPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
- private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
- private static final String REVIEW_MODE_TITLE = "Cases" + " (" + LOCAL_HOST_NAME + ")";
/*
* This is field is static for the sake of the closeOpenRecentCasesWindow
* method.
*/
private static JDialog recentCasesWindow;
- private static JDialog autoIngestCasePanelWindow;
public static void closeOpenRecentCasesWindow() {
if (null != recentCasesWindow) {
@@ -56,15 +50,9 @@ public class CueBannerPanel extends javax.swing.JPanel {
}
}
- public static void closeAutoIngestCasesWindow() {
- if (null != autoIngestCasePanelWindow) {
- autoIngestCasePanelWindow.setVisible(false);
- }
- }
-
public CueBannerPanel() {
initComponents();
- customizeComponents();
+ initRecentCasesWindow();
enableComponents();
}
@@ -75,7 +63,7 @@ public class CueBannerPanel extends javax.swing.JPanel {
ImageIcon icon = new ImageIcon(cl.getResource(welcomeLogo));
autopsyLogo.setIcon(icon);
}
- customizeComponents();
+ initRecentCasesWindow();
enableComponents();
}
@@ -90,11 +78,6 @@ public class CueBannerPanel extends javax.swing.JPanel {
public void refresh() {
enableComponents();
}
-
- private void customizeComponents() {
- initRecentCasesWindow();
- initAutoIngestCasesWindow();
- }
private void initRecentCasesWindow() {
recentCasesWindow = new JDialog(
@@ -118,39 +101,15 @@ public class CueBannerPanel extends javax.swing.JPanel {
recentCasesWindow.pack();
recentCasesWindow.setResizable(false);
}
-
- private void initAutoIngestCasesWindow() {
- autoIngestCasePanelWindow = new JDialog(
- WindowManager.getDefault().getMainWindow(),
- REVIEW_MODE_TITLE,
- Dialog.ModalityType.APPLICATION_MODAL);
- autoIngestCasePanelWindow.getRootPane().registerKeyboardAction(
- e -> {
- autoIngestCasePanelWindow.setVisible(false);
- },
- KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
- OpenRecentCasePanel recentCasesPanel = OpenRecentCasePanel.getInstance();
- recentCasesPanel.setCloseButtonActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- autoIngestCasePanelWindow.setVisible(false);
- }
- });
- AutoIngestCasePanelInterface autoIngestCasePanel = Lookup.getDefault().lookup(AutoIngestCasePanelInterface.class);
- autoIngestCasePanel.addWindowStateListener(autoIngestCasePanelWindow);
- autoIngestCasePanelWindow.add((JPanel)autoIngestCasePanel);
- autoIngestCasePanelWindow.pack();
- autoIngestCasePanelWindow.setResizable(false);
- }
private void enableComponents() {
boolean enableOpenRecentCaseButton = (RecentCases.getInstance().getTotalRecentCases() > 0);
openRecentCaseButton.setEnabled(enableOpenRecentCaseButton);
openRecentCaseLabel.setEnabled(enableOpenRecentCaseButton);
- boolean showOpenAutoIngestCaseButton = (UserPreferences.getMode() == UserPreferences.SelectedMode.REVIEW);
- openAutoIngestCaseButton.setVisible(showOpenAutoIngestCaseButton);
- openAutoIngestCaseLabel.setVisible(showOpenAutoIngestCaseButton);
+ boolean enableOpenMultiUserCaseButton = UserPreferences.getIsMultiUserModeEnabled();
+ openMultiUserCaseButton.setEnabled(enableOpenMultiUserCaseButton);
+ openMultiUserCaseLabel.setEnabled(enableOpenMultiUserCaseButton);
}
/**
@@ -172,8 +131,8 @@ public class CueBannerPanel extends javax.swing.JPanel {
openExistingCaseLabel = new javax.swing.JLabel();
closeButton = new javax.swing.JButton();
jSeparator1 = new javax.swing.JSeparator();
- openAutoIngestCaseButton = new javax.swing.JButton();
- openAutoIngestCaseLabel = new javax.swing.JLabel();
+ openMultiUserCaseButton = new javax.swing.JButton();
+ openMultiUserCaseLabel = new javax.swing.JLabel();
autopsyLogo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/welcome_logo.png"))); // NOI18N
autopsyLogo.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.autopsyLogo.text")); // NOI18N
@@ -229,21 +188,21 @@ public class CueBannerPanel extends javax.swing.JPanel {
jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);
- openAutoIngestCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"))); // NOI18N
- openAutoIngestCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openAutoIngestCaseButton.text")); // NOI18N
- openAutoIngestCaseButton.setBorder(null);
- openAutoIngestCaseButton.setBorderPainted(false);
- openAutoIngestCaseButton.setContentAreaFilled(false);
- openAutoIngestCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1));
- openAutoIngestCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
- openAutoIngestCaseButton.addActionListener(new java.awt.event.ActionListener() {
+ openMultiUserCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"))); // NOI18N
+ openMultiUserCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openMultiUserCaseButton.text")); // NOI18N
+ openMultiUserCaseButton.setBorder(null);
+ openMultiUserCaseButton.setBorderPainted(false);
+ openMultiUserCaseButton.setContentAreaFilled(false);
+ openMultiUserCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1));
+ openMultiUserCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
+ openMultiUserCaseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
- openAutoIngestCaseButtonActionPerformed(evt);
+ openMultiUserCaseButtonActionPerformed(evt);
}
});
- openAutoIngestCaseLabel.setFont(openAutoIngestCaseLabel.getFont().deriveFont(openAutoIngestCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13));
- openAutoIngestCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openAutoIngestCaseLabel.text")); // NOI18N
+ openMultiUserCaseLabel.setFont(openMultiUserCaseLabel.getFont().deriveFont(openMultiUserCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13));
+ openMultiUserCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openMultiUserCaseLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
@@ -261,13 +220,13 @@ public class CueBannerPanel extends javax.swing.JPanel {
.addComponent(createNewCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(openRecentCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(openExistingCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(openAutoIngestCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(openMultiUserCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(createNewCaseLabel)
.addComponent(openRecentCaseLabel)
.addComponent(openExistingCaseLabel)
- .addComponent(openAutoIngestCaseLabel)))
+ .addComponent(openMultiUserCaseLabel)))
.addComponent(closeButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
);
@@ -290,9 +249,9 @@ public class CueBannerPanel extends javax.swing.JPanel {
.addComponent(openExistingCaseLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(openAutoIngestCaseButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(openMultiUserCaseButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
- .addComponent(openAutoIngestCaseLabel)
+ .addComponent(openMultiUserCaseLabel)
.addGap(20, 20, 20))))
.addComponent(jSeparator1)
.addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE))
@@ -316,10 +275,11 @@ public class CueBannerPanel extends javax.swing.JPanel {
recentCasesWindow.setVisible(true);
}//GEN-LAST:event_openRecentCaseButtonActionPerformed
- private void openAutoIngestCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openAutoIngestCaseButtonActionPerformed
- autoIngestCasePanelWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
- autoIngestCasePanelWindow.setVisible(true);
- }//GEN-LAST:event_openAutoIngestCaseButtonActionPerformed
+ private void openMultiUserCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openMultiUserCaseButtonActionPerformed
+ MultiUserCasesDialog multiUserCaseWindow = MultiUserCasesDialog.getInstance();
+ multiUserCaseWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
+ multiUserCaseWindow.setVisible(true);
+ }//GEN-LAST:event_openMultiUserCaseButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel autopsyLogo;
@@ -327,8 +287,8 @@ public class CueBannerPanel extends javax.swing.JPanel {
private javax.swing.JButton createNewCaseButton;
private javax.swing.JLabel createNewCaseLabel;
private javax.swing.JSeparator jSeparator1;
- private javax.swing.JButton openAutoIngestCaseButton;
- private javax.swing.JLabel openAutoIngestCaseLabel;
+ private javax.swing.JButton openMultiUserCaseButton;
+ private javax.swing.JLabel openMultiUserCaseLabel;
private javax.swing.JButton openExistingCaseButton;
private javax.swing.JLabel openExistingCaseLabel;
private javax.swing.JButton openRecentCaseButton;
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseManager.java
new file mode 100755
index 0000000000..d0baf94bc9
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseManager.java
@@ -0,0 +1,388 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011-2017 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.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.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 LOG_FILE_NAME = "auto_ingest_log.txt";
+ 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 getCases() throws CoordinationServiceException {
+ List cases = new ArrayList<>();
+ List nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
+ for (String node : nodeList) {
+ Path casePath = Paths.get(node);
+ File caseFolder = casePath.toFile();
+ if(caseFolder.exists()) {
+ File[] autFiles = caseFolder.listFiles((dir, name) -> name.toLowerCase().endsWith(".aut"));
+ if(autFiles != null && autFiles.length > 0) {
+ try {
+ CaseMetadata caseMetadata = new CaseMetadata(Paths.get(autFiles[0].getAbsolutePath()));
+ cases.add(new MultiUserCase(casePath, caseMetadata));
+ } catch (CaseMetadata.CaseMetadataException | MultiUserCase.MultiUserCaseException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Error reading case metadata file '%s'.", autFiles[0].getAbsolutePath()), 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 {
+
+ private final Path caseDirectoryPath;
+ private final String caseDisplayName;
+ private final String metadataFileName;
+ private final Date createDate;
+ private final Date lastAccessedDate;
+
+ /**
+ * 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) throws MultiUserCaseException {
+ this.caseDirectoryPath = caseDirectoryPath;
+ caseDisplayName = caseMetadata.getCaseDisplayName();
+ metadataFileName = caseMetadata.getFilePath().getFileName().toString();
+ 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 based on the auto ingest result file in the
+ * case directory.
+ *
+ * @return See CaseStatus enum definition.
+ */
+ CaseStatus getStatus() {
+ if(caseDirectoryPath.resolve("autoingest.alert").toFile().exists()) {
+ return CaseStatus.ALERT;
+ } else {
+ return CaseStatus.OK;
+ }
+ }
+
+ /**
+ * 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 {
+
+ /**
+ * 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
+ }
+
+}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesDialog.java
new file mode 100755
index 0000000000..8c90aeccb4
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesDialog.java
@@ -0,0 +1,89 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011-2017 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.casemodule;
+
+import java.awt.Dialog;
+import java.awt.event.KeyEvent;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.KeyStroke;
+import org.openide.windows.WindowManager;
+
+/**
+ * This class extends a JDialog and maintains the MultiUserCasesPanel.
+ */
+final class MultiUserCasesDialog extends JDialog {
+
+ private static final long serialVersionUID = 1L;
+ private static final String REVIEW_MODE_TITLE = "Open Multi-User Case";
+ private static MultiUserCasesPanel multiUserCasesPanel;
+ private static MultiUserCasesDialog instance;
+
+ /**
+ * Gets the instance of the MultiuserCasesDialog.
+ *
+ * @return The instance.
+ */
+ static public MultiUserCasesDialog getInstance() {
+ if(instance == null) {
+ instance = new MultiUserCasesDialog();
+ instance.init();
+ }
+ return instance;
+ }
+
+ /**
+ * Constructs a MultiUserCasesDialog object.
+ */
+ private MultiUserCasesDialog() {
+ super(WindowManager.getDefault().getMainWindow(),
+ REVIEW_MODE_TITLE,
+ Dialog.ModalityType.APPLICATION_MODAL);
+ }
+
+ /**
+ * Initializes the multi-user cases panel.
+ */
+ private void init() {
+ getRootPane().registerKeyboardAction(
+ e -> {
+ setVisible(false);
+ },
+ KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
+
+ multiUserCasesPanel = new MultiUserCasesPanel(this);
+ add(multiUserCasesPanel);
+ pack();
+ setResizable(false);
+ }
+
+ /**
+ * Set the dialog visibility. When setting it to visible, the contents will
+ * refresh.
+ *
+ * @param value True or false.
+ */
+ @Override
+ public void setVisible(boolean value) {
+ if(value) {
+ multiUserCasesPanel.refresh();
+ }
+ super.setVisible(value);
+ }
+}
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form
similarity index 84%
rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.form
rename to Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form
index cb0f275809..179bc23bb8 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.form
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form
@@ -45,7 +45,7 @@
-
+
@@ -82,7 +82,7 @@
-
+
@@ -118,7 +118,7 @@
-
+
@@ -153,7 +153,7 @@
-
+
@@ -165,10 +165,10 @@
-
+
-
+
@@ -182,7 +182,7 @@
-
+
@@ -196,7 +196,7 @@
-
+
@@ -209,7 +209,7 @@
-
+
@@ -222,7 +222,7 @@
-
+
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java
similarity index 64%
rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java
rename to Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java
index 2bcd335916..7e87ce8947 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCasePanel.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java
@@ -16,52 +16,38 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.sleuthkit.autopsy.experimental.autoingest;
+package org.sleuthkit.autopsy.casemodule;
import java.awt.Cursor;
import java.awt.Desktop;
-import java.awt.EventQueue;
-import java.awt.event.ActionEvent;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.SwingWorker;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
-import org.openide.util.NbBundle;
-import org.openide.util.lookup.ServiceProvider;
-import org.openide.windows.WindowManager;
-import org.sleuthkit.autopsy.casemodule.CaseActionCancelledException;
-import org.sleuthkit.autopsy.casemodule.CaseMetadata;
-import org.sleuthkit.autopsy.casemodule.StartupWindowProvider;
+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.casemodule.AutoIngestCasePanelInterface;
-import org.sleuthkit.autopsy.casemodule.CueBannerPanel;
-import org.sleuthkit.autopsy.coreutils.NetworkUtils;
-import org.sleuthkit.autopsy.experimental.configuration.StartupWindow;
+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.
*/
-@ServiceProvider(service = AutoIngestCasePanelInterface.class)
-public final class AutoIngestCasePanel extends JPanel implements AutoIngestCasePanelInterface {
+final class MultiUserCasesPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
- private static final Logger logger = Logger.getLogger(AutoIngestCasePanel.class.getName());
- private static final AutoIngestCase.LastAccessedDateDescendingComparator reverseDateModifiedComparator = new AutoIngestCase.LastAccessedDateDescendingComparator();
+ private static final Logger LOGGER = Logger.getLogger(MultiUserCasesPanel.class.getName());
+ private static final String LOG_FILE_NAME = "auto_ingest_log.txt";
+ private static final MultiUserCaseManager.MultiUserCase.LastAccessedDateDescendingComparator REVERSE_DATE_MODIFIED_COMPARATOR = new MultiUserCaseManager.MultiUserCase.LastAccessedDateDescendingComparator();
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;
@@ -71,9 +57,6 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
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 int MILLIS_TO_WAIT_BEFORE_STARTING = 500;
- private static final int MILLIS_TO_WAIT_BETWEEN_UPDATES = 300000;
- private ScheduledThreadPoolExecutor casesTableRefreshExecutor;
/*
* The JTable table model for the cases table presented by this view is
@@ -82,11 +65,12 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
* TODO (RC): Consider unifying this stuff in an enum as in
* AutoIngestDashboard to make it less error prone.
*/
- private static final String CASE_HEADER = org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "ReviewModeCasePanel.CaseHeaderText");
- private static final String CREATEDTIME_HEADER = org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "ReviewModeCasePanel.CreatedTimeHeaderText");
- private static final String COMPLETEDTIME_HEADER = org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "ReviewModeCasePanel.LastAccessedTimeHeaderText");
- private static final String STATUS_ICON_HEADER = org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "ReviewModeCasePanel.StatusIconHeaderText");
- private static final String OUTPUT_FOLDER_HEADER = org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "ReviewModeCasePanel.OutputFolderHeaderText");
+ private static final String CASE_HEADER = org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.CaseHeaderText");
+ private static final String CREATEDTIME_HEADER = org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.CreatedTimeHeaderText");
+ private static final String COMPLETEDTIME_HEADER = org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.LastAccessedTimeHeaderText");
+ private static final String STATUS_ICON_HEADER = org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.StatusIconHeaderText");
+ private static final String OUTPUT_FOLDER_HEADER = org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.OutputFolderHeaderText");
+ private static final String METADATA_FILE_HEADER = org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.MetadataFileHeaderText");
enum COLUMN_HEADERS {
@@ -94,51 +78,20 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
CREATEDTIME,
COMPLETEDTIME,
STATUS_ICON,
- OUTPUTFOLDER
+ OUTPUTFOLDER,
+ METADATA_FILE
}
- private final String[] columnNames = {CASE_HEADER, CREATEDTIME_HEADER, COMPLETEDTIME_HEADER, STATUS_ICON_HEADER, OUTPUT_FOLDER_HEADER};
+ private final String[] columnNames = {CASE_HEADER, CREATEDTIME_HEADER, COMPLETEDTIME_HEADER, STATUS_ICON_HEADER, OUTPUT_FOLDER_HEADER, METADATA_FILE_HEADER};
private DefaultTableModel caseTableModel;
private Path currentlySelectedCase = null;
-
- public AutoIngestCasePanel() {
- init(null);
- }
-
- @Override
- public void addWindowStateListener(JDialog parent) {
- /*
- * Add a window state listener that starts and stops refreshing of the
- * cases table.
- */
- parent.addWindowListener(new WindowAdapter() {
- @Override
- public void windowClosing(WindowEvent e) {
- stopCasesTableRefreshes();
- }
-
- @Override
- public void windowActivated(WindowEvent e) {
- startCasesTableRefreshes();
- }
-
- @Override
- public void windowClosed(WindowEvent e) {
- stopCasesTableRefreshes();
- }
- });
- }
+ private JDialog parentDialog;
/**
* Constructs a panel that allows a user to open cases created by automated
* ingest.
- *
- * @param parent The parent dialog for this panel.
*/
- public AutoIngestCasePanel(JDialog parent) {
- init(parent);
- }
-
- public void init(JDialog parent) {
+ MultiUserCasesPanel(JDialog parentDialog) {
+ this.parentDialog = parentDialog;
caseTableModel = new DefaultTableModel(columnNames, 0) {
private static final long serialVersionUID = 1L;
@@ -146,6 +99,7 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
public boolean isCellEditable(int row, int column) {
return false;
}
+
@Override
public Class> getColumnClass(int col) {
if (this.getColumnName(col).equals(CREATEDTIME_HEADER) || this.getColumnName(col).equals(COMPLETEDTIME_HEADER)) {
@@ -184,13 +138,14 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
theColumn.setWidth(TIME_COL_PREFERRED_WIDTH);
theColumn = casesTable.getColumn(STATUS_ICON_HEADER);
- theColumn.setCellRenderer(new CaseStatusIconCellRenderer());
+ 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));
/*
* Listen for row selection changes and set button state for the current
@@ -203,75 +158,39 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
}
setButtons();
});
-
- /*
- * Add a window state listener that starts and stops refreshing of the
- * cases table.
- */
- if (parent != null) {
- parent.addWindowListener(new WindowAdapter() {
- @Override
- public void windowClosing(WindowEvent e) {
- stopCasesTableRefreshes();
- }
-
- @Override
- public void windowActivated(WindowEvent e) {
- startCasesTableRefreshes();
- }
-
- @Override
- public void windowClosed(WindowEvent e) {
- stopCasesTableRefreshes();
- }
- });
- }
- }
-
- /**
- * Start doing periodic refreshes of the cases table.
- */
- private void startCasesTableRefreshes() {
- if (null == casesTableRefreshExecutor) {
- casesTableRefreshExecutor = new ScheduledThreadPoolExecutor(1);
- this.casesTableRefreshExecutor.scheduleAtFixedRate(() -> {
- refreshCasesTable();
- }, MILLIS_TO_WAIT_BEFORE_STARTING, MILLIS_TO_WAIT_BETWEEN_UPDATES, TimeUnit.MILLISECONDS);
- }
- }
-
- /**
- * Stop doing periodic refreshes of the cases table.
- */
- private void stopCasesTableRefreshes() {
- if (null != casesTableRefreshExecutor) {
- casesTableRefreshExecutor.shutdown();
- }
- this.casesTableRefreshExecutor = null;
- }
-
- /*
- * Updates the view presented by the panel.
- */
- public void updateView() {
- Thread thread = new Thread(() -> {
- refreshCasesTable();
- });
- thread.start();
}
/**
* Gets the list of cases known to the review mode cases manager and
* refreshes the cases table.
*/
- private void refreshCasesTable() {
+ void refresh() {
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+
try {
currentlySelectedCase = getSelectedCase();
- AutoIngestCaseManager manager = AutoIngestCaseManager.getInstance();
- List theModel = manager.getCases();
- EventQueue.invokeLater(new CaseTableRefreshTask(theModel));
- } catch (Exception ex) {
- logger.log(Level.SEVERE, "Unexpected exception in refreshCasesTable", ex); //NON-NLS
+ MultiUserCaseManager manager = MultiUserCaseManager.getInstance();
+ List cases = manager.getCases();
+ cases.sort(REVERSE_DATE_MODIFIED_COMPARATOR);
+ caseTableModel.setRowCount(0);
+ long now = new Date().getTime();
+ for (MultiUserCase autoIngestCase : cases) {
+ if (passesTimeFilter(now, autoIngestCase.getLastAccessedDate().getTime())) {
+ caseTableModel.addRow(new Object[]{
+ autoIngestCase.getCaseDisplayName(),
+ autoIngestCase.getCreationDate(),
+ autoIngestCase.getLastAccessedDate(),
+ (MultiUserCaseManager.CaseStatus.OK != autoIngestCase.getStatus()) ? StatusIconCellRenderer.Status.WARNING : StatusIconCellRenderer.Status.OK,
+ autoIngestCase.getCaseDirectoryPath().toString(),
+ autoIngestCase.getMetadataFileName()});
+ }
+ }
+ setSelectedCase(currentlySelectedCase);
+ setButtons();
+ } catch (MultiUserCaseManager.MultiUserCaseManagerException | CoordinationService.CoordinationServiceException ex) {
+ LOGGER.log(Level.SEVERE, "Unexpected exception while refreshing the table.", ex); //NON-NLS
+ } finally {
+ setCursor(null);
}
}
@@ -320,103 +239,78 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
* in the cases table.
*/
private void setButtons() {
- boolean enabled = casesTable.getSelectedRow() >= 0 && casesTable.getSelectedRow() < casesTable.getRowCount();
- bnOpen.setEnabled(enabled);
- bnShowLog.setEnabled(enabled);
+ boolean openEnabled = casesTable.getSelectedRow() >= 0 && casesTable.getSelectedRow() < casesTable.getRowCount();
+ bnOpen.setEnabled(openEnabled);
+
+ Path pathToLog = getSelectedCaseLogFilePath();
+ boolean showLogEnabled = openEnabled && pathToLog != null && pathToLog.toFile().exists();
+ bnShowLog.setEnabled(showLogEnabled);
}
/**
- * Opens a case.
+ * 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;
+ }
+
+ /**
+ * Open a case.
*
* @param caseMetadataFilePath The path to the case metadata file.
*/
private void openCase(Path caseMetadataFilePath) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
- new SwingWorker() {
-
- @Override
- protected Void doInBackground() throws Exception {
- AutoIngestCaseManager.getInstance().openCase(caseMetadataFilePath);
- stopCasesTableRefreshes();
- StartupWindowProvider.getInstance().close();
- CueBannerPanel.closeAutoIngestCasesWindow();
- return null;
+ try {
+ StartupWindowProvider.getInstance().close();
+ if (parentDialog != null) {
+ parentDialog.setVisible(false);
}
-
- @Override
- protected void done() {
- try {
- get();
- } catch (InterruptedException | ExecutionException 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());
- }
- StartupWindowProvider.getInstance().open();
- } finally {
- setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
- }
+ 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());
}
- }.execute();
+ StartupWindowProvider.getInstance().open();
+ } finally {
+ setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
}
/**
- * A task that refreshes the cases table using a list of auto ingest cases.
+ * 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 class CaseTableRefreshTask implements Runnable {
-
- private final List cases;
-
- CaseTableRefreshTask(List cases) {
- setButtons();
- this.cases = cases;
+ 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;
}
-
- /**
- * @inheritDoc
- */
- @Override
- public void run() {
- cases.sort(reverseDateModifiedComparator);
- caseTableModel.setRowCount(0);
- long now = new Date().getTime();
- for (AutoIngestCase autoIngestCase : cases) {
- if (passesTimeFilter(now, autoIngestCase.getLastAccessedDate().getTime())) {
- caseTableModel.addRow(new Object[]{
- autoIngestCase.getCaseName(),
- autoIngestCase.getCreationDate(),
- autoIngestCase.getLastAccessedDate(),
- (AutoIngestCase.CaseStatus.OK != autoIngestCase.getStatus()),
- autoIngestCase.getCaseDirectoryPath().toString()});
- }
- }
- setSelectedCase(currentlySelectedCase);
- }
-
- /**
- * 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;
- }
- return ((currentTime - inputTime) / (1000 * 60 * 60 * 24)) < (numberOfUnits * multiplier);
- }
-
+ return ((currentTime - inputTime) / (1000 * 60 * 60 * 24)) < (numberOfUnits * multiplier);
}
/**
@@ -443,7 +337,7 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
setName("Completed Cases"); // NOI18N
- org.openide.awt.Mnemonics.setLocalizedText(bnOpen, org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "AutoIngestCasePanel.bnOpen.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(bnOpen, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnOpen.text")); // NOI18N
bnOpen.setEnabled(false);
bnOpen.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
@@ -463,7 +357,7 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
});
scrollPaneTable.setViewportView(casesTable);
- org.openide.awt.Mnemonics.setLocalizedText(bnRefresh, org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "AutoIngestCasePanel.bnRefresh.text")); // NOI18N
+ 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);
@@ -472,7 +366,7 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
rbGroupHistoryLength.add(rbAllCases);
rbAllCases.setSelected(true);
- org.openide.awt.Mnemonics.setLocalizedText(rbAllCases, org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "AutoIngestCasePanel.rbAllCases.text")); // NOI18N
+ 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);
@@ -494,8 +388,8 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
.addComponent(rbAllCases))
);
- org.openide.awt.Mnemonics.setLocalizedText(bnShowLog, org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "AutoIngestCasePanel.bnShowLog.text")); // NOI18N
- bnShowLog.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "AutoIngestCasePanel.bnShowLog.toolTipText")); // NOI18N
+ 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) {
@@ -504,7 +398,7 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
});
rbGroupHistoryLength.add(rbDays);
- org.openide.awt.Mnemonics.setLocalizedText(rbDays, org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "AutoIngestCasePanel.rbDays.text")); // NOI18N
+ 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) {
@@ -513,7 +407,7 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
});
rbGroupHistoryLength.add(rbWeeks);
- org.openide.awt.Mnemonics.setLocalizedText(rbWeeks, org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "AutoIngestCasePanel.rbWeeks.text")); // NOI18N
+ 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);
@@ -521,7 +415,7 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
});
rbGroupHistoryLength.add(rbMonths);
- org.openide.awt.Mnemonics.setLocalizedText(rbMonths, org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "AutoIngestCasePanel.rbMonths.text")); // NOI18N
+ 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);
@@ -529,7 +423,7 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
});
rbGroupLabel.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
- org.openide.awt.Mnemonics.setLocalizedText(rbGroupLabel, org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "AutoIngestCasePanel.rbGroupLabel.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(rbGroupLabel, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbGroupLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
@@ -556,7 +450,7 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(bnRefresh)
.addGap(4, 4, 4))
- .addComponent(scrollPaneTable, javax.swing.GroupLayout.DEFAULT_SIZE, 1007, Short.MAX_VALUE))
+ .addComponent(scrollPaneTable))
.addContainerGap())
);
layout.setVerticalGroup(
@@ -588,10 +482,12 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
*/
private void bnOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenActionPerformed
int modelRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow());
- Path caseMetadataFilePath = Paths.get((String) caseTableModel.getValueAt(modelRow,
- COLUMN_HEADERS.OUTPUTFOLDER.ordinal()),
- caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.CASE.ordinal()) + CaseMetadata.getFileExtension());
- openCase(caseMetadataFilePath);
+ String caseDirectory = (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal());
+ Path caseMetadataFilePath = Paths.get(caseDirectory, (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.METADATA_FILE.ordinal()));
+
+ new Thread(() -> {
+ openCase(caseMetadataFilePath);
+ }).start();
}//GEN-LAST:event_bnOpenActionPerformed
/**
@@ -599,53 +495,50 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
*
* @param evt -- The event that caused this to be called
*/
- private void bnRefreshActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bnRefreshActionPerformed
- {//GEN-HEADEREND:event_bnRefreshActionPerformed
- updateView();
- }//GEN-LAST:event_bnRefreshActionPerformed
+ private void bnRefreshActionPerformed(java.awt.event.ActionEvent evt) {
+ refresh();
+ }
- private void rbDaysItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbDaysItemStateChanged
+ private void rbDaysItemStateChanged(java.awt.event.ItemEvent evt) {
if (rbDays.isSelected()) {
- updateView();
+ refresh();
}
- }//GEN-LAST:event_rbDaysItemStateChanged
+ }
private void rbAllCasesItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbAllCasesItemStateChanged
if (rbAllCases.isSelected()) {
- updateView();
+ refresh();
}
}//GEN-LAST:event_rbAllCasesItemStateChanged
private void rbMonthsItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbMonthsItemStateChanged
if (rbMonths.isSelected()) {
- updateView();
+ refresh();
}
}//GEN-LAST:event_rbMonthsItemStateChanged
private void rbWeeksItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbWeeksItemStateChanged
if (rbWeeks.isSelected()) {
- updateView();
+ refresh();
}
}//GEN-LAST:event_rbWeeksItemStateChanged
private void bnShowLogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnShowLogActionPerformed
- int selectedRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow());
- int rowCount = casesTable.getRowCount();
- if (selectedRow >= 0 && selectedRow < rowCount) {
- String thePath = (String) caseTableModel.getValueAt(selectedRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal());
- Path pathToLog = AutoIngestJobLogger.getLogPath(Paths.get(thePath));
+ 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(AutoIngestCasePanel.class, "DisplayLogDialog.cannotFindLog"),
- org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "DisplayLogDialog.unableToShowLogFile"), JOptionPane.ERROR_MESSAGE);
+ 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);
+ 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(AutoIngestCasePanel.class, "DisplayLogDialog.cannotOpenLog"),
- org.openide.util.NbBundle.getMessage(AutoIngestCasePanel.class, "DisplayLogDialog.unableToShowLogFile"),
+ org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.cannotOpenLog"),
+ org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.unableToShowLogFile"),
JOptionPane.PLAIN_MESSAGE);
}
}
@@ -654,9 +547,8 @@ public final class AutoIngestCasePanel extends JPanel implements AutoIngestCaseP
private void casesTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_casesTableMouseClicked
if (evt.getClickCount() == 2) {
int modelRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow());
- Path caseMetadataFilePath = Paths.get((String) caseTableModel.getValueAt(modelRow,
- COLUMN_HEADERS.OUTPUTFOLDER.ordinal()),
- caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.CASE.ordinal()) + CaseMetadata.getFileExtension());
+ 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
diff --git a/Core/src/org/sleuthkit/autopsy/core/Installer.java b/Core/src/org/sleuthkit/autopsy/core/Installer.java
index 0b660acd2a..83250f719d 100755
--- a/Core/src/org/sleuthkit/autopsy/core/Installer.java
+++ b/Core/src/org/sleuthkit/autopsy/core/Installer.java
@@ -29,15 +29,15 @@ import java.util.logging.Handler;
import java.util.logging.Level;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
-import javax.swing.SwingWorker;
-import org.openide.LifecycleManager;
import org.openide.modules.ModuleInstall;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.actions.IngestRunningCheck;
import org.sleuthkit.autopsy.casemodule.Case;
+import static org.sleuthkit.autopsy.core.UserPreferences.SETTINGS_PROPERTIES;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
+import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
@@ -206,6 +206,9 @@ public class Installer extends ModuleInstall {
// Prevent the Autopsy UI from shrinking on high DPI displays
System.setProperty("sun.java2d.dpiaware", "false");
System.setProperty("prism.allowhidpi", "false");
+
+ // Update existing configuration in case of unsupported settings
+ updateConfig();
packageInstallers = new ArrayList<>();
packageInstallers.add(org.sleuthkit.autopsy.coreutils.Installer.getDefault());
@@ -214,6 +217,21 @@ public class Installer extends ModuleInstall {
packageInstallers.add(org.sleuthkit.autopsy.ingest.Installer.getDefault());
packageInstallers.add(org.sleuthkit.autopsy.centralrepository.eventlisteners.Installer.getDefault());
}
+
+ /**
+ * If the mode in the configuration file is 'REVIEW' (2, now invalid), this
+ * method will set it to 'STANDALONE' (0) and disable auto ingest.
+ */
+ private void updateConfig() {
+ String mode = ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, "AutopsyMode");
+ if(mode != null) {
+ int ordinal = Integer.parseInt(mode);
+ if(ordinal > 1) {
+ UserPreferences.setMode(UserPreferences.SelectedMode.STANDALONE);
+ ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, "JoinAutoModeCluster", Boolean.toString(false));
+ }
+ }
+ }
/**
* Check if JavaFx initialized
diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java
index df63b0fa1f..86f68e1df5 100755
--- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java
+++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2014 Basis Technology Corp.
+ * Copyright 2014-2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,7 +23,6 @@ import java.util.prefs.BackingStoreException;
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
-import org.openide.util.Exceptions;
import org.openide.util.NbPreferences;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
@@ -76,8 +75,7 @@ public final class UserPreferences {
public enum SelectedMode {
STANDALONE,
- AUTOINGEST,
- REVIEW
+ AUTOINGEST
};
/**
diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml
index 95cfcd481e..fb5618c6e6 100755
--- a/Core/src/org/sleuthkit/autopsy/core/layer.xml
+++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml
@@ -51,7 +51,7 @@
-
+
@@ -157,7 +157,7 @@
-
+
@@ -165,11 +165,11 @@
-
+
-
+
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/TimeStampUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/TimeStampUtils.java
similarity index 73%
rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/TimeStampUtils.java
rename to Core/src/org/sleuthkit/autopsy/coreutils/TimeStampUtils.java
index fe53871dd5..e14b5c5897 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/TimeStampUtils.java
+++ b/Core/src/org/sleuthkit/autopsy/coreutils/TimeStampUtils.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2015 Basis Technology Corp.
+ * Copyright 2015-2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.sleuthkit.autopsy.experimental.autoingest;
+package org.sleuthkit.autopsy.coreutils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -24,32 +24,32 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * Utility methods for working with strings with the time-stamp suffixes used by
- * auto ingest.
+ * Utility methods for working with time stamps of the form
+ * 'yyyy_MM_dd_HH_mm_ss'.
*/
public final class TimeStampUtils {
/*
* Sample time stamp suffix: 2015_02_02_12_10_31
*/
- private static final Pattern timeStampPattern = Pattern.compile("\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}$");
+ private static final Pattern TIME_STAMP_PATTERN = Pattern.compile("\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}$");
private static final int LENGTH_OF_DATE_TIME_STAMP = 20; // length of the above time stamp
- private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
/**
- * Checks whether a string ends with an auto ingest time stamp.
+ * Checks whether a string ends with a time stamp.
*
* @param inputString The string to check.
*
* @return True or false.
*/
public static boolean endsWithTimeStamp(String inputString) {
- Matcher m = timeStampPattern.matcher(inputString);
+ Matcher m = TIME_STAMP_PATTERN.matcher(inputString);
return m.find();
}
/**
- * Gets the fixed length of the auto-ingest time stamp suffix.
+ * Gets the fixed length of the time stamp suffix.
*
* @return The length.
*/
@@ -58,16 +58,16 @@ public final class TimeStampUtils {
}
/**
- * Creates an auto ingest time stamp suffix using the current time.
+ * Creates a time stamp suffix using the current time.
*
* @return The suffix.
*/
public static String createTimeStamp() {
- return dateFormat.format(Calendar.getInstance().getTime());
+ return DATE_FORMAT.format(Calendar.getInstance().getTime());
}
/**
- * Removes an auto ingest timestamp suffix, if it present.
+ * Removes the time stamp suffix from a string, if present.
*
* @param inputString The string to trim.
*
@@ -82,7 +82,7 @@ public final class TimeStampUtils {
}
/**
- * Gets the auto ingest time stamp suffix from a string, if it is present.
+ * Gets the time stamp suffix from a string, if present.
*
* @param inputString the name to check for a timestamp
*
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CenteredGrayableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/guiutils/CenteredGrayableCellRenderer.java
similarity index 91%
rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CenteredGrayableCellRenderer.java
rename to Core/src/org/sleuthkit/autopsy/guiutils/CenteredGrayableCellRenderer.java
index b9040b674b..af37f3f2e3 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CenteredGrayableCellRenderer.java
+++ b/Core/src/org/sleuthkit/autopsy/guiutils/CenteredGrayableCellRenderer.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2015 Basis Technology Corp.
+ * Copyright 2015-2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.sleuthkit.autopsy.experimental.autoingest;
+package org.sleuthkit.autopsy.guiutils;
import static javax.swing.SwingConstants.CENTER;
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DurationCellRenderer.java b/Core/src/org/sleuthkit/autopsy/guiutils/DurationCellRenderer.java
similarity index 71%
rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DurationCellRenderer.java
rename to Core/src/org/sleuthkit/autopsy/guiutils/DurationCellRenderer.java
index 6bac0a996b..970dafacfc 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DurationCellRenderer.java
+++ b/Core/src/org/sleuthkit/autopsy/guiutils/DurationCellRenderer.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2015 Basis Technology Corp.
+ * Copyright 2015-2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.sleuthkit.autopsy.experimental.autoingest;
+package org.sleuthkit.autopsy.guiutils;
+import java.awt.Color;
import java.awt.Component;
import java.time.Duration;
import javax.swing.JTable;
@@ -28,11 +29,11 @@ import static javax.swing.SwingConstants.CENTER;
* string with days, hours, minutes, and seconds components. It center-aligns
* cell content and grays out the cell if the table is disabled.
*/
-class DurationCellRenderer extends GrayableCellRenderer {
+public class DurationCellRenderer extends GrayableCellRenderer {
private static final long serialVersionUID = 1L;
- DurationCellRenderer() {
+ public DurationCellRenderer() {
setHorizontalAlignment(CENTER);
}
@@ -71,4 +72,26 @@ class DurationCellRenderer extends GrayableCellRenderer {
grayCellIfTableNotEnabled(table, isSelected);
return this;
}
+
+ void grayCellIfTableNotEnabled(JTable table, boolean isSelected) {
+ if (table.isEnabled()) {
+ /*
+ * The table is enabled, make the foreground and background the
+ * normal selected or unselected color.
+ */
+ if (isSelected) {
+ setBackground(table.getSelectionBackground());
+ setForeground(table.getSelectionForeground());
+ } else {
+ setBackground(table.getBackground());
+ setForeground(table.getForeground());
+ }
+ } else {
+ /*
+ * The table is disabled, make the foreground and background gray.
+ */
+ setBackground(Color.lightGray);
+ setForeground(Color.darkGray);
+ }
+ }
}
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/GrayableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/guiutils/GrayableCellRenderer.java
similarity index 91%
rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/GrayableCellRenderer.java
rename to Core/src/org/sleuthkit/autopsy/guiutils/GrayableCellRenderer.java
index 60d3b77ccd..53031faae3 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/GrayableCellRenderer.java
+++ b/Core/src/org/sleuthkit/autopsy/guiutils/GrayableCellRenderer.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2015 Basis Technology Corp.
+ * Copyright 2015-2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.sleuthkit.autopsy.experimental.autoingest;
+package org.sleuthkit.autopsy.guiutils;
import java.awt.Color;
import java.awt.Component;
@@ -28,11 +28,11 @@ import javax.swing.table.DefaultTableCellRenderer;
* A JTable cell renderer that left-aligns cell content and grays out the cell
* if the table is disabled.
*/
-class GrayableCellRenderer extends DefaultTableCellRenderer {
+public class GrayableCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
- GrayableCellRenderer() {
+ public GrayableCellRenderer() {
setHorizontalAlignment(LEFT);
}
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/LongDateCellRenderer.java b/Core/src/org/sleuthkit/autopsy/guiutils/LongDateCellRenderer.java
similarity index 91%
rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/LongDateCellRenderer.java
rename to Core/src/org/sleuthkit/autopsy/guiutils/LongDateCellRenderer.java
index cfa2cedb14..373e4e2501 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/LongDateCellRenderer.java
+++ b/Core/src/org/sleuthkit/autopsy/guiutils/LongDateCellRenderer.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2015 Basis Technology Corp.
+ * Copyright 2015-2017 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.sleuthkit.autopsy.experimental.autoingest;
+package org.sleuthkit.autopsy.guiutils;
import java.awt.Component;
import java.text.SimpleDateFormat;
@@ -28,7 +28,7 @@ import static javax.swing.SwingConstants.CENTER;
* center-aligned, long-format date string. It also grays out the cell if the
* table is disabled.
*/
-class LongDateCellRenderer extends GrayableCellRenderer {
+public class LongDateCellRenderer extends GrayableCellRenderer {
private static final long serialVersionUID = 1L;
private static final String FORMAT_STRING = "yyyy/MM/dd HH:mm:ss"; //NON-NLS
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ShortDateCellRenderer.java b/Core/src/org/sleuthkit/autopsy/guiutils/ShortDateCellRenderer.java
similarity index 66%
rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ShortDateCellRenderer.java
rename to Core/src/org/sleuthkit/autopsy/guiutils/ShortDateCellRenderer.java
index 713d177c0a..299880f1c0 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ShortDateCellRenderer.java
+++ b/Core/src/org/sleuthkit/autopsy/guiutils/ShortDateCellRenderer.java
@@ -16,8 +16,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.sleuthkit.autopsy.experimental.autoingest;
+package org.sleuthkit.autopsy.guiutils;
+import java.awt.Color;
import java.awt.Component;
import java.text.SimpleDateFormat;
import javax.swing.JTable;
@@ -46,4 +47,26 @@ class ShortDateCellRenderer extends GrayableCellRenderer {
grayCellIfTableNotEnabled(table, isSelected);
return this;
}
+
+ void grayCellIfTableNotEnabled(JTable table, boolean isSelected) {
+ if (table.isEnabled()) {
+ /*
+ * The table is enabled, make the foreground and background the
+ * normal selected or unselected color.
+ */
+ if (isSelected) {
+ setBackground(table.getSelectionBackground());
+ setForeground(table.getSelectionForeground());
+ } else {
+ setBackground(table.getBackground());
+ setForeground(table.getForeground());
+ }
+ } else {
+ /*
+ * The table is disabled, make the foreground and background gray.
+ */
+ setBackground(Color.lightGray);
+ setForeground(Color.darkGray);
+ }
+ }
}
diff --git a/Core/src/org/sleuthkit/autopsy/guiutils/StatusIconCellRenderer.java b/Core/src/org/sleuthkit/autopsy/guiutils/StatusIconCellRenderer.java
new file mode 100755
index 0000000000..b7ee58e4d4
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/guiutils/StatusIconCellRenderer.java
@@ -0,0 +1,74 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2015-2017 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.guiutils;
+
+import java.awt.Component;
+import javax.swing.ImageIcon;
+import javax.swing.JTable;
+import static javax.swing.SwingConstants.CENTER;
+import org.openide.util.ImageUtilities;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ * A JTable cell renderer that represents a status as a center-aligned icon, and
+ * grays out the cell if the table is disabled. The statuses represented are OK,
+ * WARNING, and ERROR.
+ */
+public class StatusIconCellRenderer extends GrayableCellRenderer {
+
+ private static final long serialVersionUID = 1L;
+ static final ImageIcon OK_ICON = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/tick.png", false));
+ static final ImageIcon WARNING_ICON = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/warning16.png", false));
+ static final ImageIcon ERROR_ICON = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/cross-script.png", false));
+
+ @Messages({
+ "StatusIconCellRenderer.tooltiptext.ok=OK",
+ "StatusIconCellRenderer.tooltiptext.warning=A warning occurred",
+ "StatusIconCellRenderer.tooltiptext.error=An error occurred"
+ })
+ @Override
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ setHorizontalAlignment(CENTER);
+ if ((value instanceof Status)) {
+ switch((Status) value) {
+ case OK:
+ setIcon(OK_ICON);
+ setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.ok"));
+ break;
+ case WARNING:
+ setIcon(WARNING_ICON);
+ setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.warning"));
+ break;
+ case ERROR:
+ setIcon(ERROR_ICON);
+ setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.error"));
+ break;
+ }
+ }
+ grayCellIfTableNotEnabled(table, isSelected);
+
+ return this;
+ }
+
+ public enum Status {
+ OK,
+ WARNING,
+ ERROR
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/images/tick.png b/Core/src/org/sleuthkit/autopsy/images/tick.png
new file mode 100755
index 0000000000..a7d7a96be3
Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/tick.png differ
diff --git a/Core/src/org/sleuthkit/autopsy/images/warning16.png b/Core/src/org/sleuthkit/autopsy/images/warning16.png
new file mode 100755
index 0000000000..f5ba881738
Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/warning16.png differ
diff --git a/Core/test/unit/src/org/sleuthkit/autopsy/ingest/IntestFileFiltersTest.java b/Core/test/unit/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java
similarity index 94%
rename from Core/test/unit/src/org/sleuthkit/autopsy/ingest/IntestFileFiltersTest.java
rename to Core/test/unit/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java
index b952462de8..29618e30ec 100755
--- a/Core/test/unit/src/org/sleuthkit/autopsy/ingest/IntestFileFiltersTest.java
+++ b/Core/test/unit/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java
@@ -23,9 +23,9 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
-public class IntestFileFiltersTest {
+public class IngestFileFiltersTest {
- public IntestFileFiltersTest() {
+ public IngestFileFiltersTest() {
}
@BeforeClass
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java
index 3bc76e61fd..b2204f1b2c 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java
@@ -28,6 +28,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
import org.sleuthkit.datamodel.Content;
/*
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCase.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCase.java
deleted file mode 100755
index ff47423030..0000000000
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCase.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Autopsy Forensic Browser
- *
- * Copyright 2015-2017 Basis Technology Corp.
- * Contact: carrier sleuthkit org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.autopsy.experimental.autoingest;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.Objects;
-import java.util.logging.Level;
-import org.sleuthkit.autopsy.casemodule.CaseMetadata;
-import org.sleuthkit.autopsy.coreutils.Logger;
-
-/**
- * A representation of a case created by automated ingest.
- */
-class AutoIngestCase implements Comparable {
-
- private static final Logger logger = Logger.getLogger(AutoIngestCase.class.getName());
- private final Path caseDirectoryPath;
- private final String caseName;
- private final Path metadataFilePath;
- private final Date createDate;
- private final Date lastAccessedDate;
-
- /**
- * Constructs a representation of case created by automated ingest.
- *
- * @param caseDirectoryPath The case directory path.
- */
- AutoIngestCase(Path caseDirectoryPath) {
- this.caseDirectoryPath = caseDirectoryPath;
- caseName = PathUtils.caseNameFromCaseDirectoryPath(caseDirectoryPath);
- metadataFilePath = caseDirectoryPath.resolve(caseName + CaseMetadata.getFileExtension());
- BasicFileAttributes fileAttrs = null;
- try {
- fileAttrs = Files.readAttributes(metadataFilePath, 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 name.
- *
- * @return The case name.
- */
- String getCaseName() {
- return this.caseName;
- }
-
- /**
- * 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 the status of this case based on the auto ingest result file in the
- * case directory.
- *
- * @return See CaseStatus enum definition.
- */
- CaseStatus getStatus() {
- if (AutoIngestAlertFile.exists(caseDirectoryPath)) {
- return CaseStatus.ALERT;
- } else {
- return CaseStatus.OK;
- }
- }
-
- /**
- * Indicates whether or not some other object is "equal to" this
- * AutoIngestCase object.
- *
- * @param other The other object.
- *
- * @return True or false.
- */
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof AutoIngestCase)) {
- return false;
- }
- if (other == this) {
- return true;
- }
- return this.caseDirectoryPath.toString().equals(((AutoIngestCase) other).caseDirectoryPath.toString());
- }
-
- /**
- * Returns a hash code value for this AutoIngestCase 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.caseName);
- return hash;
- }
-
- /**
- * Compares this AutopIngestCase object with abnother AutoIngestCase object
- * for order.
- */
- @Override
- public int compareTo(AutoIngestCase other) {
- return -this.lastAccessedDate.compareTo(other.getLastAccessedDate());
- }
-
- /**
- * Comparator for a descending order sort on date created.
- */
- static class LastAccessedDateDescendingComparator implements Comparator {
-
- /**
- * Compares two AutoIngestCase objects for order based on last accessed
- * date (descending).
- *
- * @param object The first AutoIngestCase object
- * @param otherObject The second AuotIngestCase 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(AutoIngestCase object, AutoIngestCase otherObject) {
- return -object.getLastAccessedDate().compareTo(otherObject.getLastAccessedDate());
- }
- }
-
- enum CaseStatus {
-
- OK,
- ALERT
- }
-
-}
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseManager.java
deleted file mode 100755
index 8469ff2315..0000000000
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseManager.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Autopsy Forensic Browser
- *
- * Copyright 2011-2017 Basis Technology Corp.
- * Contact: carrier sleuthkit org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.autopsy.experimental.autoingest;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CaseActionException;
-import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
-import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
-
-/**
- * Handles locating and opening cases created by auto ingest.
- */
-final class AutoIngestCaseManager {
-
- private static AutoIngestCaseManager instance;
-
- private CoordinationService coordinationService;
-
- /**
- * Gets the auto ingest case manager.
- *
- * @return The auto ingest case manager singleton.
- *
- * @throws AutoIngestCaseManagerException
- */
- synchronized static AutoIngestCaseManager getInstance() throws AutoIngestCaseManager.AutoIngestCaseManagerException {
- if (null == instance) {
- instance = new AutoIngestCaseManager();
- }
- return instance;
- }
-
- /**
- * Constructs an object that handles locating and opening cases created by
- * auto ingest.
- *
- * @throws AutoIngestCaseManagerException
- */
- private AutoIngestCaseManager() throws AutoIngestCaseManagerException {
- try {
- coordinationService = CoordinationService.getInstance();
- } catch (CoordinationServiceException ex) {
- throw new AutoIngestCaseManager.AutoIngestCaseManagerException("Failed to get the coordination service.", ex);
- }
- }
-
- /**
- * Gets a list of the cases in the top level case folder used by auto
- * ingest.
- *
- * @return List of cases.
- *
- * @throws AutoIngestCaseManagerException
- */
- List getCases() throws AutoIngestCaseManagerException {
- List cases = new ArrayList<>();
- List casePathList = getCasePaths();
- for (Path casePath : casePathList) {
- cases.add(new AutoIngestCase(casePath));
- }
- return cases;
- }
-
- /**
- * Retrieve all of the case nodes and filter for only those that represent
- * case paths.
- *
- * @return List of case paths.
- *
- * @throws AutoIngestCaseManagerException
- */
- private List getCasePaths() throws AutoIngestCaseManagerException {
- try {
- List nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
- List casePathList = new ArrayList(0);
- for (String node : nodeList) {
- if(node.indexOf('\\') >= 0 || node.indexOf('/') >= 0) {
- /*
- * This is not a case name lock (name specifies a path).
- */
- String nodeUpperCase = node.toUpperCase();
- if(!nodeUpperCase.endsWith("_RESOURCES") && !nodeUpperCase.endsWith("AUTO_INGEST_LOG.TXT")) {
- /*
- * This is not a case resource lock, nor a case auto
- * ingest log lock. Collect the path.
- */
- casePathList.add(Paths.get(node));
- }
- }
- }
- return casePathList;
-
- } catch (CoordinationServiceException ex) {
- throw new AutoIngestCaseManager.AutoIngestCaseManagerException("Failed to get node list from coordination service.", ex);
- }
- }
-
- /**
- * Opens an auto ingest case 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 an auto ingest
- * case manager operation.
- */
- static final class AutoIngestCaseManagerException extends Exception {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * Constructs an instance of the exception type thrown when there is an
- * error completing an auto ingest case manager operation.
- *
- * @param message The exception message.
- */
- private AutoIngestCaseManagerException(String message) {
- super(message);
- }
-
- /**
- * Constructs an instance of the exception type thrown when there is an
- * error completing an auto ingest case manager operation.
- *
- * @param message The exception message.
- * @param cause A Throwable cause for the error.
- */
- private AutoIngestCaseManagerException(String message, Throwable cause) {
- super(message, cause);
- }
-
- }
-}
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseOpenAction.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseOpenAction.java
deleted file mode 100755
index e86a7bcf8a..0000000000
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseOpenAction.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Autopsy Forensic Browser
- *
- * Copyright 2015 Basis Technology Corp.
- * Contact: carrier sleuthkit org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.autopsy.experimental.autoingest;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.logging.Level;
-import org.openide.util.HelpCtx;
-import org.openide.util.Lookup;
-import org.openide.util.NbBundle;
-import org.openide.util.actions.CallableSystemAction;
-import org.openide.util.actions.SystemAction;
-import org.sleuthkit.autopsy.casemodule.CaseCloseAction;
-import org.sleuthkit.autopsy.casemodule.CaseOpenAction;
-import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.StartupWindowProvider;
-import org.sleuthkit.autopsy.core.UserPreferences;
-
-final class AutoIngestCaseOpenAction extends CallableSystemAction implements ActionListener {
-
- private static final Logger logger = Logger.getLogger(AutoIngestCaseOpenAction.class.getName());
- private static final long serialVersionUID = 1L;
-
- public AutoIngestCaseOpenAction() {
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
-
- UserPreferences.SelectedMode mode = UserPreferences.getMode();
- switch (mode) {
- case REVIEW:
-
- if (Case.isCaseOpen()) {
- /*
- * In review mode, close the currently open case, if any, and
- * then display the review mode cases panel. This can be
- * accomplished by invoking CaseCloseAction because it calls
- * StartupWindowProvider.getInstance().open() after it closes
- * the current case.
- */
- SystemAction.get(CaseCloseAction.class).actionPerformed(e);
- } else {
- // no case is open, so show the startup window
- StartupWindowProvider.getInstance().open();
- }
- break;
-
- case AUTOINGEST:
- /*
- * New case action is disabled in auto ingest mode.
- */
- break;
-
- case STANDALONE:
- /**
- * In standalone mode, invoke default Autopsy version of CaseOpenAction.
- */
- Lookup.getDefault().lookup(CaseOpenAction.class).actionPerformed(e);
- break;
-
-
- default:
- logger.log(Level.SEVERE, "Attempting to open case in unsupported mode {0}", mode.toString());
- }
- }
-
- @Override
- public void performAction() {
- }
-
- @Override
- public String getName() {
- return NbBundle.getMessage(AutoIngestCaseOpenAction.class, "CTL_OpenAction");
- }
-
- @Override
- public HelpCtx getHelpCtx() {
- return HelpCtx.DEFAULT_HELP;
- }
-
-}
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java
index 891975223a..fa4476d6f5 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java
@@ -60,10 +60,13 @@ import org.sleuthkit.autopsy.core.ServicesMonitor;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
-import org.sleuthkit.autopsy.ingest.IngestManager;
-import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestManager.CaseDeletionResult;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestManager.JobsSnapshot;
+import org.sleuthkit.autopsy.guiutils.DurationCellRenderer;
+import org.sleuthkit.autopsy.guiutils.LongDateCellRenderer;
+import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer;
+import org.sleuthkit.autopsy.ingest.IngestManager;
+import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog;
/**
* A panel for monitoring automated ingest by a cluster, and for controlling
@@ -596,7 +599,7 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
* renderer that will choose an icon to represent the job status.
*/
column = completedTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader());
- column.setCellRenderer(new CaseStatusIconCellRenderer());
+ column.setCellRenderer(new StatusIconCellRenderer());
column.setMinWidth(STATUS_COL_MIN_WIDTH);
column.setMaxWidth(STATUS_COL_MAX_WIDTH);
column.setPreferredWidth(STATUS_COL_PREFERRED_WIDTH);
@@ -1152,7 +1155,7 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
job.getProcessingStageStartDate(), // STARTED_TIME
job.getCompletedDate(), // COMPLETED_TIME
status.getDescription(), // ACTIVITY
- job.getErrorsOccurred(), // STATUS
+ job.getErrorsOccurred() ? StatusIconCellRenderer.Status.WARNING : StatusIconCellRenderer.Status.OK, // STATUS
((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // ACTIVITY_TIME
job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH
job.getProcessingHostName().equals(LOCAL_HOST_NAME), // IS_LOCAL_JOB
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.form b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.form
index 4a943b4924..9a599877f8 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.form
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.form
@@ -29,8 +29,19 @@
+
+
-
+
+
+
+
+
+
+
+
+
+
@@ -39,18 +50,9 @@
-
-
-
-
-
-
-
-
-
@@ -81,6 +83,7 @@
+
@@ -255,5 +258,15 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java
index 2d10dff258..feafba3f4f 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java
@@ -44,6 +44,9 @@ import org.sleuthkit.autopsy.core.ServicesMonitor;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.JobsSnapshot;
+import org.sleuthkit.autopsy.guiutils.DurationCellRenderer;
+import org.sleuthkit.autopsy.guiutils.LongDateCellRenderer;
+import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer;
/**
* A dashboard for monitoring an automated ingest cluster.
@@ -400,7 +403,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
* renderer that will choose an icon to represent the job status.
*/
column = completedTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader());
- column.setCellRenderer(new CaseStatusIconCellRenderer());
+ column.setCellRenderer(new StatusIconCellRenderer());
column.setMinWidth(STATUS_COL_MIN_WIDTH);
column.setMaxWidth(STATUS_COL_MAX_WIDTH);
column.setPreferredWidth(STATUS_COL_PREFERRED_WIDTH);
@@ -472,7 +475,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
job.getProcessingStageStartDate(), // STARTED_TIME
job.getCompletedDate(), // COMPLETED_TIME
status.getDescription(), // STAGE
- job.getErrorsOccurred(), // STATUS
+ job.getErrorsOccurred() ? StatusIconCellRenderer.Status.WARNING : StatusIconCellRenderer.Status.OK, // STATUS
((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // STAGE_TIME
job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH
job.getManifest().getFilePath(), // MANIFEST_FILE_PATH
@@ -662,6 +665,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
tbServicesStatusMessage = new javax.swing.JTextField();
prioritizeJobButton = new javax.swing.JButton();
prioritizeCaseButton = new javax.swing.JButton();
+ clusterMetricsButton = new javax.swing.JButton();
org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.jButton1.text")); // NOI18N
@@ -762,6 +766,13 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
}
});
+ org.openide.awt.Mnemonics.setLocalizedText(clusterMetricsButton, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.clusterMetricsButton.text")); // NOI18N
+ clusterMetricsButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ clusterMetricsButtonActionPerformed(evt);
+ }
+ });
+
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
@@ -770,24 +781,26 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(pendingScrollPane)
+ .addComponent(runningScrollPane, javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(completedScrollPane, javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(lbPending)
- .addComponent(lbCompleted)
- .addComponent(lbRunning)
- .addGroup(layout.createSequentialGroup()
- .addComponent(lbServicesStatus)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
- .addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 861, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addComponent(refreshButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(prioritizeJobButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(prioritizeCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)))
- .addGap(0, 0, Short.MAX_VALUE))
- .addComponent(runningScrollPane, javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(completedScrollPane, javax.swing.GroupLayout.Alignment.LEADING))
+ .addComponent(prioritizeCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(clusterMetricsButton))
+ .addComponent(lbPending, javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(lbCompleted, javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(lbRunning, javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addComponent(lbServicesStatus)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 861, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
@@ -813,7 +826,8 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(refreshButton)
.addComponent(prioritizeJobButton)
- .addComponent(prioritizeCaseButton))
+ .addComponent(prioritizeCaseButton)
+ .addComponent(clusterMetricsButton))
.addContainerGap())
);
}// //GEN-END:initComponents
@@ -872,7 +886,12 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
}
}//GEN-LAST:event_prioritizeCaseButtonActionPerformed
+ private void clusterMetricsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clusterMetricsButtonActionPerformed
+ new AutoIngestMetricsDialog(this.getTopLevelAncestor(), autoIngestMonitor);
+ }//GEN-LAST:event_clusterMetricsButtonActionPerformed
+
// Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton clusterMetricsButton;
private javax.swing.JScrollPane completedScrollPane;
private javax.swing.JTable completedTable;
private javax.swing.JButton jButton1;
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java
index 7e1b1907af..45563a4f4b 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java
@@ -25,7 +25,6 @@ import org.openide.util.HelpCtx;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.core.UserPreferences;
-import static org.sleuthkit.autopsy.core.UserPreferences.SelectedMode.REVIEW;
import org.sleuthkit.autopsy.coreutils.Logger;
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.experimental.autoingest.AutoIngestDashboardOpenAction")
@@ -39,8 +38,7 @@ public final class AutoIngestDashboardOpenAction extends CallableSystemAction {
@Override
public boolean isEnabled() {
- UserPreferences.SelectedMode mode = UserPreferences.getMode();
- return (mode == REVIEW);
+ return (UserPreferences.getIsMultiUserModeEnabled());
}
@Override
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java
index 2f2f22fb0d..b621cb514f 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java
@@ -62,6 +62,7 @@ import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
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.CoordinationService;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
@@ -117,7 +118,6 @@ public final class AutoIngestManager extends Observable implements PropertyChang
private static final int NUM_INPUT_SCAN_SCHEDULING_THREADS = 1;
private static final String INPUT_SCAN_SCHEDULER_THREAD_NAME = "AIM-input-scan-scheduler-%d";
private static final String INPUT_SCAN_THREAD_NAME = "AIM-input-scan-%d";
- private static int DEFAULT_JOB_PRIORITY = 0;
private static final String AUTO_INGEST_THREAD_NAME = "AIM-job-processing-%d";
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
private static final String EVENT_CHANNEL_NAME = "Auto-Ingest-Manager-Events";
@@ -2115,7 +2115,8 @@ public final class AutoIngestManager extends Observable implements PropertyChang
Case.openAsCurrentCase(metadataFilePath.toString());
} else {
caseDirectoryPath = PathUtils.createCaseFolderPath(rootOutputDirectory, caseName);
- Case.createAsCurrentCase(caseDirectoryPath.toString(), caseName, "", "", CaseType.MULTI_USER_CASE);
+ CaseDetails caseDetails = new CaseDetails(caseName);
+ Case.createAsCurrentCase(CaseType.MULTI_USER_CASE, caseDirectoryPath.toString(), caseDetails);
/*
* Sleep a bit before releasing the lock to ensure
* that the new case folder is visible on the
@@ -2932,4 +2933,4 @@ public final class AutoIngestManager extends Observable implements PropertyChang
}
-}
+}
\ No newline at end of file
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.form b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.form
new file mode 100755
index 0000000000..49e700ca21
--- /dev/null
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.form
@@ -0,0 +1,113 @@
+
+
+
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java
new file mode 100755
index 0000000000..66c6d28581
--- /dev/null
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java
@@ -0,0 +1,178 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011-2017 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.experimental.autoingest;
+
+import com.github.lgooddatepicker.datepicker.DatePicker;
+import java.awt.Container;
+import java.awt.Cursor;
+import java.awt.Window;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+import java.time.ZoneOffset;
+import org.openide.util.NbBundle;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ * Display basic metrics for a cluster.
+ */
+final class AutoIngestMetricsDialog extends javax.swing.JDialog {
+
+ private final AutoIngestMonitor autoIngestMonitor;
+
+ /**
+ * Creates new form AutoIngestMetricsDialog
+ *
+ * @param parent The parent container.
+ * @param autoIngestMonitor The auto ingest monitor.
+ */
+ @Messages({
+ "AutoIngestMetricsDialog.title.text=Auto Ingest Cluster Metrics",
+ "AutoIngestMetricsDialog.initReportText=Select a date below and click the 'Get Metrics Since...' button to generate\na metrics report."
+ })
+ AutoIngestMetricsDialog(Container parent, AutoIngestMonitor autoIngestMonitor) {
+ super((Window) parent, NbBundle.getMessage(AutoIngestMetricsDialog.class, "AutoIngestMetricsDialog.title.text"), ModalityType.MODELESS);
+ initComponents();
+ reportTextArea.setText(NbBundle.getMessage(AutoIngestMetricsDialog.class, "AutoIngestMetricsDialog.initReportText"));
+ this.autoIngestMonitor = autoIngestMonitor;
+ setModal(true);
+ setSize(getPreferredSize());
+ setLocationRelativeTo(parent);
+ setVisible(true);
+ }
+
+ /**
+ * Update the metrics shown in the report text area.
+ */
+ private void updateMetrics() {
+ if(datePicker.getDate() == null) {
+ return;
+ }
+
+ AutoIngestMonitor.MetricsSnapshot metricsSnapshot = autoIngestMonitor.getMetricsSnapshot();
+ Object[] completedJobDates = metricsSnapshot.getCompletedJobDates().toArray();
+ int count = 0;
+ long pickedDate = datePicker.getDate().atStartOfDay().toEpochSecond(ZoneOffset.UTC) * 1000;
+ for(int i = completedJobDates.length - 1; i >= 0; i--) {
+ if((Long)completedJobDates[i] >= pickedDate) {
+ count++;
+ }
+ }
+
+ SimpleDateFormat dateFormatter = new SimpleDateFormat("MMM d, yyyy");
+ reportTextArea.setText(String.format(
+ "Since %s:\n" +
+ "\tNumber of Jobs Completed: %d\n",
+ dateFormatter.format(Date.valueOf(datePicker.getDate())),
+ count
+ ));
+ }
+
+ /**
+ * 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")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ closeButton = new javax.swing.JButton();
+ jScrollPane1 = new javax.swing.JScrollPane();
+ reportTextArea = new javax.swing.JTextArea();
+ metricsButton = new javax.swing.JButton();
+ datePicker = new DatePicker();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+ setAlwaysOnTop(true);
+ setResizable(false);
+
+ org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(AutoIngestMetricsDialog.class, "AutoIngestMetricsDialog.closeButton.text")); // NOI18N
+ closeButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ closeButtonActionPerformed(evt);
+ }
+ });
+
+ reportTextArea.setEditable(false);
+ reportTextArea.setColumns(20);
+ reportTextArea.setRows(5);
+ reportTextArea.setText(org.openide.util.NbBundle.getMessage(AutoIngestMetricsDialog.class, "AutoIngestMetricsDialog.reportTextArea.text")); // NOI18N
+ jScrollPane1.setViewportView(reportTextArea);
+
+ org.openide.awt.Mnemonics.setLocalizedText(metricsButton, org.openide.util.NbBundle.getMessage(AutoIngestMetricsDialog.class, "AutoIngestMetricsDialog.metricsButton.text")); // NOI18N
+ metricsButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ metricsButtonActionPerformed(evt);
+ }
+ });
+
+ datePicker.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestMetricsDialog.class, "AutoIngestMetricsDialog.datePicker.toolTipText")); // NOI18N
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jScrollPane1)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(metricsButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(datePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 33, Short.MAX_VALUE)
+ .addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 128, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(closeButton)
+ .addComponent(metricsButton))
+ .addComponent(datePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ pack();
+ }// //GEN-END:initComponents
+
+ private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
+ setVisible(false);
+ dispose();
+ }//GEN-LAST:event_closeButtonActionPerformed
+
+ private void metricsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_metricsButtonActionPerformed
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ updateMetrics();
+ setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }//GEN-LAST:event_metricsButtonActionPerformed
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton closeButton;
+ private com.github.lgooddatepicker.datepicker.DatePicker datePicker;
+ private javax.swing.JScrollPane jScrollPane1;
+ private javax.swing.JButton metricsButton;
+ private javax.swing.JTextArea reportTextArea;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java
index 9a262bb6d3..37c635b9f0 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java
@@ -23,10 +23,12 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Observable;
import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@@ -43,7 +45,7 @@ import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.ProcessingSta
* An auto ingest monitor responsible for monitoring and reporting the
* processing of auto ingest jobs.
*/
-public final class AutoIngestMonitor extends Observable implements PropertyChangeListener {
+final class AutoIngestMonitor extends Observable implements PropertyChangeListener {
private static final Logger LOGGER = Logger.getLogger(AutoIngestMonitor.class.getName());
private static final int NUM_COORD_SVC_QUERY_THREADS = 1;
@@ -265,17 +267,86 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
}
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, String.format("Unexpected interrupt while retrieving coordination service node data for '%s'", node), ex);
- } catch (AutoIngestJobNodeData.InvalidDataException | AutoIngestJob.AutoIngestJobException ex) {
+ } catch (AutoIngestJobNodeData.InvalidDataException ex) {
LOGGER.log(Level.SEVERE, String.format("Unable to use node data for '%s'", node), ex);
+ } catch (AutoIngestJob.AutoIngestJobException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Failed to create a job for '%s'", node), ex);
}
}
+
return newJobsSnapshot;
+
} catch (CoordinationServiceException ex) {
LOGGER.log(Level.SEVERE, "Failed to get node list from coordination service", ex);
return new JobsSnapshot();
}
}
+ /**
+ * Gets a new metrics snapshot from the coordination service for an auto
+ * ingest cluster.
+ *
+ * @return The metrics snapshot.
+ */
+ private MetricsSnapshot queryCoordinationServiceForMetrics() {
+ try {
+ MetricsSnapshot newMetricsSnapshot = new MetricsSnapshot();
+ List nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.MANIFESTS);
+ for (String node : nodeList) {
+ try {
+ AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, node));
+ if (nodeData.getVersion() < 1) {
+ /*
+ * Ignore version '0' nodes that have not been
+ * "upgraded" since they don't carry enough data.
+ */
+ continue;
+ }
+ AutoIngestJob job = new AutoIngestJob(nodeData);
+ ProcessingStatus processingStatus = nodeData.getProcessingStatus();
+ switch (processingStatus) {
+ case PENDING:
+ case PROCESSING:
+ case DELETED:
+ /*
+ * These are not jobs we care about for metrics, so
+ * we will ignore them.
+ */
+ break;
+ case COMPLETED:
+ newMetricsSnapshot.addCompletedJobDate(job.getCompletedDate());
+ break;
+ default:
+ LOGGER.log(Level.SEVERE, "Unknown AutoIngestJobData.ProcessingStatus");
+ break;
+ }
+ } catch (InterruptedException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Unexpected interrupt while retrieving coordination service node data for '%s'", node), ex);
+ } catch (AutoIngestJobNodeData.InvalidDataException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Unable to use node data for '%s'", node), ex);
+ } catch (AutoIngestJob.AutoIngestJobException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Failed to create a job for '%s'", node), ex);
+ }
+ }
+
+ return newMetricsSnapshot;
+
+ } catch (CoordinationServiceException ex) {
+ LOGGER.log(Level.SEVERE, "Failed to get node list from coordination service", ex);
+ return new MetricsSnapshot();
+ }
+ }
+
+ /**
+ * Gets a new metrics snapshot. The jobs snapshot will also be updated in
+ * effect.
+ *
+ * @return The metrics snapshot.
+ */
+ public MetricsSnapshot getMetricsSnapshot() {
+ return queryCoordinationServiceForMetrics();
+ }
+
/**
* Bumps the priority of all pending ingest jobs for a specified case.
*
@@ -522,6 +593,32 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
}
+ /**
+ * A snapshot of metrics for an auto ingest cluster.
+ */
+ public static final class MetricsSnapshot {
+
+ private final List completedJobDates = new ArrayList<>();
+
+ /**
+ * Gets a list of completed job dates, formatted in milliseconds.
+ *
+ * @return The completed job dates, formatted in milliseconds.
+ */
+ List getCompletedJobDates() {
+ return new ArrayList<>(completedJobDates);
+ }
+
+ /**
+ * Adds a new date to the list of completed job dates.
+ *
+ * @param date The date to be added.
+ */
+ void addCompletedJobDate(Date date) {
+ completedJobDates.add(date.getTime());
+ }
+ }
+
/**
* Exception type thrown when there is an error completing an auto ingest
* monitor operation.
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties
index edac418672..5f335b1ba4 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties
@@ -1,4 +1,3 @@
-CTL_OpenAction=Open Case...
AutoIngestDashboard.lbCompleted.text=Completed Jobs
AutoIngestDashboard.lbRunning.text=Running Jobs
AutoIngestDashboard.lbPending.text=Pending Jobs
@@ -47,14 +46,10 @@ OpenIDE-Module-Long-Description=\
We make no guarantee that the API of this module will not change, so developers should be careful when relying on it.
OpenIDE-Module-Name=Experimental
OpenIDE-Module-Short-Description=This module contains features that are being developed by Basis Technology and are not part of the default Autopsy distribution.
-ReviewModeCasePanel.cannotOpenCase=Cannot Open Case
-ReviewModeCasePanel.casePathNotFound=Case path not found
-ReviewModeCasePanel.caseIsLocked=Single-user case is locked.
DisplayLogDialog.cannotOpenLog=Unable to open the selected case log file
DisplayLogDialog.cannotFindLog=Unable to find the selected case log file
DisplayLogDialog.unableToShowLogFile=Unable to show log file
DisplayLogDialog.okay=Okay
-ReviewModeCasePanel.bnShowLog.text=&Show Log
CopyFilesPanel.lbFrom.text=From Source
CopyFilesPanel.lbTo.text=Destination Case
CopyFilesPanel.bnCopy.text=&Copy
@@ -128,8 +123,6 @@ CopyFilesPanel.cbThrottleNetwork.toolTipText=Select this box if a low-band
CopyFilesPanel.bnShowCurrentLog.text=Show &Log
CopyFilesPanel.bnShowCurrentLog.text=Show &Log
CopyFilesPanel.lbCaseName.text=Case Name
-CaseStatusIconCellRenderer.tooltiptext.ok=Images processed successfully
-CaseStatusIconCellRenderer.tooltiptext.warning=An error occurred or processing was canceled for at least one image - please check the log
OptionsCategory_Name_Case_Import=Case Import
OptionsCategory_Keywords_Case_Import=Case Import Settings
CaseImportPanel.validationErrMsg.MUdisabled=Multi user settings must be enabled and saved
@@ -165,11 +158,6 @@ SingleUserCaseImporter.FailedToComplete=Failed to complete processing of
SingleUserCaseImporter.CompletedBatch=Completed batch processing of
SingleUserCaseImporter.AbortingBatch=Aborting batch processing of
SingleUserCaseImporter.SourceImageMissing=. Source image missing for
-ReviewModeCasePanel.CaseHeaderText=Case
-ReviewModeCasePanel.CreatedTimeHeaderText=Created Time
-ReviewModeCasePanel.StatusIconHeaderText=Status
-ReviewModeCasePanel.OutputFolderHeaderText=Output Folder
-ReviewModeCasePanel.LastAccessedTimeHeaderText=Last Accessed Time
CopyFilesPanel.bnOptions.text=&Options
AutoIngestDashboard.lbServicesStatus.text=Services Status:
AutoIngestDashboard.tbServicesStatusMessage.text=Connecting...
@@ -231,15 +219,6 @@ FileExporterSettingsPanel.BrowseReportTooltip_1=Browse for the Reports Folder
FileExporterSettingsPanel.NewRuleTooltip_1=Clear the rule editor to begin a new rule
FileExporterSettingsPanel.DeleteTooltip_1=Delete the selected rule
FileExporterSettingsPanel.SaveTooltip_1=Save the current rule
-AutoIngestCasePanel.rbDays.text=Days
-AutoIngestCasePanel.rbWeeks.text=Weeks
-AutoIngestCasePanel.rbMonths.text=Months
-AutoIngestCasePanel.rbAllCases.text=Everything
-AutoIngestCasePanel.bnRefresh.text=&Refresh
-AutoIngestCasePanel.bnOpen.text=&Open
-AutoIngestCasePanel.bnShowLog.toolTipText=Display case log file for selected case
-AutoIngestCasePanel.bnShowLog.text=&Show Log
-AutoIngestCasePanel.rbGroupLabel.text=Show cases accessed in the last 10:
AutoIngestDashboard.refreshButton.toolTipText=Refresh displayed tables
AutoIngestDashboard.refreshButton.text=&Refresh
AutoIngestDashboard.jButton1.text=jButton1
@@ -247,6 +226,11 @@ AutoIngestDashboard.prioritizeJobButton.toolTipText=Move the selected job to the
AutoIngestDashboard.prioritizeJobButton.text=Prioritize &Job
AutoIngestDashboard.prioritizeCaseButton.toolTipText=Move all images associated with a case to top of Pending queue.
AutoIngestDashboard.prioritizeCaseButton.text=Prioritize &Case
+AutoIngestMetricsDialog.reportTextArea.text=
+AutoIngestDashboard.clusterMetricsButton.text=Cluster Metrics
+AutoIngestMetricsDialog.metricsButton.text=Get Metrics Since...
+AutoIngestMetricsDialog.closeButton.text=Close
+AutoIngestMetricsDialog.datePicker.toolTipText=Choose a date
ArchiveFilePanel.pathLabel.text=Browse for an archive file:
ArchiveFilePanel.browseButton.text=Browse
ArchiveFilePanel.pathTextField.text=
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseImportPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseImportPanel.java
index a7cd13af72..2e4866b010 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseImportPanel.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseImportPanel.java
@@ -672,7 +672,6 @@ public class CaseImportPanel extends javax.swing.JPanel implements ImportDoneCal
*/
private void enableStartButton() {
if (UserPreferences.getIsMultiUserModeEnabled()
- && AutoIngestUserPreferences.getJoinAutoModeCluster()
&& (! RuntimeProperties.runningWithGUI())
&& !tbCaseSource.getText().isEmpty()
&& !tbCaseDestination.getText().isEmpty()
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseStatusIconCellRenderer.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseStatusIconCellRenderer.java
deleted file mode 100755
index 0010cc90ee..0000000000
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseStatusIconCellRenderer.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Autopsy Forensic Browser
- *
- * Copyright 2015 Basis Technology Corp.
- * Contact: carrier sleuthkit org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.autopsy.experimental.autoingest;
-
-import java.awt.Component;
-import javax.swing.ImageIcon;
-import javax.swing.JTable;
-import static javax.swing.SwingConstants.CENTER;
-import org.openide.util.ImageUtilities;
-
-/**
- * A JTable cell renderer that represents an auto ingest alert file exists flag
- * as a center-aligned icon, and grays out the cell if the table is disabled.
- */
-class CaseStatusIconCellRenderer extends GrayableCellRenderer {
-
- private static final long serialVersionUID = 1L;
- static final ImageIcon checkedIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/tick.png", false));
- static final ImageIcon warningIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/warning16.png", false));
-
- @Override
- public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
- setHorizontalAlignment(CENTER);
- if ((value instanceof Boolean)) {
- if (true == (Boolean) value) {
- setIcon(warningIcon);
- setToolTipText(org.openide.util.NbBundle.getMessage(CaseStatusIconCellRenderer.class, "CaseStatusIconCellRenderer.tooltiptext.warning"));
- } else {
- setIcon(checkedIcon);
- setToolTipText(org.openide.util.NbBundle.getMessage(CaseStatusIconCellRenderer.class, "CaseStatusIconCellRenderer.tooltiptext.ok"));
- }
- }
- grayCellIfTableNotEnabled(table, isSelected);
-
- return this;
- }
-}
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/PathUtils.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/PathUtils.java
index 18ce9709d5..a7d5675883 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/PathUtils.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/PathUtils.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
+import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
final class PathUtils {
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/SingleUserCaseImporter.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/SingleUserCaseImporter.java
index e04399d321..3a5c45632f 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/SingleUserCaseImporter.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/SingleUserCaseImporter.java
@@ -47,6 +47,7 @@ import org.sleuthkit.autopsy.casemodule.SingleUserCaseConverter;
import org.sleuthkit.autopsy.casemodule.SingleUserCaseConverter.ImportCaseData;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
+import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
public class SingleUserCaseImporter implements Runnable {
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/layer.xml b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/layer.xml
index 56ad444eb0..ced44d8569 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/layer.xml
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/layer.xml
@@ -2,20 +2,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -23,19 +9,12 @@
-
-
-
-
-
-
-
-
+
-
+
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.form b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.form
index 20c8c7a981..68002e8055 100755
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.form
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.form
@@ -1,10 +1,6 @@