diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java index 9a60bd17e7..8485a9cfc3 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java @@ -22,6 +22,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Comparator; +import java.util.Date; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; @@ -35,6 +36,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.datamodel.BlackboardAttribute.Type; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM; @@ -357,4 +359,29 @@ final class DataSourceInfoUtilities { } } } + + + + private static BlackboardAttribute getAttributeOrNull(BlackboardArtifact artifact, Type attributeType) { + try { + return artifact.getAttribute(attributeType); + } catch (TskCoreException ex) { + return null; + } + } + + static String getStringOrNull(BlackboardArtifact artifact, Type attributeType) { + BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); + return (attr == null) ? null : attr.getValueString(); + } + + static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) { + BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); + return (attr == null) ? null : attr.getValueLong(); + } + + static Date getDateOrNull(BlackboardArtifact artifact, Type attributeType) { + Long longVal = getLongOrNull(artifact, attributeType); + return (longVal == null) ? null : new Date(longVal * 1000); + } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java deleted file mode 100644 index cc97b62389..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopDomainsSummary.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; - -import java.util.Date; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.sleuthkit.datamodel.DataSource; - -/** - * Provides summary information about top domains in a datasource. At this time, - * the data being provided is fictitious and is done as a placeholder. - */ -public class DataSourceTopDomainsSummary { - - private static final long SLEEP_TIME = 5000; - - /** - * A function to calculate a result from 2 parameters. - */ - interface Function2 { - - O apply(A1 a1, A2 a2); - } - - /** - * Gets a list of recent domains based on the datasource. - * - * @param dataSource The datasource to query for recent domains. - * @param count The max count of items to return. - * - * @return The list of items retrieved from the database. - * - * @throws InterruptedException - */ - public List getRecentDomains(DataSource dataSource, int count) throws InterruptedException { - Thread.sleep(SLEEP_TIME); - final String dId = Long.toString(dataSource.getId()); - final Function2 getId = (s, idx) -> String.format("d:%s, f:%s, i:%d", dId, s, idx); - return IntStream.range(0, count) - .mapToObj(num -> new TopDomainsResult( - getId.apply("domain", num), - getId.apply("url", num), - (long) num, - new Date(((long) num) * 1000 * 60 * 60 * 24) - )) - .collect(Collectors.toList()); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceUserActivitySummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceUserActivitySummary.java new file mode 100644 index 0000000000..f9de3fcd0d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceUserActivitySummary.java @@ -0,0 +1,269 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.datasourcesummary.datamodel; + +import java.util.Date; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.apache.commons.lang3.StringUtils; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; +import static org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities.SortOrder; +import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException; +import org.sleuthkit.autopsy.texttranslation.TextTranslationService; +import org.sleuthkit.autopsy.texttranslation.TranslationException; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; + +/** + * Provides summary information about top domains in a datasource. At this time, + * the data being provided is fictitious and is done as a placeholder. + */ +public class DataSourceUserActivitySummary { + + private static final BlackboardArtifact.Type TYPE_DEVICE_ATTACHED = new BlackboardArtifact.Type(TSK_DEVICE_ATTACHED); + private static final BlackboardArtifact.Type TYPE_MESSAGE = new BlackboardArtifact.Type(TSK_MESSAGE); + private static final BlackboardArtifact.Type TYPE_WEB_SEARCH_QUERY = new BlackboardArtifact.Type(TSK_WEB_SEARCH_QUERY); + + private static final BlackboardAttribute.Type TYPE_DATETIME = new BlackboardAttribute.Type(TSK_DATETIME); + private static final BlackboardAttribute.Type TYPE_DATETIME_ACCESSED = new BlackboardAttribute.Type(TSK_DATETIME_ACCESSED); + private static final BlackboardAttribute.Type TYPE_DEVICE_ID = new BlackboardAttribute.Type(TSK_DEVICE_ID); + private static final BlackboardAttribute.Type TYPE_DEVICE_MAKE = new BlackboardAttribute.Type(TSK_DEVICE_MAKE); + private static final BlackboardAttribute.Type TYPE_DEVICE_MODEL = new BlackboardAttribute.Type(TSK_DEVICE_MODEL); + private static final BlackboardAttribute.Type TYPE_MAC_ADDRESS = new BlackboardAttribute.Type(TSK_MAC_ADDRESS); + private static final BlackboardAttribute.Type TYPE_MESSAGE_TYPE = new BlackboardAttribute.Type(TSK_MESSAGE_TYPE); + private static final BlackboardAttribute.Type TYPE_TEXT = new BlackboardAttribute.Type(TSK_TEXT); + private static final BlackboardAttribute.Type TYPE_DOMAIN = new BlackboardAttribute.Type(TSK_DOMAIN); + private static final BlackboardAttribute.Type TYPE_PROG_NAME = new BlackboardAttribute.Type(TSK_PROG_NAME); + + private static final long SLEEP_TIME = 5000; + + /** + * A function to calculate a result from 2 parameters. + */ + interface Function2 { + + O apply(A1 a1, A2 a2); + } + + /** + * Gets a list of recent domains based on the datasource. + * + * @param dataSource The datasource to query for recent domains. + * @param count The max count of items to return. + * + * @return The list of items retrieved from the database. + * + * @throws InterruptedException + */ + public List getRecentDomains(DataSource dataSource, int count) throws InterruptedException { + Thread.sleep(SLEEP_TIME); + final String dId = Long.toString(dataSource.getId()); + final Function2 getId = (s, idx) -> String.format("d:%s, f:%s, i:%d", dId, s, idx); + return IntStream.range(0, count) + .mapToObj(num -> new TopDomainsResult( + getId.apply("domain", num), + getId.apply("url", num), + (long) num, + new Date(((long) num) * 1000 * 60 * 60 * 24) + )) + .collect(Collectors.toList()); + } + + private final SleuthkitCaseProvider caseProvider; + private final TextTranslationService translationService; + private final java.util.logging.Logger logger; + + public DataSourceUserActivitySummary() { + this(SleuthkitCaseProvider.DEFAULT, TextTranslationService.getInstance(), + org.sleuthkit.autopsy.coreutils.Logger.getLogger(DataSourceUserActivitySummary.class.getName())); + } + + public DataSourceUserActivitySummary(SleuthkitCaseProvider provider, TextTranslationService translationService, java.util.logging.Logger logger) { + this.caseProvider = provider; + this.translationService = translationService; + this.logger = logger; + } + + public List getMostRecentWebSearches(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + List results = + DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_WEB_SEARCH_QUERY, dataSource, TYPE_DATETIME_ACCESSED, SortOrder.DESCENDING, count) + .stream() + .map(artifact -> new TopWebSearchResult( + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT), + DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME_ACCESSED), + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DOMAIN), + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PROG_NAME) + )) + .collect(Collectors.toList()); + + for (TopWebSearchResult result : results) { + if (StringUtils.isNotBlank(result.getSearchString()) && translationService.hasProvider()) { + String translated = null; + try { + translated = translationService.translate(result.getSearchString()); + } catch (NoServiceProviderException | TranslationException ex) { + logger.log(Level.WARNING, String.format("There was an error translating text: '%s'", result.getSearchString()), ex); + } + + if (StringUtils.isNotBlank(translated)) { + result.setTranslatedResult(translated); + } + } + } + + return results; + } + + public List getRecentDevices(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + return DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_DEVICE_ATTACHED, dataSource, TYPE_DATETIME, SortOrder.DESCENDING, count) + .stream() + .map(artifact -> new TopDeviceAttachedResult( + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_ID), + DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME), + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MAKE), + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL), + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MAC_ADDRESS) + )) + .collect(Collectors.toList()); + } + + public List getRecentAccounts(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + // TODO fix this for groupings + return DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_MESSAGE, dataSource, TYPE_DATETIME, SortOrder.DESCENDING, count) + .stream() + .map(artifact -> new TopAccountResult( + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE), + DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME) + )) + .collect(Collectors.toList()); + } + + public static class TopWebSearchResult { + + private final String searchString; + private final Date dateAccessed; + private final String domain; + private final String programName; + private String translatedResult; + + public TopWebSearchResult(String searchString, Date dateAccessed, String domain, String programName) { + this.searchString = searchString; + this.dateAccessed = dateAccessed; + this.domain = domain; + this.programName = programName; + } + + public String getTranslatedResult() { + return translatedResult; + } + + public void setTranslatedResult(String translatedResult) { + this.translatedResult = translatedResult; + } + + public String getSearchString() { + return searchString; + } + + public Date getDateAccessed() { + return dateAccessed; + } + + public String getDomain() { + return domain; + } + + public String getProgramName() { + return programName; + } + } + + public static class TopDeviceAttachedResult { + + private final String deviceId; + private final Date dateAccessed; + private final String deviceMake; + private final String deviceModel; + private final String macAddress; + + public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, String macAddress) { + this.deviceId = deviceId; + this.dateAccessed = dateAccessed; + this.deviceMake = deviceMake; + this.deviceModel = deviceModel; + this.macAddress = macAddress; + } + + public String getDeviceId() { + return deviceId; + } + + public Date getDateAccessed() { + return dateAccessed; + } + + public String getDeviceMake() { + return deviceMake; + } + + public String getDeviceModel() { + return deviceModel; + } + + public String getMacAddress() { + return macAddress; + } + } + + public static class TopAccountResult { + + private final String accountType; + private final Date lastAccess; + + public TopAccountResult(String accountType, Date lastAccess) { + this.accountType = accountType; + this.lastAccess = lastAccess; + } + + public String getAccountType() { + return accountType; + } + + public Date getLastAccess() { + return lastAccess; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties index 708010e07a..1aba6cdb11 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties @@ -37,3 +37,6 @@ DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains +DataSourceSummaryUserActivityPanel.recentDomainsLabel1.text=Recent Domains +DataSourceSummaryUserActivityPanel.recentDomainsLabel2.text=Recent Domains +DataSourceSummaryUserActivityPanel.recentDomainsLabel3.text=Recent Domains diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form index 9798add7eb..4dbe01dbd8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form @@ -11,7 +11,7 @@ - + @@ -181,6 +181,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java index eb3f09a81f..4852806d39 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java @@ -22,13 +22,18 @@ import java.awt.Component; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Arrays; +import java.util.Date; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; +import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopDomainsSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceUserActivitySummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopProgramsSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceUserActivitySummary.TopAccountResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceUserActivitySummary.TopDeviceAttachedResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceUserActivitySummary.TopWebSearchResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopDomainsResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; @@ -45,23 +50,49 @@ import org.sleuthkit.datamodel.DataSource; */ @Messages({ "DataSourceSummaryUserActivityPanel_tab_title=User Activity", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run", + "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain", "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL", "DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access", - "DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists",}) + "DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists", + + "DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_searchString_header=Search String", + "DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_dateAccessed_header=Date Accessed", + "DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_translatedResult_header=Translated", + + "DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_deviceId_header=Device Id", + "DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_makeModel_header=Make and Model", + "DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed", + + "DataSourceSummaryUserActivityPanel_TopAccountTableModel_accountType_header=Account Type", + "DataSourceSummaryUserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed", +}) public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPanel { private static final long serialVersionUID = 1L; private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()); private static final int TOP_PROGS_COUNT = 10; private static final int TOP_DOMAINS_COUNT = 10; + private static final int TOP_SEARCHES_COUNT = 10; + private static final int TOP_ACCOUNTS_COUNT = 5; + private static final int TOP_DEVICES_COUNT = 10; + private static String getFormatted(Date date) { + return date == null ? "" : DATETIME_FORMAT.format(date); + } + + private final JTablePanel topProgramsTable; private final JTablePanel recentDomainsTable; + private final JTablePanel topWebSearchesTable; + private final JTablePanel topDevicesAttachedTable; + private final JTablePanel topAccountsTable; + private final List> dataFetchComponents; private final List> tables; @@ -69,7 +100,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan * Creates a new DataSourceUserActivityPanel. */ public DataSourceSummaryUserActivityPanel() { - this(new DataSourceTopProgramsSummary(), new DataSourceTopDomainsSummary()); + this(new DataSourceTopProgramsSummary(), new DataSourceUserActivitySummary()); } /** @@ -78,15 +109,18 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan * @param topProgramsData Class from which to obtain top programs data. * @param topDomainsData Class from which to obtain recent domains data. */ - public DataSourceSummaryUserActivityPanel(DataSourceTopProgramsSummary topProgramsData, DataSourceTopDomainsSummary topDomainsData) { + public DataSourceSummaryUserActivityPanel(DataSourceTopProgramsSummary topProgramsData, DataSourceUserActivitySummary topDomainsData) { // set up recent programs table - this.topProgramsTable = JTablePanel.getJTablePanel(Arrays.asList(new ColumnModel<>( - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), - (prog) -> { - return new DefaultCellModel(prog.getProgramName()) - .setTooltip(prog.getProgramPath()); - }, - 250), + this.topProgramsTable = JTablePanel.getJTablePanel(Arrays.asList( + // program name column + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), + (prog) -> { + return new DefaultCellModel(prog.getProgramName()) + .setTooltip(prog.getProgramPath()); + }, + 250), + // program folder column new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), (prog) -> { @@ -96,6 +130,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan prog.getProgramName())); }, 150), + // run count column new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(), (prog) -> { @@ -104,49 +139,145 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan .setHorizontalAlignment(HorizontalAlign.RIGHT); }, 80), + // last run date column new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header(), (prog) -> { - String date = prog.getLastRun() == null ? "" : DATETIME_FORMAT.format(prog.getLastRun()); - return new DefaultCellModel(date) + return new DefaultCellModel(getFormatted(prog.getLastRun())) .setHorizontalAlignment(HorizontalAlign.RIGHT); }, 150) )); // set up recent domains table - this.recentDomainsTable = JTablePanel.getJTablePanel(Arrays.asList(new ColumnModel<>( - Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(), - (d) -> new DefaultCellModel(d.getDomain()), - 250), + this.recentDomainsTable = JTablePanel.getJTablePanel(Arrays.asList( + // domain column + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(), + (recentDomain) -> new DefaultCellModel(recentDomain.getDomain()), + 250), + + // url column new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header(), - (d) -> new DefaultCellModel(d.getUrl()), + (recentDomain) -> new DefaultCellModel(recentDomain.getUrl()), 250), + + // last accessed column new ColumnModel<>( Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header(), - (prog) -> { - String lastVisit = prog.getLastVisit() == null ? "" : DATETIME_FORMAT.format(prog.getLastVisit()); - return new DefaultCellModel(lastVisit) + (recentDomain) -> { + return new DefaultCellModel(getFormatted(recentDomain.getLastVisit())) .setHorizontalAlignment(HorizontalAlign.RIGHT); }, 150) )); + // top web searches table + this.topWebSearchesTable = JTablePanel.getJTablePanel(Arrays.asList( + // search string column + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_searchString_header(), + (webSearch) -> new DefaultCellModel(webSearch.getSearchString()), + 250 + ), + // last accessed + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_dateAccessed_header(), + (webSearch) -> new DefaultCellModel(getFormatted(webSearch.getDateAccessed())) + .setHorizontalAlignment(HorizontalAlign.RIGHT), + 150 + ), + // translated value + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_translatedResult_header(), + (webSearch) -> new DefaultCellModel(webSearch.getTranslatedResult()), + 250 + ) + )); + + // top devices attached table + this.topDevicesAttachedTable = JTablePanel.getJTablePanel(Arrays.asList( + // device id column + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_deviceId_header(), + (device) -> new DefaultCellModel(device.getDeviceId()), + 250 + ), + // last accessed + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header(), + (device) -> new DefaultCellModel(getFormatted(device.getDateAccessed())) + .setHorizontalAlignment(HorizontalAlign.RIGHT), + 150 + ), + // make and model + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_makeModel_header(), + (device) -> { + String make = StringUtils.isBlank(device.getDeviceMake()) ? "" : device.getDeviceMake().trim(); + String model = StringUtils.isBlank(device.getDeviceModel()) ? "" : device.getDeviceModel().trim(); + String makeModelString = (make.isEmpty() || model.isEmpty()) ? + make + model : + String.format("%s - %s", make, model); + return new DefaultCellModel(makeModelString); + }, + 250 + ) + )); + + // top accounts table + this.topAccountsTable = JTablePanel.getJTablePanel(Arrays.asList( + // account type column + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopAccountTableModel_accountType_header(), + (account) -> new DefaultCellModel(account.getAccountType()), + 250 + ), + // last accessed + new ColumnModel<>( + Bundle.DataSourceSummaryUserActivityPanel_TopAccountTableModel_lastAccess_header(), + (account) -> new DefaultCellModel(getFormatted(account.getLastAccess())) + .setHorizontalAlignment(HorizontalAlign.RIGHT), + 150 + ) + )); + + this.tables = Arrays.asList( topProgramsTable, - recentDomainsTable + recentDomainsTable, + topWebSearchesTable, + topDevicesAttachedTable, + topAccountsTable ); // set up data acquisition methods dataFetchComponents = Arrays.asList( + // top programs query new DataFetchComponents>( (dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT), (result) -> topProgramsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), Bundle.DataSourceSummaryUserActivityPanel_noDataExists())), + // top domains query new DataFetchComponents>( (dataSource) -> topDomainsData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT), (result) -> recentDomainsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), + Bundle.DataSourceSummaryUserActivityPanel_noDataExists())), + // top web searches query + new DataFetchComponents>( + (dataSource) -> topDomainsData.getMostRecentWebSearches(dataSource, TOP_SEARCHES_COUNT), + (result) -> topWebSearchesTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), + Bundle.DataSourceSummaryUserActivityPanel_noDataExists())), + // top devices query + new DataFetchComponents>( + (dataSource) -> topDomainsData.getRecentDevices(dataSource, TOP_DEVICES_COUNT), + (result) -> topDevicesAttachedTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), + Bundle.DataSourceSummaryUserActivityPanel_noDataExists())), + // top accounts query + new DataFetchComponents>( + (dataSource) -> topDomainsData.getRecentAccounts(dataSource, TOP_ACCOUNTS_COUNT), + (result) -> topAccountsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), Bundle.DataSourceSummaryUserActivityPanel_noDataExists())) ); @@ -194,8 +325,19 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan javax.swing.JLabel recentDomainsLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel recentDomainsTablePanel = recentDomainsTable; + javax.swing.Box.Filler filler4 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); + javax.swing.JLabel recentDomainsLabel1 = new javax.swing.JLabel(); + javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); + javax.swing.JPanel recentDomainsTablePanel1 = recentDomainsTable; + javax.swing.Box.Filler filler6 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); + javax.swing.JLabel recentDomainsLabel2 = new javax.swing.JLabel(); + javax.swing.Box.Filler filler7 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); + javax.swing.JPanel recentDomainsTablePanel2 = recentDomainsTable; + javax.swing.Box.Filler filler8 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); + javax.swing.JLabel recentDomainsLabel3 = new javax.swing.JLabel(); + javax.swing.Box.Filler filler9 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); + javax.swing.JPanel recentDomainsTablePanel3 = recentDomainsTable; - setMaximumSize(null); setLayout(new java.awt.BorderLayout()); contentScrollPane.setMaximumSize(null); @@ -229,6 +371,42 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan recentDomainsTablePanel.setMinimumSize(new java.awt.Dimension(700, 187)); recentDomainsTablePanel.setPreferredSize(new java.awt.Dimension(700, 187)); contentPanel.add(recentDomainsTablePanel); + contentPanel.add(filler4); + + recentDomainsLabel1.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + org.openide.awt.Mnemonics.setLocalizedText(recentDomainsLabel1, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.recentDomainsLabel1.text")); // NOI18N + contentPanel.add(recentDomainsLabel1); + contentPanel.add(filler5); + + recentDomainsTablePanel1.setAlignmentX(0.0F); + recentDomainsTablePanel1.setMaximumSize(new java.awt.Dimension(700, 187)); + recentDomainsTablePanel1.setMinimumSize(new java.awt.Dimension(700, 187)); + recentDomainsTablePanel1.setPreferredSize(new java.awt.Dimension(700, 187)); + contentPanel.add(recentDomainsTablePanel1); + contentPanel.add(filler6); + + recentDomainsLabel2.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + org.openide.awt.Mnemonics.setLocalizedText(recentDomainsLabel2, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.recentDomainsLabel2.text")); // NOI18N + contentPanel.add(recentDomainsLabel2); + contentPanel.add(filler7); + + recentDomainsTablePanel2.setAlignmentX(0.0F); + recentDomainsTablePanel2.setMaximumSize(new java.awt.Dimension(700, 187)); + recentDomainsTablePanel2.setMinimumSize(new java.awt.Dimension(700, 187)); + recentDomainsTablePanel2.setPreferredSize(new java.awt.Dimension(700, 187)); + contentPanel.add(recentDomainsTablePanel2); + contentPanel.add(filler8); + + recentDomainsLabel3.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + org.openide.awt.Mnemonics.setLocalizedText(recentDomainsLabel3, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.recentDomainsLabel3.text")); // NOI18N + contentPanel.add(recentDomainsLabel3); + contentPanel.add(filler9); + + recentDomainsTablePanel3.setAlignmentX(0.0F); + recentDomainsTablePanel3.setMaximumSize(new java.awt.Dimension(700, 187)); + recentDomainsTablePanel3.setMinimumSize(new java.awt.Dimension(700, 187)); + recentDomainsTablePanel3.setPreferredSize(new java.awt.Dimension(700, 187)); + contentPanel.add(recentDomainsTablePanel3); contentScrollPane.setViewportView(contentPanel);