mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
5129 resolve merge conflicts
This commit is contained in:
commit
1f3bf30e64
@ -18,13 +18,10 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.communications.relationships;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||
@ -40,7 +37,6 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TimeUtilities;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.communications.Utils;
|
||||
@ -72,7 +68,6 @@ final class MessageNode extends BlackboardArtifactNode {
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
List<Tag> tags = getAllTagsFromDatabase();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
@ -81,17 +76,6 @@ final class MessageNode extends BlackboardArtifactNode {
|
||||
|
||||
sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS
|
||||
|
||||
addScoreProperty(sheetSet, tags);
|
||||
|
||||
CorrelationAttributeInstance correlationAttribute = null;
|
||||
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
|
||||
correlationAttribute = getCorrelationAttributeInstance();
|
||||
}
|
||||
addCommentProperty(sheetSet, tags, correlationAttribute);
|
||||
|
||||
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
|
||||
addCountProperty(sheetSet, correlationAttribute);
|
||||
}
|
||||
final BlackboardArtifact artifact = getArtifact();
|
||||
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
|
||||
|
@ -23,6 +23,7 @@ import org.openide.nodes.Node;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
|
||||
/**
|
||||
* A DataContentViewer that displays text with the TextViewers available.
|
||||
@ -90,6 +91,17 @@ public class TextContentViewer implements DataContentViewer {
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
// get the node's File, if it has one
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// disable the text content viewer for directories and empty files
|
||||
if (file.isDir() || file.getSize() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return panel.isSupported(node);
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ public class TextContentViewerPanel extends javax.swing.JPanel implements DataCo
|
||||
}
|
||||
|
||||
/**
|
||||
* Deterime wether the content viewer which displays this panel isSupported.
|
||||
* Determine whether the content viewer which displays this panel isSupported.
|
||||
* This panel is supported if any of the TextViewer's displayed in it are
|
||||
* supported.
|
||||
*
|
||||
|
@ -217,3 +217,4 @@ DataResultViewerTable.pageNumLabel.text=
|
||||
DataResultViewerTable.pageLabel.text=Page:
|
||||
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
|
||||
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
|
||||
DataResultViewerTable.exportCSVButton.text=Save table as CSV
|
||||
|
@ -32,6 +32,7 @@ DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found
|
||||
DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s)
|
||||
DataResultViewerTable.countRender.name=O
|
||||
DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository
|
||||
DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export
|
||||
DataResultViewerTable.firstColLbl=Name
|
||||
DataResultViewerTable.goToPageTextField.err=Invalid page number
|
||||
# {0} - totalPages
|
||||
@ -270,3 +271,4 @@ DataResultViewerTable.pageNumLabel.text=
|
||||
DataResultViewerTable.pageLabel.text=Page:
|
||||
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
|
||||
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
|
||||
DataResultViewerTable.exportCSVButton.text=Save table as CSV
|
||||
|
@ -16,13 +16,13 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="outlineView" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace pref="608" max="32767" attributes="0"/>
|
||||
<Component id="outlineView" pref="904" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="pageLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="pageNumLabel" min="-2" pref="53" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
|
||||
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="pagePrevButton" linkSize="1" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
@ -32,14 +32,15 @@
|
||||
<Component id="gotoPageLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="gotoPageTextField" min="-2" pref="33" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="exportCSVButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="2" attributes="0">
|
||||
<Component id="pageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="pageNumLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
@ -48,9 +49,10 @@
|
||||
<Component id="pageNextButton" linkSize="2" alignment="2" min="-2" pref="15" max="-2" attributes="0"/>
|
||||
<Component id="gotoPageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="gotoPageTextField" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="exportCSVButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="outlineView" pref="324" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
<Component id="outlineView" pref="321" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -164,5 +166,15 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="gotoPageTextFieldActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="exportCSVButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.exportCSVButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exportCSVButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -77,6 +77,7 @@ import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
|
||||
@ -176,6 +177,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
initComponents();
|
||||
|
||||
initializePagingSupport();
|
||||
|
||||
/*
|
||||
* Disable the CSV export button for the common properties results
|
||||
*/
|
||||
if (this instanceof org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributesSearchResultsViewerTable) {
|
||||
exportCSVButton.setEnabled(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the child OutlineView (explorer view) component.
|
||||
@ -1291,6 +1299,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);
|
||||
gotoPageLabel = new javax.swing.JLabel();
|
||||
gotoPageTextField = new javax.swing.JTextField();
|
||||
exportCSVButton = new javax.swing.JButton();
|
||||
|
||||
pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N
|
||||
|
||||
@ -1338,17 +1347,24 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
}
|
||||
});
|
||||
|
||||
exportCSVButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.exportCSVButton.text")); // NOI18N
|
||||
exportCSVButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
exportCSVButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap(608, Short.MAX_VALUE)
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(pageLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGap(14, 14, 14)
|
||||
.addComponent(pagesLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
@ -1358,7 +1374,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
.addComponent(gotoPageLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(exportCSVButton))
|
||||
);
|
||||
|
||||
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
|
||||
@ -1366,7 +1383,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGap(3, 3, 3)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
||||
.addComponent(pageLabel)
|
||||
.addComponent(pageNumLabel)
|
||||
@ -1374,9 +1391,10 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(gotoPageLabel)
|
||||
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 324, Short.MAX_VALUE)
|
||||
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(exportCSVButton))
|
||||
.addGap(3, 3, 3)
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 321, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
@ -1397,7 +1415,19 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
pagingSupport.gotoPage();
|
||||
}//GEN-LAST:event_gotoPageTextFieldActionPerformed
|
||||
|
||||
@NbBundle.Messages({"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export"
|
||||
})
|
||||
private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed
|
||||
Node currentRoot = this.getExplorerManager().getRootContext();
|
||||
if (currentRoot != null && currentRoot.getChildren().getNodesCount() > 0) {
|
||||
org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(currentRoot.getChildren().getNodes()), this);
|
||||
} else {
|
||||
MessageNotifyUtil.Message.info(Bundle.DataResultViewerTable_exportCSVButtonActionPerformed_empty());
|
||||
}
|
||||
}//GEN-LAST:event_exportCSVButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton exportCSVButton;
|
||||
private javax.swing.JLabel gotoPageLabel;
|
||||
private javax.swing.JTextField gotoPageTextField;
|
||||
private org.openide.explorer.view.OutlineView outlineView;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.ref.WeakReference;
|
||||
@ -27,8 +26,6 @@ import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
@ -65,6 +62,7 @@ import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
@ -76,15 +74,10 @@ import org.sleuthkit.datamodel.TskData;
|
||||
public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends AbstractContentNode<T> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AbstractAbstractFileNode.class.getName());
|
||||
@NbBundle.Messages("AbstractAbstractFileNode.addFileProperty.desc=no description")
|
||||
private static final String NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc();
|
||||
|
||||
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
|
||||
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED);
|
||||
|
||||
private static final ExecutorService translationPool;
|
||||
private static final Integer MAX_POOL_SIZE = 10;
|
||||
|
||||
/**
|
||||
* @param abstractFile file to wrap
|
||||
*/
|
||||
@ -101,7 +94,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
}
|
||||
|
||||
if (UserPreferences.displayTranslatedFileNames()) {
|
||||
AbstractAbstractFileNode.translationPool.submit(new TranslationTask(
|
||||
backgroundTasksPool.submit(new TranslationTask(
|
||||
new WeakReference<>(this), weakPcl));
|
||||
}
|
||||
|
||||
@ -109,14 +102,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
// or when tags are added.
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
|
||||
}
|
||||
|
||||
static {
|
||||
//Initialize this pool only once! This will be used by every instance of AAFN
|
||||
//to do their heavy duty SCO column and translation updates.
|
||||
translationPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
|
||||
new ThreadFactoryBuilder().setNameFormat("translation-task-thread-%d").build());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The finalizer removes event listeners as the BlackboardArtifactNode is
|
||||
* being garbage collected. Yes, we know that finalizers are considered to
|
||||
@ -137,16 +123,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event signals to indicate the background tasks have completed processing.
|
||||
* Currently, we have one property task in the background:
|
||||
*
|
||||
* 1) Retreiving the translation of the file name
|
||||
*/
|
||||
enum NodeSpecificEvents {
|
||||
TRANSLATION_AVAILABLE,
|
||||
}
|
||||
|
||||
|
||||
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
||||
String eventType = evt.getPropertyName();
|
||||
|
||||
@ -190,7 +167,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
|
||||
ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
|
||||
if (event.getAddedTag().getContent().equals(content)) {
|
||||
List<ContentTag> tags = getContentTagsFromDatabase();
|
||||
List<Tag> tags = this.getAllTagsFromDatabase();
|
||||
Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
|
||||
Score value = scorePropAndDescr.getLeft();
|
||||
String descr = scorePropAndDescr.getRight();
|
||||
@ -202,7 +179,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
|
||||
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
|
||||
if (event.getDeletedTagInfo().getContentID() == content.getId()) {
|
||||
List<ContentTag> tags = getContentTagsFromDatabase();
|
||||
List<Tag> tags = getAllTagsFromDatabase();
|
||||
Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
|
||||
Score value = scorePropAndDescr.getLeft();
|
||||
String descr = scorePropAndDescr.getRight();
|
||||
@ -214,7 +191,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
} else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
|
||||
CommentChangedEvent event = (CommentChangedEvent) evt;
|
||||
if (event.getContentID() == content.getId()) {
|
||||
List<ContentTag> tags = getContentTagsFromDatabase();
|
||||
List<Tag> tags = getAllTagsFromDatabase();
|
||||
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
|
||||
updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(tags, attribute)));
|
||||
}
|
||||
@ -223,6 +200,18 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
//Set the tooltip
|
||||
this.setShortDescription(content.getName());
|
||||
updateSheet(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, content.getName()));
|
||||
} else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString())) {
|
||||
SCOData scoData = (SCOData)evt.getNewValue();
|
||||
if (scoData.getScoreAndDescription() != null) {
|
||||
updateSheet(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
|
||||
}
|
||||
if (scoData.getComment() != null) {
|
||||
updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, scoData.getComment()));
|
||||
}
|
||||
if (scoData.getCountAndDescription() != null &&
|
||||
!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
|
||||
updateSheet(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
@ -235,38 +224,6 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
*/
|
||||
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
|
||||
|
||||
/**
|
||||
* Updates the values of the properties in the current property sheet with
|
||||
* the new properties being passed in. Only if that property exists in the
|
||||
* current sheet will it be applied. That way, we allow for subclasses to
|
||||
* add their own (or omit some!) properties and we will not accidentally
|
||||
* disrupt their UI.
|
||||
*
|
||||
* Race condition if not synchronized. Only one update should be applied at
|
||||
* a time.
|
||||
*
|
||||
* @param newProps New file property instances to be updated in the current
|
||||
* sheet.
|
||||
*/
|
||||
private synchronized void updateSheet(NodeProperty<?>... newProps) {
|
||||
//Refresh ONLY those properties in the sheet currently. Subclasses may have
|
||||
//only added a subset of our properties or their own props.s
|
||||
Sheet visibleSheet = this.getSheet();
|
||||
Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
|
||||
Property<?>[] visibleProps = visibleSheetSet.getProperties();
|
||||
for (NodeProperty<?> newProp : newProps) {
|
||||
for (int i = 0; i < visibleProps.length; i++) {
|
||||
if (visibleProps[i].getName().equals(newProp.getName())) {
|
||||
visibleProps[i] = newProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
visibleSheetSet.put(visibleProps);
|
||||
visibleSheet.put(visibleSheetSet);
|
||||
//setSheet() will notify Netbeans to update this node in the UI.
|
||||
this.setSheet(visibleSheet);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when the node is first initialized. Any new updates or
|
||||
* changes happen by directly manipulating the sheet. That means we can fire
|
||||
@ -368,18 +325,18 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
properties.add(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, ""));
|
||||
}
|
||||
|
||||
//SCO column prereq info..
|
||||
List<ContentTag> tags = getContentTagsFromDatabase();
|
||||
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
|
||||
|
||||
Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
|
||||
properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
|
||||
DataResultViewerTable.HasCommentStatus comment = getCommentProperty(tags, attribute);
|
||||
properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, comment));
|
||||
if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
|
||||
Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute);
|
||||
properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), countAndDescription.getRight(), countAndDescription.getLeft()));
|
||||
// Create place holders for S C O
|
||||
properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), VALUE_LOADING, ""));
|
||||
properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), VALUE_LOADING, ""));
|
||||
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
|
||||
properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), VALUE_LOADING, ""));
|
||||
}
|
||||
|
||||
|
||||
// Get the SCO columns data in a background task
|
||||
backgroundTasksPool.submit(new GetSCOTask(
|
||||
new WeakReference<>(this), weakPcl));
|
||||
|
||||
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
|
||||
properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content)));
|
||||
properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content)));
|
||||
@ -441,7 +398,8 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
"AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated",
|
||||
"# {0} - occuranceCount",
|
||||
"AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
|
||||
Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
|
||||
@Override
|
||||
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
|
||||
Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
|
||||
String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description();
|
||||
try {
|
||||
@ -468,7 +426,8 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
"AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.",
|
||||
"AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.",
|
||||
"AbstractAbstractFileNode.createSheet.noScore.description=No score"})
|
||||
Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<ContentTag> tags) {
|
||||
@Override
|
||||
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
|
||||
DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE;
|
||||
String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description();
|
||||
if (content.getKnown() == TskData.FileKnown.BAD) {
|
||||
@ -486,7 +445,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) {
|
||||
score = DataResultViewerTable.Score.INTERESTING_SCORE;
|
||||
description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description();
|
||||
for (ContentTag tag : tags) {
|
||||
for (Tag tag : tags) {
|
||||
if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
|
||||
score = DataResultViewerTable.Score.NOTABLE_SCORE;
|
||||
description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description();
|
||||
@ -499,11 +458,12 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
|
||||
@NbBundle.Messages({
|
||||
"AbstractAbstractFileNode.createSheet.comment.displayName=C"})
|
||||
HasCommentStatus getCommentProperty(List<ContentTag> tags, CorrelationAttributeInstance attribute) {
|
||||
@Override
|
||||
protected HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
|
||||
|
||||
DataResultViewerTable.HasCommentStatus status = !tags.isEmpty() ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT;
|
||||
|
||||
for (ContentTag tag : tags) {
|
||||
for (Tag tag : tags) {
|
||||
if (!StringUtils.isBlank(tag.getComment())) {
|
||||
//if the tag is null or empty or contains just white space it will indicate there is not a comment
|
||||
status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT;
|
||||
@ -571,7 +531,13 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
return tags;
|
||||
}
|
||||
|
||||
CorrelationAttributeInstance getCorrelationAttributeInstance() {
|
||||
@Override
|
||||
protected List<Tag> getAllTagsFromDatabase() {
|
||||
return new ArrayList<>(getContentTagsFromDatabase());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
|
||||
CorrelationAttributeInstance attribute = null;
|
||||
if (EamDb.isEnabled() && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
|
||||
attribute = EamArtifactUtil.getInstanceFromContent(content);
|
||||
|
@ -18,20 +18,29 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Sheet;
|
||||
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
@ -50,6 +59,38 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
|
||||
T content;
|
||||
private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName());
|
||||
|
||||
/**
|
||||
* A pool of background tasks to run any long computation needed to
|
||||
* populate this node.
|
||||
*/
|
||||
static final ExecutorService backgroundTasksPool;
|
||||
private static final Integer MAX_POOL_SIZE = 10;
|
||||
|
||||
/**
|
||||
* Default no description string
|
||||
*/
|
||||
@NbBundle.Messages({"AbstractContentNode.nodescription=no description",
|
||||
"AbstractContentNode.valueLoading=value loading"})
|
||||
protected static final String NO_DESCR = Bundle.AbstractContentNode_nodescription();
|
||||
protected static final String VALUE_LOADING = Bundle.AbstractContentNode_valueLoading();
|
||||
|
||||
/**
|
||||
* Event signals to indicate the background tasks have completed processing.
|
||||
* Currently, we have one property task in the background:
|
||||
*
|
||||
* 1) Retrieving the translation of the file name
|
||||
*/
|
||||
enum NodeSpecificEvents {
|
||||
TRANSLATION_AVAILABLE,
|
||||
SCO_AVAILABLE
|
||||
}
|
||||
|
||||
static {
|
||||
//Initialize this pool only once! This will be used by every instance of AAFN
|
||||
//to do their heavy duty SCO column and translation updates.
|
||||
backgroundTasksPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
|
||||
new ThreadFactoryBuilder().setNameFormat("content-node-background-task-%d").build());
|
||||
}
|
||||
/**
|
||||
* Handles aspects that depend on the Content object
|
||||
*
|
||||
@ -240,4 +281,79 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
|
||||
public int read(byte[] buf, long offset, long len) throws TskException {
|
||||
return content.read(buf, offset, len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the values of the properties in the current property sheet with
|
||||
* the new properties being passed in. Only if that property exists in the
|
||||
* current sheet will it be applied. That way, we allow for subclasses to
|
||||
* add their own (or omit some!) properties and we will not accidentally
|
||||
* disrupt their UI.
|
||||
*
|
||||
* Race condition if not synchronized. Only one update should be applied at
|
||||
* a time.
|
||||
*
|
||||
* @param newProps New file property instances to be updated in the current
|
||||
* sheet.
|
||||
*/
|
||||
protected synchronized void updateSheet(NodeProperty<?>... newProps) {
|
||||
//Refresh ONLY those properties in the sheet currently. Subclasses may have
|
||||
//only added a subset of our properties or their own props.s
|
||||
Sheet visibleSheet = this.getSheet();
|
||||
Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
|
||||
Property<?>[] visibleProps = visibleSheetSet.getProperties();
|
||||
for (NodeProperty<?> newProp : newProps) {
|
||||
for (int i = 0; i < visibleProps.length; i++) {
|
||||
if (visibleProps[i].getName().equals(newProp.getName())) {
|
||||
visibleProps[i] = newProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
visibleSheetSet.put(visibleProps);
|
||||
visibleSheet.put(visibleSheetSet);
|
||||
//setSheet() will notify Netbeans to update this node in the UI.
|
||||
this.setSheet(visibleSheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns a list of all tags associated with this content node.
|
||||
*
|
||||
* @return list of tags associated with the node.
|
||||
*/
|
||||
abstract protected List<Tag> getAllTagsFromDatabase();
|
||||
|
||||
/**
|
||||
* Returns correlation attribute instance for the underlying content of the node.
|
||||
*
|
||||
* @return correlation attribute instance for the underlying content of the node.
|
||||
*/
|
||||
abstract protected CorrelationAttributeInstance getCorrelationAttributeInstance();
|
||||
|
||||
/**
|
||||
* Returns Score property for the node.
|
||||
*
|
||||
* @param tags list of tags.
|
||||
*
|
||||
* @return Score property for the underlying content of the node.
|
||||
*/
|
||||
abstract protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags);
|
||||
|
||||
/**
|
||||
* Returns comment property for the node.
|
||||
*
|
||||
* @param tags list of tags
|
||||
* @param attribute correlation attribute instance
|
||||
*
|
||||
* @return Comment property for the underlying content of the node.
|
||||
*/
|
||||
abstract protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute);
|
||||
|
||||
/**
|
||||
* Returns occurrences/count property for the node.
|
||||
*
|
||||
* @param attribute correlation attribute instance
|
||||
*
|
||||
* @return count property for the underlying content of the node.
|
||||
*/
|
||||
abstract protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -22,6 +22,7 @@ import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -37,6 +38,7 @@ import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.Action;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -55,11 +57,13 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
|
||||
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
|
||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager;
|
||||
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
|
||||
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
|
||||
@ -95,10 +99,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
private Content associated = null;
|
||||
|
||||
private List<NodeProperty<? extends Object>> customProperties;
|
||||
|
||||
private final static String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text");
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Artifact types which should have the full unique path of the associated
|
||||
* content as a property.
|
||||
@ -151,6 +152,19 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
contentCache.invalidateAll();
|
||||
}
|
||||
}
|
||||
else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString())) {
|
||||
SCOData scoData = (SCOData)evt.getNewValue();
|
||||
if (scoData.getScoreAndDescription() != null) {
|
||||
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
|
||||
}
|
||||
if (scoData.getComment() != null) {
|
||||
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, scoData.getComment()));
|
||||
}
|
||||
if (scoData.getCountAndDescription() != null &&
|
||||
!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
|
||||
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -335,8 +349,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
List<Tag> tags = getAllTagsFromDatabase();
|
||||
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
@ -351,17 +363,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
NO_DESCR,
|
||||
this.getSourceName()));
|
||||
|
||||
addScoreProperty(sheetSet, tags);
|
||||
|
||||
CorrelationAttributeInstance correlationAttribute = null;
|
||||
// Create place holders for S C O
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), VALUE_LOADING, ""));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, ""));
|
||||
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
|
||||
correlationAttribute = getCorrelationAttributeInstance();
|
||||
}
|
||||
addCommentProperty(sheetSet, tags, correlationAttribute);
|
||||
|
||||
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
|
||||
addCountProperty(sheetSet, correlationAttribute);
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, ""));
|
||||
}
|
||||
|
||||
// Get the SCO columns data in a background task
|
||||
backgroundTasksPool.submit(new GetSCOTask(
|
||||
new WeakReference<>(this), weakPcl));
|
||||
|
||||
if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
|
||||
try {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
|
||||
@ -520,6 +532,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* @return a list of tags which on the artifact or the file it is associated
|
||||
* with
|
||||
*/
|
||||
@Override
|
||||
protected final List<Tag> getAllTagsFromDatabase() {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
try {
|
||||
@ -569,6 +582,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
|
||||
CorrelationAttributeInstance correlationAttribute = null;
|
||||
if (EamDb.isEnabled()) {
|
||||
@ -581,16 +595,36 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* Used by (subclasses of) BlackboardArtifactNode to add the comment
|
||||
* property to their sheets.
|
||||
*
|
||||
* @param sheetSet the modifiable Sheet.Set returned by
|
||||
* Sheet.get(Sheet.PROPERTIES)
|
||||
* @param sheetSet the modifiable Sheet.Set to add the property to
|
||||
* @param tags the list of tags associated with the file
|
||||
* @param attribute the correlation attribute associated with this
|
||||
* artifact's associated file, null if central repo is not
|
||||
* enabled
|
||||
*
|
||||
* @deprecated Use the GetSCOTask to get this data on a background thread...,
|
||||
* and then update the property sheet asynchronously
|
||||
*/
|
||||
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
|
||||
"BlackboardArtifactNode.createSheet.comment.displayName=C"})
|
||||
@Deprecated
|
||||
protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
|
||||
HasCommentStatus status = getCommentProperty(tags, attribute );
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
|
||||
status));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the comment property for the node
|
||||
*
|
||||
* @param tags the list of tags associated with the file
|
||||
* @param attribute the correlation attribute associated with this
|
||||
* artifact's associated file, null if central repo is not
|
||||
* enabled
|
||||
* @return comment property
|
||||
*/
|
||||
@Override
|
||||
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
|
||||
|
||||
HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT;
|
||||
for (Tag tag : tags) {
|
||||
if (!StringUtils.isBlank(tag.getComment())) {
|
||||
@ -609,17 +643,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
status = HasCommentStatus.CR_COMMENT;
|
||||
}
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
|
||||
status));
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by (subclasses of) BlackboardArtifactNode to add the Score property
|
||||
* to their sheets.
|
||||
*
|
||||
* @param sheetSet the modifiable Sheet.Set returned by
|
||||
* Sheet.get(Sheet.PROPERTIES)
|
||||
* @param sheetSet the modifiable Sheet.Set to add the property to
|
||||
* @param tags the list of tags associated with the file
|
||||
*
|
||||
* @deprecated Use the GetSCOTask to get this data on a background thread...,
|
||||
* and then update the property sheet asynchronously
|
||||
*/
|
||||
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
|
||||
"BlackboardArtifactNode.createSheet.score.displayName=S",
|
||||
@ -628,8 +662,22 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
"BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
|
||||
"BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
|
||||
"BlackboardArtifactNode.createSheet.noScore.description=No score"})
|
||||
protected final void addScoreProperty(Sheet.Set sheetSet, List<Tag> tags) {
|
||||
Score score = Score.NO_SCORE;
|
||||
@Deprecated
|
||||
protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
|
||||
Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the score property for the node.
|
||||
*
|
||||
* @param tags the list of tags associated with the file
|
||||
*
|
||||
* @return score property and description
|
||||
*/
|
||||
@Override
|
||||
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
|
||||
Score score = Score.NO_SCORE;
|
||||
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
|
||||
if (associated instanceof AbstractFile) {
|
||||
if (((AbstractFile) associated).getKnown() == TskData.FileKnown.BAD) {
|
||||
@ -673,18 +721,43 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
}
|
||||
}
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), description, score));
|
||||
|
||||
return Pair.of(score, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by (subclasses of) BlackboardArtifactNode to add the Occurrences property
|
||||
* to their sheets.
|
||||
*
|
||||
* @param sheetSet the modifiable Sheet.Set to add the property to
|
||||
* @param attribute correlation attribute instance
|
||||
*
|
||||
* @deprecated Use the GetSCOTask to get this data on a background thread...,
|
||||
* and then update the property sheet asynchronously
|
||||
*/
|
||||
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
|
||||
"BlackboardArtifactNode.createSheet.count.displayName=O",
|
||||
"BlackboardArtifactNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
|
||||
"BlackboardArtifactNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this artifact's associated file when the column was populated",
|
||||
"# {0} - occuranceCount",
|
||||
"BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
|
||||
|
||||
@Deprecated
|
||||
protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
|
||||
Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
|
||||
Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute);
|
||||
sheetSet.put(
|
||||
new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Occurrences property for the node.
|
||||
*
|
||||
* @param attribute correlation attribute instance
|
||||
*
|
||||
* @return count and description
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
|
||||
Long count = -1L;
|
||||
String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description();
|
||||
try {
|
||||
//don't perform the query if there is no correlation value
|
||||
@ -699,14 +772,13 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
|
||||
}
|
||||
sheetSet.put(
|
||||
new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), description, count));
|
||||
}
|
||||
|
||||
return Pair.of(count, description);
|
||||
}
|
||||
|
||||
private void updateSheet() {
|
||||
this.setSheet(createSheet());
|
||||
}
|
||||
|
||||
|
||||
private String getRootParentName() {
|
||||
String parentName = associated.getName();
|
||||
Content parent = associated;
|
||||
|
@ -1,5 +1,4 @@
|
||||
AbstractAbstractFileNode.accessTimeColLbl=Access Time
|
||||
AbstractAbstractFileNode.addFileProperty.desc=no description
|
||||
AbstractAbstractFileNode.attrAddrColLbl=Attr. Addr.
|
||||
AbstractAbstractFileNode.changeTimeColLbl=Change Time
|
||||
AbstractAbstractFileNode.createdTimeColLbl=Created Time
|
||||
@ -37,6 +36,8 @@ AbstractAbstractFileNode.tagsProperty.displayName=Tags
|
||||
AbstractAbstractFileNode.typeDirColLbl=Type(Dir)
|
||||
AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)
|
||||
AbstractAbstractFileNode.useridColLbl=UserID
|
||||
AbstractContentNode.nodescription=no description
|
||||
AbstractContentNode.valueLoading=value loading
|
||||
AbstractFsContentNode.noDesc.text=no description
|
||||
ArtifactStringContent.attrsTableHeader.sources=Source(s)
|
||||
ArtifactStringContent.attrsTableHeader.type=Type
|
||||
|
@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.actions.ReplaceBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.ReplaceContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.datamodel.Reports.ReportNode;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
@ -92,6 +93,7 @@ public class DataModelActionsFactory {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -119,6 +121,7 @@ public class DataModelActionsFactory {
|
||||
actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, slackFileNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -155,6 +158,7 @@ public class DataModelActionsFactory {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());//
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -189,6 +193,7 @@ public class DataModelActionsFactory {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -223,6 +228,7 @@ public class DataModelActionsFactory {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -257,6 +263,7 @@ public class DataModelActionsFactory {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -291,6 +298,7 @@ public class DataModelActionsFactory {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -325,6 +333,7 @@ public class DataModelActionsFactory {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -379,6 +388,7 @@ public class DataModelActionsFactory {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -415,6 +425,7 @@ public class DataModelActionsFactory {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
|
@ -28,6 +28,7 @@ import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
|
||||
@ -89,6 +90,7 @@ public class DirectoryNode extends AbstractFsContentNode<AbstractFile> {
|
||||
actionsList.add(ViewFileInTimelineAction.createViewFileAction(content));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(new RunIngestModulesAction(content));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
|
@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
@ -173,6 +174,7 @@ public class FileNode extends AbstractFsContentNode<AbstractFile> {
|
||||
actionsList.add(null); // Creates an item separator
|
||||
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // Creates an item separator
|
||||
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
73
Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java
Normal file
73
Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
|
||||
/**
|
||||
* Background task to get Score, Comment and Occurrences values for an
|
||||
* Abstract content node.
|
||||
*
|
||||
*/
|
||||
class GetSCOTask implements Runnable {
|
||||
|
||||
private final WeakReference<AbstractContentNode<?>> weakNodeRef;
|
||||
private final PropertyChangeListener listener;
|
||||
|
||||
GetSCOTask(WeakReference<AbstractContentNode<?>> weakContentRef, PropertyChangeListener listener) {
|
||||
this.weakNodeRef = weakContentRef;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
AbstractContentNode<?> contentNode = weakNodeRef.get();
|
||||
|
||||
//Check for stale reference
|
||||
if (contentNode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get the SCO column values
|
||||
List<Tag> tags = contentNode.getAllTagsFromDatabase();
|
||||
CorrelationAttributeInstance attribute = contentNode.getCorrelationAttributeInstance();
|
||||
|
||||
SCOData scoData = new SCOData();
|
||||
scoData.setScoreAndDescription(contentNode.getScorePropertyAndDescription(tags));
|
||||
scoData.setComment(contentNode.getCommentProperty(tags, attribute));
|
||||
if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
|
||||
scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(attribute));
|
||||
}
|
||||
|
||||
// signal SCO data is available.
|
||||
if (listener != null) {
|
||||
listener.propertyChange(new PropertyChangeEvent(
|
||||
AutopsyEvent.SourceType.LOCAL.toString(),
|
||||
AbstractAbstractFileNode.NodeSpecificEvents.SCO_AVAILABLE.toString(),
|
||||
null, scoData));
|
||||
}
|
||||
}
|
||||
}
|
@ -28,12 +28,15 @@ import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.Action;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
||||
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
|
||||
@ -47,6 +50,7 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
|
||||
/**
|
||||
* This class is used to represent the "Node" for the image. The children of
|
||||
@ -250,4 +254,67 @@ public class ImageNode extends AbstractContentNode<Image> {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads and returns a list of all tags associated with this content node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @return list of tags associated with the node.
|
||||
*/
|
||||
@Override
|
||||
protected List<Tag> getAllTagsFromDatabase() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
/**
|
||||
* Returns correlation attribute instance for the underlying content of the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @return correlation attribute instance for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Score property for the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @param tags list of tags.
|
||||
*
|
||||
* @return Score property for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
|
||||
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
|
||||
}
|
||||
/**
|
||||
* Returns comment property for the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @param tags list of tags
|
||||
* @param attribute correlation attribute instance
|
||||
*
|
||||
* @return Comment property for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
|
||||
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
|
||||
}
|
||||
/**
|
||||
* Returns occurrences/count property for the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @param attribute correlation attribute instance
|
||||
*
|
||||
* @return count property for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
|
||||
return Pair.of(-1L, NO_DESCR);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
@ -105,6 +106,7 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
||||
|
@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
@ -82,6 +83,7 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
|
||||
actionsList.add(null); // creates a menu separator
|
||||
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
||||
|
56
Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java
Normal file
56
Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
|
||||
/**
|
||||
* Container to bag the S C & O data for an abstract file node.
|
||||
*
|
||||
*/
|
||||
class SCOData {
|
||||
|
||||
private Pair<DataResultViewerTable.Score, String> scoreAndDescription = null;
|
||||
private DataResultViewerTable.HasCommentStatus comment = null;
|
||||
private Pair<Long, String> countAndDescription = null;
|
||||
|
||||
Pair<DataResultViewerTable.Score, String> getScoreAndDescription() {
|
||||
return scoreAndDescription;
|
||||
}
|
||||
|
||||
DataResultViewerTable.HasCommentStatus getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
Pair<Long, String> getCountAndDescription() {
|
||||
return countAndDescription;
|
||||
}
|
||||
|
||||
void setScoreAndDescription(Pair<DataResultViewerTable.Score, String> scoreAndDescription) {
|
||||
this.scoreAndDescription = scoreAndDescription;
|
||||
}
|
||||
void setComment(DataResultViewerTable.HasCommentStatus comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
void setCountAndDescription(Pair<Long, String> countAndDescription) {
|
||||
this.countAndDescription = countAndDescription;
|
||||
}
|
||||
|
||||
}
|
@ -28,6 +28,7 @@ import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
|
||||
@ -85,6 +86,7 @@ public class SlackFileNode extends AbstractFsContentNode<AbstractFile> {
|
||||
NbBundle.getMessage(this.getClass(), "SlackFileNode.getActions.viewInNewWin.text"), this));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
||||
|
@ -25,6 +25,7 @@ import javax.swing.Action;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
@ -61,6 +62,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<Spec
|
||||
Bundle.SpecialDirectoryNode_getActions_viewInNewWin_text(), this));
|
||||
actions.add(null); // creates a menu separator
|
||||
actions.add(ExtractAction.getInstance());
|
||||
actions.add(ExportCSVAction.getInstance());
|
||||
actions.add(null); // creates a menu separator
|
||||
actions.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
|
||||
if (content.isDataSource()) {
|
||||
|
@ -25,10 +25,14 @@ import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.Action;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
|
||||
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
|
||||
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
@ -39,6 +43,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
import org.sleuthkit.datamodel.Volume;
|
||||
import org.sleuthkit.autopsy.directorytree.FileSystemDetailsAction;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
|
||||
/**
|
||||
* This class is used to represent the "Node" for the volume. Its child is the
|
||||
@ -212,4 +217,67 @@ public class VolumeNode extends AbstractContentNode<Volume> {
|
||||
public String getItemType() {
|
||||
return DisplayableItemNode.FILE_PARENT_NODE_KEY;
|
||||
}
|
||||
/**
|
||||
* Reads and returns a list of all tags associated with this content node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @return list of tags associated with the node.
|
||||
*/
|
||||
@Override
|
||||
protected List<Tag> getAllTagsFromDatabase() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
/**
|
||||
* Returns correlation attribute instance for the underlying content of the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @return correlation attribute instance for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Score property for the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @param tags list of tags.
|
||||
*
|
||||
* @return Score property for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
|
||||
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
|
||||
}
|
||||
/**
|
||||
* Returns comment property for the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @param tags list of tags
|
||||
* @param attribute correlation attribute instance
|
||||
*
|
||||
* @return Comment property for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
|
||||
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
|
||||
}
|
||||
/**
|
||||
* Returns occurrences/count property for the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @param attribute correlation attribute instance
|
||||
*
|
||||
* @return count property for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
|
||||
return Pair.of(-1L, NO_DESCR);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
CSVWriter.done.notifyMsg.error=Error exporting to CSV file
|
||||
# {0} - Output file
|
||||
CSVWriter.done.notifyMsg.success=Wrote to {0}
|
||||
CSVWriter.progress.cancelling=Cancelling
|
||||
CSVWriter.progress.extracting=Exporting to CSV file
|
||||
CTL_DirectoryTreeTopComponent=Directory Tree
|
||||
DataResultFilterNode.viewSourceArtifact.text=View Source Result
|
||||
# {0} - dataSourceCount
|
||||
@ -5,6 +10,11 @@ DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contai
|
||||
DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?
|
||||
DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.
|
||||
DirectoryTreeTopComponent.resultsView.title=Listing
|
||||
ExportCSV.saveNodesToCSV.empty=No data to export
|
||||
# {0} - Output file
|
||||
ExportCSV.saveNodesToCSV.fileExists=File {0} already exists
|
||||
ExportCSV.saveNodesToCSV.noCurrentCase=No open case available
|
||||
ExportCSV.title.text=Export selected rows to CSV
|
||||
ExternalViewerAction.actionPerformed.failure.exe.message=The file is an executable and will not be opened.
|
||||
ExternalViewerAction.actionPerformed.failure.IO.message=There is no associated editor for files of this type or the associated application failed to launch.
|
||||
ExternalViewerAction.actionPerformed.failure.missingFile.message=The file no longer exists.
|
||||
|
@ -372,6 +372,7 @@ public class DataResultFilterNode extends FilterNode {
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
actionsList.add(AddBlackboardArtifactTagAction.getInstance());
|
||||
|
@ -125,6 +125,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
|
||||
}
|
||||
}
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.addAll(ContextMenuExtensionPoint.getActions());
|
||||
return actionsList;
|
||||
}
|
||||
@ -142,6 +143,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
|
||||
}
|
||||
}
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.addAll(ContextMenuExtensionPoint.getActions());
|
||||
return actionsList;
|
||||
}
|
||||
@ -150,6 +152,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
|
||||
public List<? extends Action> visit(final DerivedFile d) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
||||
final Collection<AbstractFile> selectedFilesList =
|
||||
@ -166,6 +169,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
|
||||
public List<? extends Action> visit(final LocalFile d) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
||||
final Collection<AbstractFile> selectedFilesList =
|
||||
@ -182,6 +186,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
|
||||
public List<? extends Action> visit(final org.sleuthkit.datamodel.File d) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
||||
final Collection<AbstractFile> selectedFilesList =
|
||||
|
@ -0,0 +1,360 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this content 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.directorytree;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.File;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.openide.util.Cancellable;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Node.PropertySet;
|
||||
import org.openide.nodes.Node.Property;
|
||||
|
||||
/**
|
||||
* Exports CSV version of result nodes to a location selected by the user.
|
||||
*/
|
||||
public final class ExportCSVAction extends AbstractAction {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ExportCSVAction.class.getName());
|
||||
private final static String DEFAULT_FILENAME = "Results";
|
||||
private final static List<String> columnsToSkip = Arrays.asList(AbstractFilePropertyType.SCORE.toString(),
|
||||
AbstractFilePropertyType.COMMENT.toString(), AbstractFilePropertyType.OCCURRENCES.toString());
|
||||
|
||||
private static String userDefinedExportPath;
|
||||
|
||||
// This class is a singleton to support multi-selection of nodes, since
|
||||
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
|
||||
// node in the array returns a reference to the same action object from Node.getActions(boolean).
|
||||
private static ExportCSVAction instance;
|
||||
|
||||
/**
|
||||
* Get an instance of the Action. See above for why
|
||||
* the class is a singleton.
|
||||
*
|
||||
* @return the instance
|
||||
*/
|
||||
public static synchronized ExportCSVAction getInstance() {
|
||||
if (null == instance) {
|
||||
instance = new ExportCSVAction();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor for the action.
|
||||
*/
|
||||
@NbBundle.Messages({"ExportCSV.title.text=Export selected rows to CSV"})
|
||||
private ExportCSVAction() {
|
||||
super(Bundle.ExportCSV_title_text());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks user to choose destination, then extracts content to destination
|
||||
* (recursing on directories).
|
||||
*
|
||||
* @param e The action event.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Collection<? extends Node> selectedNodes = Utilities.actionsGlobalContext().lookupAll(Node.class);
|
||||
saveNodesToCSV(selectedNodes, (Component)e.getSource());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the selected nodes to a CSV file
|
||||
*
|
||||
* @param nodesToExport the nodes to save
|
||||
* @param component
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - Output file",
|
||||
"ExportCSV.saveNodesToCSV.fileExists=File {0} already exists",
|
||||
"ExportCSV.saveNodesToCSV.noCurrentCase=No open case available",
|
||||
"ExportCSV.saveNodesToCSV.empty=No data to export"})
|
||||
public static void saveNodesToCSV(Collection<? extends Node> nodesToExport, Component component) {
|
||||
|
||||
if (nodesToExport.isEmpty()) {
|
||||
MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_empty());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Set up the file chooser with a default name and either the Export
|
||||
// folder or the last used folder.
|
||||
String fileName = getDefaultOutputFileName(nodesToExport.iterator().next().getParentNode());
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows())));
|
||||
fileChooser.setSelectedFile(new File(fileName));
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("csv file", "csv"));
|
||||
|
||||
int returnVal = fileChooser.showSaveDialog(component);
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
|
||||
// Get the file name, appending .csv if necessary
|
||||
File selectedFile = fileChooser.getSelectedFile();
|
||||
if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS
|
||||
selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS
|
||||
}
|
||||
|
||||
// Save the directory used for next time
|
||||
updateExportDirectory(selectedFile.getParent(), Case.getCurrentCaseThrows());
|
||||
|
||||
if (selectedFile.exists()) {
|
||||
logger.log(Level.SEVERE, "File {0} already exists", selectedFile.getAbsolutePath()); //NON-NLS
|
||||
MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_fileExists(selectedFile));
|
||||
return;
|
||||
}
|
||||
|
||||
CSVWriter writer = new CSVWriter(nodesToExport, selectedFile);
|
||||
writer.execute();
|
||||
}
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
JOptionPane.showMessageDialog(component, Bundle.ExportCSV_saveNodesToCSV_noCurrentCase());
|
||||
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default name for the CSV output.
|
||||
*
|
||||
* @param parent The parent node for the selected nodes
|
||||
*
|
||||
* @return the default name
|
||||
*/
|
||||
private static String getDefaultOutputFileName(Node parent) {
|
||||
String dateStr = String.format("%1$tY%1$tm%1$te%1$tI%1$tM%1$tS", Calendar.getInstance());
|
||||
|
||||
if (parent != null) {
|
||||
// The first value in the property set is generally a reasonable name
|
||||
for (PropertySet set : parent.getPropertySets()) {
|
||||
for (Property<?> prop : set.getProperties()) {
|
||||
try {
|
||||
String parentName = prop.getValue().toString();
|
||||
|
||||
// Strip off the count (if present)
|
||||
parentName = parentName.replaceAll("\\([0-9]+\\)$", "");
|
||||
|
||||
// Strip out any invalid characters
|
||||
parentName = parentName.replaceAll("[\\\\/:*?\"<>|]", "_");
|
||||
|
||||
return parentName + " " + dateStr;
|
||||
} catch (IllegalAccessException | InvocationTargetException ex) {
|
||||
logger.log(Level.WARNING, "Failed to get property set value as string", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return DEFAULT_FILENAME + " " + dateStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the export directory path.
|
||||
*
|
||||
* @param openCase The current case.
|
||||
*
|
||||
* @return The export directory path.
|
||||
*/
|
||||
private static String getExportDirectory(Case openCase) {
|
||||
String caseExportPath = openCase.getExportDirectory();
|
||||
|
||||
if (userDefinedExportPath == null) {
|
||||
return caseExportPath;
|
||||
}
|
||||
|
||||
File file = new File(userDefinedExportPath);
|
||||
if (file.exists() == false || file.isDirectory() == false) {
|
||||
return caseExportPath;
|
||||
}
|
||||
|
||||
return userDefinedExportPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the default export directory. If the directory path matches the
|
||||
* case export directory, then the directory used will always match the
|
||||
* export directory of any given case. Otherwise, the path last used will be
|
||||
* saved.
|
||||
*
|
||||
* @param exportPath The export path.
|
||||
* @param openCase The current case.
|
||||
*/
|
||||
private static void updateExportDirectory(String exportPath, Case openCase) {
|
||||
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
|
||||
userDefinedExportPath = null;
|
||||
} else {
|
||||
userDefinedExportPath = exportPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Thread that does the actual extraction work
|
||||
*/
|
||||
private static class CSVWriter extends SwingWorker<Object, Void> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CSVWriter.class.getName());
|
||||
private ProgressHandle progress;
|
||||
|
||||
private final Collection<? extends Node> nodesToExport;
|
||||
private final File outputFile;
|
||||
|
||||
/**
|
||||
* Create an instance of the CSVWriter.
|
||||
*
|
||||
* @param extractionTasks List of file extraction tasks.
|
||||
*/
|
||||
CSVWriter(Collection<? extends Node> nodesToExport, File outputFile) {
|
||||
this.nodesToExport = nodesToExport;
|
||||
this.outputFile = outputFile;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"CSVWriter.progress.extracting=Exporting to CSV file",
|
||||
"CSVWriter.progress.cancelling=Cancelling"})
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
if (nodesToExport.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set up progress bar.
|
||||
final String displayName = Bundle.CSVWriter_progress_extracting();
|
||||
progress = ProgressHandle.createHandle(displayName, new Cancellable() {
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
if (progress != null) {
|
||||
progress.setDisplayName(Bundle.CSVWriter_progress_cancelling());
|
||||
}
|
||||
return ExportCSVAction.CSVWriter.this.cancel(true);
|
||||
}
|
||||
});
|
||||
progress.start();
|
||||
progress.switchToIndeterminate();
|
||||
|
||||
try (BufferedWriter br = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8))) {
|
||||
// Write BOM
|
||||
br.write('\ufeff');
|
||||
|
||||
// Write the header
|
||||
List<String> headers = new ArrayList<>();
|
||||
PropertySet[] sets = nodesToExport.iterator().next().getPropertySets();
|
||||
for(PropertySet set : sets) {
|
||||
for (Property<?> prop : set.getProperties()) {
|
||||
if ( ! columnsToSkip.contains(prop.getDisplayName())) {
|
||||
headers.add(prop.getDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
br.write(listToCSV(headers));
|
||||
|
||||
// Write each line
|
||||
Iterator<?> nodeIterator = nodesToExport.iterator();
|
||||
while (nodeIterator.hasNext()) {
|
||||
if (this.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Node node = (Node)nodeIterator.next();
|
||||
List<String> values = new ArrayList<>();
|
||||
sets = node.getPropertySets();
|
||||
for(PropertySet set : sets) {
|
||||
for (Property<?> prop : set.getProperties()) {
|
||||
if ( ! columnsToSkip.contains(prop.getDisplayName())) {
|
||||
values.add(escapeQuotes(prop.getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
br.write(listToCSV(values));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape any quotes in the string
|
||||
*
|
||||
* @param original
|
||||
*
|
||||
* @return the string with quotes escaped
|
||||
*/
|
||||
private String escapeQuotes(String original) {
|
||||
return original.replaceAll("\"", "\\\\\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert list of values to a comma separated string.
|
||||
*
|
||||
* @param values Values to convert
|
||||
*
|
||||
* @return values as CSV
|
||||
*/
|
||||
private String listToCSV(List<String> values) {
|
||||
return "\"" + String.join("\",\"", values) + "\"\n";
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"CSVWriter.done.notifyMsg.error=Error exporting to CSV file",
|
||||
"# {0} - Output file",
|
||||
"CSVWriter.done.notifyMsg.success=Wrote to {0}"})
|
||||
@Override
|
||||
protected void done() {
|
||||
boolean msgDisplayed = false;
|
||||
try {
|
||||
super.get();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.info(Bundle.CSVWriter_done_notifyMsg_error());
|
||||
msgDisplayed = true;
|
||||
} catch (java.util.concurrent.CancellationException ex) {
|
||||
// catch and ignore if we were cancelled
|
||||
} finally {
|
||||
progress.finish();
|
||||
if (!this.isCancelled() && !msgDisplayed) {
|
||||
MessageNotifyUtil.Message.info(Bundle.CSVWriter_done_notifyMsg_success(outputFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -278,6 +278,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
|
||||
//enable the new button
|
||||
FilesSetDefsPanel.this.newSetButton.setEnabled(canBeEnabled);
|
||||
FilesSetDefsPanel.this.importSetButton.setEnabled(canBeEnabled);
|
||||
|
||||
// Get the selected interesting files set and populate the set
|
||||
// components.
|
||||
FilesSet selectedSet = FilesSetDefsPanel.this.setsList.getSelectedValue();
|
||||
@ -302,6 +303,12 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
|
||||
if (!FilesSetDefsPanel.this.rulesListModel.isEmpty()) {
|
||||
FilesSetDefsPanel.this.rulesList.setSelectedIndex(0);
|
||||
}
|
||||
} else {
|
||||
// Disable the edit, delete, copy, and export buttons
|
||||
FilesSetDefsPanel.this.editSetButton.setEnabled(false);
|
||||
FilesSetDefsPanel.this.deleteSetButton.setEnabled(false);
|
||||
FilesSetDefsPanel.this.copySetButton.setEnabled(false);
|
||||
FilesSetDefsPanel.this.exportSetButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,4 +120,13 @@ public final class TextTranslationService {
|
||||
public boolean hasProvider() {
|
||||
return selectedTranslator.isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hard limit for translation request sizes.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getMaxPayloadSize() {
|
||||
return selectedTranslator.get().getMaxPayloadSize();
|
||||
}
|
||||
}
|
||||
|
@ -56,4 +56,11 @@ public interface TextTranslator {
|
||||
* Save the settings as they have been modified in the component.
|
||||
*/
|
||||
void saveSettings();
|
||||
|
||||
/**
|
||||
* Returns the hard limit for translation request sizes.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int getMaxPayloadSize();
|
||||
}
|
||||
|
@ -108,7 +108,6 @@ public class BingTranslator implements TextTranslator {
|
||||
String toTranslate = string.trim();
|
||||
//Translates some text into English, without specifying the source langauge.
|
||||
|
||||
// HTML files were producing lots of white space at the end
|
||||
//Google Translate required us to replace (\r\n|\n) with <br />
|
||||
//but Bing Translator doesn not have that requirement.
|
||||
//The free account has a maximum file size. If you have a paid account,
|
||||
@ -172,4 +171,9 @@ public class BingTranslator implements TextTranslator {
|
||||
throw new TranslationException("JSON text does not match Bing Translator scheme: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPayloadSize() {
|
||||
return MAX_STRING_LENGTH;
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ import org.sleuthkit.autopsy.texttranslation.TranslationException;
|
||||
public final class GoogleTranslator implements TextTranslator {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(GoogleTranslator.class.getName());
|
||||
private static final int MAX_STRING_LENGTH = 15000;
|
||||
//See translate method for justification of this limit.
|
||||
private static final int MAX_PAYLOAD_SIZE = 5000;
|
||||
private final GoogleTranslatorSettingsPanel settingsPanel;
|
||||
private final GoogleTranslatorSettings settings = new GoogleTranslatorSettings();
|
||||
private Translate googleTranslate;
|
||||
@ -90,21 +91,20 @@ public final class GoogleTranslator implements TextTranslator {
|
||||
if (googleTranslate != null) {
|
||||
try {
|
||||
// Translates some text into English, without specifying the source language.
|
||||
|
||||
// HTML files were producing lots of white space at the end
|
||||
String substring = string.trim();
|
||||
|
||||
// We can't currently set parameters, so we are using the default behavior of
|
||||
// assuming the input is HTML. We need to replace newlines with <br> for Google to preserve them
|
||||
substring = substring.replaceAll("(\r\n|\n)", "<br />");
|
||||
|
||||
// The API complains if the "Payload" is over 204800 bytes. I'm assuming that
|
||||
// deals with the full request. At some point, we get different errors about too
|
||||
// much text. Officially, Google says they will googleTranslate only 5k chars,
|
||||
// but we have seen more than that working.
|
||||
// there could be a value betwen 15k and 25k that works. I (BC) didn't test further
|
||||
if (substring.length() > MAX_STRING_LENGTH) {
|
||||
substring = substring.substring(0, MAX_STRING_LENGTH);
|
||||
// The API complains if the "Payload" is over 204800 bytes. Google references that
|
||||
//their service is optimized for 2K code points and recommends keeping the requests that size.
|
||||
//There is a hard limit of 30K code points per request. There is also a time-based quota that
|
||||
//we are not enforcing, which may lead to 403 errors. We are currently configured for a max of 5K
|
||||
//in each request, for two reasons. 1) To be more in line with Google's recommendation. 2) To
|
||||
//minimize accidental exceedence of time based quotas.
|
||||
if (substring.length() > MAX_PAYLOAD_SIZE) {
|
||||
substring = substring.substring(0, MAX_PAYLOAD_SIZE);
|
||||
}
|
||||
Translation translation
|
||||
= googleTranslate.translate(substring);
|
||||
@ -178,4 +178,9 @@ public final class GoogleTranslator implements TextTranslator {
|
||||
settings.saveSettings();
|
||||
loadTranslator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPayloadSize() {
|
||||
return MAX_PAYLOAD_SIZE;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
OptionsCategory_Name_Machine_Translation=Machine Translation
|
||||
OptionsCategory_Keywords_Machine_Translation_Settings=Machine Translation Settings
|
||||
TranslationContentPanel.ShowLabel.text=Show:
|
||||
TranslationContentPanel.warningLabel2MB.text=Only the first 1MB of text will be displayed
|
||||
TranslationContentPanel.ocrLabel.text=OCR:
|
||||
TranslationOptionsPanel.translationServiceLabel.text=Text translator:
|
||||
TranslationOptionsPanelController.moduleErr=Module Error
|
||||
|
@ -1,6 +1,6 @@
|
||||
OptionsCategory_Name_Machine_Translation=Machine Translation
|
||||
OptionsCategory_Keywords_Machine_Translation_Settings=Machine Translation Settings
|
||||
TranslatedContentPanel.comboBoxOption.originalText=Original Text
|
||||
TranslatedContentPanel.comboBoxOption.originalText=Original Text (Up to 25KB)
|
||||
TranslatedContentPanel.comboBoxOption.translatedText=Translated Text
|
||||
TranslatedContentViewer.emptyTranslation=The resulting translation was empty.
|
||||
TranslatedContentViewer.errorExtractingText=Could not extract text from file.
|
||||
@ -12,11 +12,11 @@ TranslatedContentViewer.noServiceProvider=Machine Translation software was not f
|
||||
TranslatedContentViewer.textAlreadyIndexed=Please view the original text in the Indexed Text viewer.
|
||||
TranslatedContentViewer.translatingText=Translating text, please wait...
|
||||
TranslatedContentViewer.translationException=Error encountered while attempting translation.
|
||||
TranslatedTextViewer.maxPayloadSize=Up to the first %dKB of text will be translated
|
||||
TranslatedTextViewer.title=Translation
|
||||
TranslatedTextViewer.toolTip=Displays translated file text.
|
||||
TranslationContentPanel.autoDetectOCR=Autodetect language
|
||||
TranslationContentPanel.ShowLabel.text=Show:
|
||||
TranslationContentPanel.warningLabel2MB.text=Only the first 1MB of text will be displayed
|
||||
TranslationContentPanel.ocrLabel.text=OCR:
|
||||
TranslationOptionsPanel.noTextTranslators.text=No text translators exist, translation is disabled.
|
||||
TranslationOptionsPanel.noTextTranslatorSelected.text=No text translator selected, translation is disabled.
|
||||
|
@ -67,7 +67,7 @@ public final class TranslatedTextViewer implements TextViewer {
|
||||
|
||||
private static final boolean OCR_ENABLED = true;
|
||||
private static final boolean OCR_DISABLED = false;
|
||||
private static final int MAX_SIZE_1MB = 1024000;
|
||||
private static final int MAX_EXTRACT_SIZE_BYTES = 25600;
|
||||
private static final List<String> INSTALLED_LANGUAGE_PACKS = PlatformUtil.getOcrLanguagePacks();
|
||||
private final TranslationContentPanel panel = new TranslationContentPanel();
|
||||
|
||||
@ -77,6 +77,9 @@ public final class TranslatedTextViewer implements TextViewer {
|
||||
= new ThreadFactoryBuilder().setNameFormat("translation-content-viewer-%d").build();
|
||||
private final ExecutorService executorService = Executors.newSingleThreadExecutor(translationThreadFactory);
|
||||
|
||||
@NbBundle.Messages({
|
||||
"TranslatedTextViewer.maxPayloadSize=Up to the first %dKB of text will be translated"
|
||||
})
|
||||
@Override
|
||||
public void setNode(final Node node) {
|
||||
this.node = node;
|
||||
@ -92,6 +95,9 @@ public final class TranslatedTextViewer implements TextViewer {
|
||||
panel.addLanguagePackNames(INSTALLED_LANGUAGE_PACKS);
|
||||
}
|
||||
}
|
||||
|
||||
int payloadMaxInKB = TextTranslationService.getInstance().getMaxPayloadSize() / 1000;
|
||||
panel.setWarningLabelMsg(String.format(Bundle.TranslatedTextViewer_maxPayloadSize(), payloadMaxInKB));
|
||||
|
||||
//Force a background task.
|
||||
displayDropDownListener.actionPerformed(null);
|
||||
@ -296,8 +302,9 @@ public final class TranslatedTextViewer implements TextViewer {
|
||||
|
||||
//Correct for UTF-8
|
||||
byte[] resultInUTF8Bytes = result.getBytes("UTF8");
|
||||
byte[] trimTo1MB = Arrays.copyOfRange(resultInUTF8Bytes, 0, MAX_SIZE_1MB );
|
||||
return new String(trimTo1MB, "UTF-8");
|
||||
byte[] trimToArraySize = Arrays.copyOfRange(resultInUTF8Bytes, 0,
|
||||
Math.min(resultInUTF8Bytes.length, MAX_EXTRACT_SIZE_BYTES) );
|
||||
return new String(trimToArraySize, "UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -331,7 +338,7 @@ public final class TranslatedTextViewer implements TextViewer {
|
||||
|
||||
//Short-circuit the read if its greater than our max
|
||||
//translatable size
|
||||
int bytesLeft = MAX_SIZE_1MB - bytesRead;
|
||||
int bytesLeft = MAX_EXTRACT_SIZE_BYTES - bytesRead;
|
||||
|
||||
if (bytesLeft < read) {
|
||||
textBuilder.append(cbuf, 0, bytesLeft);
|
||||
@ -341,9 +348,8 @@ public final class TranslatedTextViewer implements TextViewer {
|
||||
textBuilder.append(cbuf, 0, read);
|
||||
bytesRead += read;
|
||||
}
|
||||
|
||||
// The trim is on here because HTML files were observed with nearly 1MB of white space at the end
|
||||
return textBuilder.toString().trim();
|
||||
|
||||
return textBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,7 +16,7 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jPanel1" alignment="1" pref="628" max="32767" attributes="0"/>
|
||||
<Component id="jPanel1" alignment="1" pref="872" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -48,12 +48,12 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="warningLabel2MB" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="warningLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="422" max="32767" attributes="0"/>
|
||||
<Component id="ShowLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="displayTextComboBox" min="-2" pref="128" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="displayTextComboBox" min="-2" pref="150" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="ocrLabel" min="-2" pref="26" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="ocrDropdown" min="-2" pref="180" max="-2" attributes="0"/>
|
||||
@ -72,18 +72,25 @@
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="9" pref="9" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Group type="103" alignment="1" groupAlignment="3" attributes="0">
|
||||
<Component id="displayTextComboBox" alignment="3" pref="26" max="32767" attributes="0"/>
|
||||
<Component id="ShowLabel" alignment="3" max="32767" attributes="0"/>
|
||||
<Component id="ocrLabel" alignment="3" max="32767" attributes="0"/>
|
||||
<Component id="warningLabel2MB" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="9" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="warningLabel" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Group type="103" alignment="1" groupAlignment="3" attributes="0">
|
||||
<Component id="displayTextComboBox" alignment="3" pref="26" max="32767" attributes="0"/>
|
||||
<Component id="ShowLabel" alignment="3" max="32767" attributes="0"/>
|
||||
<Component id="ocrLabel" alignment="3" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="ocrDropdown" alignment="1" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="ocrDropdown" alignment="1" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
@ -153,14 +160,11 @@
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="warningLabel2MB">
|
||||
<Component class="javax.swing.JLabel" name="warningLabel">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/warning16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties" key="TranslationContentPanel.warningLabel2MB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
|
@ -87,6 +87,10 @@ public class TranslationContentPanel extends javax.swing.JPanel {
|
||||
dropDown.removeActionListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
void setWarningLabelMsg(String msg) {
|
||||
warningLabel.setText(msg);
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"TranslationContentPanel.autoDetectOCR=Autodetect language"})
|
||||
final void reset() {
|
||||
@ -246,7 +250,7 @@ public class TranslationContentPanel extends javax.swing.JPanel {
|
||||
/**
|
||||
* Selection choices to be displayed in the combobox dropdown.
|
||||
*/
|
||||
@Messages({"TranslatedContentPanel.comboBoxOption.originalText=Original Text",
|
||||
@Messages({"TranslatedContentPanel.comboBoxOption.originalText=Original Text (Up to 25KB)",
|
||||
"TranslatedContentPanel.comboBoxOption.translatedText=Translated Text"})
|
||||
static enum DisplayDropdownOptions {
|
||||
ORIGINAL_TEXT(Bundle.TranslatedContentPanel_comboBoxOption_originalText()),
|
||||
@ -280,7 +284,7 @@ public class TranslationContentPanel extends javax.swing.JPanel {
|
||||
displayTextComboBox = new javax.swing.JComboBox<>();
|
||||
ocrDropdown = new javax.swing.JComboBox<>();
|
||||
ocrLabel = new javax.swing.JLabel();
|
||||
warningLabel2MB = new javax.swing.JLabel();
|
||||
warningLabel = new javax.swing.JLabel();
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
displayTextArea = new javax.swing.JTextArea();
|
||||
|
||||
@ -302,8 +306,7 @@ public class TranslationContentPanel extends javax.swing.JPanel {
|
||||
org.openide.awt.Mnemonics.setLocalizedText(ocrLabel, org.openide.util.NbBundle.getMessage(TranslationContentPanel.class, "TranslationContentPanel.ocrLabel.text")); // NOI18N
|
||||
ocrLabel.setEnabled(false);
|
||||
|
||||
warningLabel2MB.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/warning16.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(warningLabel2MB, org.openide.util.NbBundle.getMessage(TranslationContentPanel.class, "TranslationContentPanel.warningLabel2MB.text")); // NOI18N
|
||||
warningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/warning16.png"))); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
@ -311,12 +314,12 @@ public class TranslationContentPanel extends javax.swing.JPanel {
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(warningLabel2MB)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(warningLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 422, Short.MAX_VALUE)
|
||||
.addComponent(ShowLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(displayTextComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 128, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(displayTextComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(ocrLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(ocrDropdown, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
@ -331,16 +334,20 @@ public class TranslationContentPanel extends javax.swing.JPanel {
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGap(9, 9, 9)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(displayTextComboBox, javax.swing.GroupLayout.DEFAULT_SIZE, 26, Short.MAX_VALUE)
|
||||
.addComponent(ShowLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(ocrLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(warningLabel2MB))
|
||||
.addComponent(ocrDropdown))
|
||||
.addGap(7, 7, 7))
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(warningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(displayTextComboBox, javax.swing.GroupLayout.DEFAULT_SIZE, 26, Short.MAX_VALUE)
|
||||
.addComponent(ShowLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(ocrLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addComponent(ocrDropdown))
|
||||
.addGap(7, 7, 7))))
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
@ -362,7 +369,7 @@ public class TranslationContentPanel extends javax.swing.JPanel {
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 628, Short.MAX_VALUE)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 872, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane1)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
@ -384,6 +391,6 @@ public class TranslationContentPanel extends javax.swing.JPanel {
|
||||
private javax.swing.JSeparator jSeparator2;
|
||||
private javax.swing.JComboBox<String> ocrDropdown;
|
||||
private javax.swing.JLabel ocrLabel;
|
||||
private javax.swing.JLabel warningLabel2MB;
|
||||
private javax.swing.JLabel warningLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.util.Utilities;
|
||||
import org.openide.util.lookup.ProxyLookup;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
@ -163,6 +164,7 @@ class AdHocSearchFilterNode extends FilterNode {
|
||||
actionsList.add(new ExternalViewerAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.openExternViewActLbl"), getOriginal()));
|
||||
actionsList.add(null);
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
||||
|
@ -143,8 +143,9 @@ final class VcardParser {
|
||||
* @throws NoCurrentCaseException If there is no open case.
|
||||
*/
|
||||
void parse(File vcardFile, AbstractFile abstractFile) throws IOException, NoCurrentCaseException {
|
||||
VCard vcard = Ezvcard.parse(vcardFile).first();
|
||||
addContactArtifact(vcard, abstractFile);
|
||||
for (VCard vcard: Ezvcard.parse(vcardFile).all()) {
|
||||
addContactArtifact(vcard, abstractFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user