diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED index 0ba7683fcbaefb2bf01b635e83539350a765ba2d..ac2756ea3326f5de7e9fc8fa7584809e3efcc55f 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED @@ -57,6 +57,7 @@ FileSorter.SortingMethod.fullPath.displayName=Full Path FileSorter.SortingMethod.keywordlist.displayName=Keyword List Names FileSorter.SortingMethod.pageViews.displayName=Page Views ResultDomain_getDefaultCategory=Uncategorized +ResultDomain_noAccountTypes=Unknown ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it. ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable. ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag. diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java index afc305e53a3b751bc1c1c75056b25226618085d4..6c233bd37d493037168eb5a477b3fa18dbda9c17 100755 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,11 +44,13 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_ACCOUNT_TYPE; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; import org.sleuthkit.datamodel.CaseDbAccessManager; import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbAccessQueryCallback; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; /** * Loads domain search results for cache misses. This loader is a Guava cache @@ -103,11 +105,9 @@ List<Result> getResultDomainsFromDatabase(SearchKey key) throws TskCoreException // Filters chosen in the UI are aggregated into SQL statements to be used in // the queries that follow. - final Pair<String, String> filterClauses = createWhereAndHavingClause(key.getFilters()); - final String whereClause = filterClauses.getLeft(); - final String havingClause = filterClauses.getRight(); - - // You may think of each row of this result as a TSK_DOMAIN attribute, where the parent + final Pair<String, String> domainsFilterClauses = createWhereAndHavingClause(key.getFilters()); + final String domainsWhereClause = domainsFilterClauses.getLeft(); + final String domainsHavingClause = domainsFilterClauses.getRight(); // artifact type is within the (optional) filter and the parent artifact // had a date time attribute that was within the (optional) filter. With this // table in hand, we can simply group by domain and apply aggregate functions @@ -118,10 +118,31 @@ List<Result> getResultDomainsFromDatabase(SearchKey key) throws TskCoreException + " artifact_id AS parent_artifact_id," + " MAX(artifact_type_id) AS parent_artifact_type_id " + "FROM blackboard_attributes " - + "WHERE " + whereClause + " " + + "WHERE " + domainsWhereClause + " " + "GROUP BY artifact_id " - + "HAVING " + havingClause; + + "HAVING " + domainsHavingClause; final SleuthkitCase caseDb = key.getSleuthkitCase(); + String sqlSpecificAccountAggregator; + if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) { + sqlSpecificAccountAggregator = "STRING_AGG(DISTINCT(value_text), ',')"; //postgres string aggregator (requires specified separator + } else { + sqlSpecificAccountAggregator = "GROUP_CONCAT(DISTINCT(value_text))"; //sqlite string aggregator (uses comma separation by default) + } + /* + * As part of getting the known account types for a domain additional + * attribute values are necessary from the blackboard_attributes table + * This sub-query aggregates them and associates them with the artifact + * they correspond to. + */ + final String accountsTable + = "SELECT " + sqlSpecificAccountAggregator + " as value_text," //naming field value_text the same as the field it is aggregating to re-use aggregator + + "artifact_id AS account_artifact_id " + + "FROM blackboard_attributes " + + "WHERE (attribute_type_id = " + TSK_TEXT.getTypeID() + + " AND value_text <> '' " + + " AND (artifact_type_id = " + TSK_WEB_ACCOUNT_TYPE.getTypeID() + ")) " + + "GROUP BY artifact_id "; + // Needed to populate the visitsInLast60 data. final Instant mostRecentActivityDate = Instant.ofEpochSecond(caseDb.getTimelineManager().getMaxEventTime()); final Instant sixtyDaysAgo = mostRecentActivityDate.minus(60, ChronoUnit.DAYS); @@ -166,16 +187,18 @@ List<Result> getResultDomainsFromDatabase(SearchKey key) throws TskCoreException + " WHEN artifact_type_id = " + TSK_WEB_ACCOUNT_TYPE.getTypeID() + " THEN 1 " + " ELSE 0 " + " END) AS countOfKnownAccountTypes," - + " MAX(data_source_obj_id) AS dataSource " - + "FROM blackboard_artifacts" + + " MAX(data_source_obj_id) AS dataSource, " + + sqlSpecificAccountAggregator + " as accountTypes " + + "FROM blackboard_artifacts as barts" + " JOIN (" + domainsTable + ") AS domains_table" - + " ON artifact_id = parent_artifact_id " + + " ON barts.artifact_id = parent_artifact_id " + + " LEFT JOIN (" + accountsTable + ") AS accounts_table" + + " ON barts.artifact_id = account_artifact_id " + // Add the data source where clause here if present. ((dataSourceWhereClause != null) ? "WHERE " + dataSourceWhereClause + " " : "") + "GROUP BY " + groupByClause; final CaseDbAccessManager dbManager = caseDb.getCaseDbAccessManager(); - final DomainCallback domainCallback = new DomainCallback(caseDb); dbManager.select(domainsQuery, domainCallback); @@ -208,8 +231,8 @@ List<Result> getResultDomainsFromDatabase(SearchKey key) throws TskCoreException * to stress that these clauses are tightly coupled. */ Pair<String, String> createWhereAndHavingClause(List<AbstractFilter> filters) { - final StringJoiner whereClause = new StringJoiner(" OR "); - final StringJoiner havingClause = new StringJoiner(" AND "); + final StringJoiner whereClause = new StringJoiner(" OR ", "(", ")"); + final StringJoiner havingClause = new StringJoiner(" AND ", "(", ")"); // Capture all types by default. ArtifactTypeFilter artifactTypeFilter = new ArtifactTypeFilter(SearchData.Type.DOMAIN.getArtifactTypes()); @@ -219,7 +242,7 @@ Pair<String, String> createWhereAndHavingClause(List<AbstractFilter> filters) { if (filter instanceof ArtifactTypeFilter) { // Replace with user defined types. artifactTypeFilter = ((ArtifactTypeFilter) filter); - } else if (!(filter instanceof DataSourceFilter) && !filter.useAlternateFilter()) { + } else if (filter != null && !(filter instanceof DataSourceFilter) && !filter.useAlternateFilter()) { if (filter instanceof ArtifactDateRangeFilter) { hasDateTimeFilter = true; } @@ -300,11 +323,12 @@ public void process(ResultSet resultSet) { long pageViewsInLast60 = resultSet.getLong("pageViewsInLast60"); long countOfKnownAccountTypes = resultSet.getLong("countOfKnownAccountTypes"); long dataSourceID = resultSet.getLong("dataSource"); + String accountTypes = resultSet.getString("accountTypes"); Content dataSource = skc.getContentById(dataSourceID); resultDomains.add(new ResultDomain(domain, activityStart, activityEnd, totalPageViews, pageViewsInLast60, filesDownloaded, - countOfKnownAccountTypes, dataSource)); + countOfKnownAccountTypes, accountTypes, dataSource)); } } catch (SQLException ex) { this.sqlCause = ex; diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java index 94f0da9d0a1a0a330825123add3cd4b42c72a15e..47fa441678dda732f455cfbf508c2564bc58c6b3 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.discovery.search; +import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskData; @@ -34,6 +35,7 @@ public class ResultDomain extends Result { private final Long pageViewsInLast60; private final Long filesDownloaded; private final Long countOfKnownAccountTypes; + private final String accountTypes; private String webCategory; private final Content dataSource; @@ -45,7 +47,7 @@ public class ResultDomain extends Result { * @param domain The domain the result is being created from. */ ResultDomain(String domain, Long activityStart, Long activityEnd, Long totalPageViews, - Long pageViewsInLast60, Long filesDownloaded, Long countOfKnownAccountTypes, Content dataSource) { + Long pageViewsInLast60, Long filesDownloaded, Long countOfKnownAccountTypes, String accountTypes, Content dataSource) { this.domain = domain; this.dataSource = dataSource; this.dataSourceId = dataSource.getId(); @@ -55,6 +57,7 @@ public class ResultDomain extends Result { this.pageViewsInLast60 = pageViewsInLast60; this.filesDownloaded = filesDownloaded; this.countOfKnownAccountTypes = countOfKnownAccountTypes; + this.accountTypes = accountTypes; } /** @@ -85,8 +88,8 @@ public Long getActivityEnd() { } /** - * Get the total number of page views that this domain has had. - * Pages views is defined as the count of TSK_WEB_HISTORY artifacts. + * Get the total number of page views that this domain has had. Pages views + * is defined as the count of TSK_WEB_HISTORY artifacts. * * @return The total number of page views that this domain has had. */ @@ -95,8 +98,8 @@ public Long getTotalPageViews() { } /** - * Get the number of page views that this domain has had in the last 60 days. - * Page views is defined as the count of TSK_WEB_HISTORY artifacts. + * Get the number of page views that this domain has had in the last 60 + * days. Page views is defined as the count of TSK_WEB_HISTORY artifacts. * * @return The number of page views that this domain has had in the last 60 * days. @@ -113,7 +116,7 @@ public Long getPageViewsInLast60Days() { public Long getFilesDownloaded() { return filesDownloaded; } - + /** * Get the web category (TSK_WEB_CATEGORY) type for this domain. */ @@ -127,23 +130,41 @@ public String getWebCategory() { return webCategory; } } - + /** - * Set the web category for this domain (derived from TSK_WEB_CATEGORY) artifacts. + * Set the web category for this domain (derived from TSK_WEB_CATEGORY) + * artifacts. */ public void setWebCategory(String webCategory) { this.webCategory = webCategory; } - + /** * Determines if the domain has been associated with a known account type * (TSK_WEB_ACCOUNT_TYPE). */ public boolean hasKnownAccountType() { - return countOfKnownAccountTypes != null + return countOfKnownAccountTypes != null && countOfKnownAccountTypes > 0; } + /** + * Get the account types which are associated with this domain. + * + * @return A comma seperated list of account types which are associated with + * this domain, or "Unknown" if no account types were associated + * with it. + */ + @NbBundle.Messages({ + "ResultDomain_noAccountTypes=Unknown" + }) + public String getAccountTypes() { + if (StringUtils.isBlank(accountTypes)) { + return Bundle.ResultDomain_noAccountTypes(); + } + return accountTypes; + } + @Override public long getDataSourceObjectId() { return this.dataSourceId; diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED index 82752e24e9d8fa0f13944dcf20179bd0aadcc5df..f24ec80db6f2deaa4f4097e2f99f0a3a558c848b 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED @@ -59,13 +59,12 @@ DomainDetailsPanel.miniTimelineTitle.text=Timeline DomainSummaryPanel.activity.text=Activity: {0} to {1} DomainSummaryPanel.category.text=Category: DomainSummaryPanel.downloads.text=Files downloaded: -DomainSummaryPanel.known.text=User role: Known account type(s) DomainSummaryPanel.loadingImages.text=Loading thumbnail... DomainSummaryPanel.no.text=No DomainSummaryPanel.notability.text=Previously tagged as notable: DomainSummaryPanel.pages.text=Page views in final 60 days: DomainSummaryPanel.totalPages.text=Total page views: -DomainSummaryPanel.unknown.text=User role: Unknown +DomainSummaryPanel.userRole.text=Account type: DomainSummaryPanel.yes.text=Yes GroupsListPanel.noDomainResults.message.text=No domains were found for the selected filters.\n\nReminder:\n -The Recent Activity module must be run on each data source you want to find results in.\n -The Central Repository module must be run on each data source if you want to filter or sort by past occurrences.\n -The iOS Analyzer (iLEAPP) module must be run on each data source which contains data from an iOS device.\n GroupsListPanel.noFileResults.message.text=No files were found for the selected filters.\n\nReminder:\n -The File Type Identification module must be run on each data source you want to find results in.\n -The Hash Lookup module must be run on each data source if you want to filter by past occurrence.\n -The Picture Analyzer module must be run on each data source if you are filtering by User Created content. diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.form index 9e7fd5fd3c570d8a933bd5d0825b0df7c2445a76..68f72f9a78897562eb2acc2039e01ef5ee2aebf5 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.form @@ -27,20 +27,23 @@ <EmptySpace min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> <Component id="domainNameLabel" alignment="0" max="32767" attributes="0"/> - <Group type="102" attributes="0"> + <Component id="pagesLabel" alignment="0" max="32767" attributes="0"/> + <Group type="102" alignment="0" attributes="0"> <Group type="103" groupAlignment="1" max="-2" attributes="0"> - <Component id="filesDownloadedLabel" linkSize="3" alignment="0" pref="300" max="32767" attributes="0"/> - <Component id="activityLabel" linkSize="3" alignment="0" max="32767" attributes="0"/> + <Component id="totalVisitsLabel" alignment="0" max="32767" attributes="0"/> + <Group type="103" groupAlignment="1" max="-2" attributes="0"> + <Component id="filesDownloadedLabel" linkSize="3" alignment="0" pref="300" max="32767" attributes="0"/> + <Component id="activityLabel" linkSize="3" alignment="0" max="32767" attributes="0"/> + </Group> </Group> - <EmptySpace min="-2" max="-2" attributes="0"/> + <EmptySpace type="unrelated" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="domainNotabilityLabel" pref="300" max="32767" attributes="0"/> + <Component id="domainNotabilityLabel" pref="295" max="32767" attributes="0"/> <Component id="categoryLabel" max="32767" attributes="0"/> + <Component id="knownAccountTypesLabel" max="32767" attributes="0"/> </Group> <EmptySpace min="-2" pref="4" max="-2" attributes="0"/> </Group> - <Component id="pagesLabel" alignment="0" max="32767" attributes="0"/> - <Component id="totalVisitsLabel" alignment="0" max="32767" attributes="0"/> </Group> <EmptySpace min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" max="-2" attributes="0"> @@ -58,12 +61,12 @@ <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" attributes="0"> <Component id="numberOfImagesLabel" min="-2" pref="17" max="-2" attributes="0"/> - <EmptySpace pref="8" max="32767" attributes="0"/> + <EmptySpace max="32767" attributes="0"/> <Component id="sampleImageLabel" min="-2" max="-2" attributes="0"/> </Group> <Group type="102" attributes="0"> <Component id="domainNameLabel" min="-2" pref="35" max="-2" attributes="0"/> - <EmptySpace type="unrelated" max="32767" attributes="0"/> + <EmptySpace type="unrelated" pref="15" max="32767" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> <Component id="activityLabel" linkSize="4" alignment="1" min="-2" pref="15" max="-2" attributes="0"/> <Component id="domainNotabilityLabel" linkSize="2" alignment="1" min="-2" max="-2" attributes="0"/> @@ -73,8 +76,11 @@ <Component id="filesDownloadedLabel" linkSize="4" pref="14" max="32767" attributes="0"/> <Component id="categoryLabel" max="32767" attributes="0"/> </Group> - <EmptySpace max="-2" attributes="0"/> - <Component id="totalVisitsLabel" linkSize="2" pref="14" max="32767" attributes="0"/> + <EmptySpace min="-2" pref="9" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" max="-2" attributes="0"> + <Component id="totalVisitsLabel" linkSize="2" pref="14" max="32767" attributes="0"/> + <Component id="knownAccountTypesLabel" max="32767" attributes="0"/> + </Group> <EmptySpace max="-2" attributes="0"/> <Component id="pagesLabel" min="-2" pref="15" max="-2" attributes="0"/> <EmptySpace min="-2" pref="1" max="-2" attributes="0"/> @@ -131,5 +137,7 @@ </Component> <Component class="javax.swing.JLabel" name="categoryLabel"> </Component> + <Component class="javax.swing.JLabel" name="knownAccountTypesLabel"> + </Component> </SubComponents> </Form> diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java index 6076fc9aa03798dcfca3bc859120294f49ecf818..1d5a2f6d99ac182d0f60e1f51d9b59174beea87b 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java @@ -1,7 +1,7 @@ /* * Autopsy * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -69,6 +69,7 @@ private void initComponents() { totalVisitsLabel = new javax.swing.JLabel(); domainNotabilityLabel = new javax.swing.JLabel(); categoryLabel = new javax.swing.JLabel(); + knownAccountTypesLabel = new javax.swing.JLabel(); setBorder(javax.swing.BorderFactory.createEtchedBorder()); @@ -90,17 +91,19 @@ private void initComponents() { .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(domainNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(pagesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(filesDownloadedLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - .addComponent(activityLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(totalVisitsLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(filesDownloadedLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) + .addComponent(activityLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(domainNotabilityLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - .addComponent(categoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGap(4, 4, 4)) - .addComponent(pagesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(totalVisitsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(domainNotabilityLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 295, Short.MAX_VALUE) + .addComponent(categoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(knownAccountTypesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(4, 4, 4))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(numberOfImagesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -117,11 +120,11 @@ private void initComponents() { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(numberOfImagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 8, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(sampleImageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() .addComponent(domainNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, 15, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(activityLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(domainNotabilityLabel, javax.swing.GroupLayout.Alignment.TRAILING)) @@ -129,8 +132,10 @@ private void initComponents() { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(filesDownloadedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE) .addComponent(categoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(totalVisitsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE) + .addGap(9, 9, 9) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(totalVisitsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE) + .addComponent(knownAccountTypesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(1, 1, 1))) @@ -150,6 +155,7 @@ private void initComponents() { private javax.swing.JLabel domainNameLabel; private javax.swing.JLabel domainNotabilityLabel; private javax.swing.JLabel filesDownloadedLabel; + private javax.swing.JLabel knownAccountTypesLabel; private javax.swing.JLabel numberOfImagesLabel; private javax.swing.JLabel pagesLabel; private javax.swing.JLabel sampleImageLabel; @@ -164,8 +170,7 @@ private void initComponents() { "DomainSummaryPanel.totalPages.text=Total page views: ", "DomainSummaryPanel.downloads.text=Files downloaded: ", "DomainSummaryPanel.notability.text=Previously tagged as notable: ", - "DomainSummaryPanel.unknown.text=User role: Unknown", - "DomainSummaryPanel.known.text=User role: Known account type(s)", + "DomainSummaryPanel.userRole.text=Account type: ", "DomainSummaryPanel.category.text=Category: ", "DomainSummaryPanel.loadingImages.text=Loading thumbnail...", "DomainSummaryPanel.no.text=No", @@ -188,6 +193,7 @@ public Component getListCellRendererComponent(JList<? extends DomainWrapper> lis totalVisitsLabel.setText(Bundle.DomainSummaryPanel_totalPages_text() + value.getResultDomain().getTotalPageViews()); pagesLabel.setText(Bundle.DomainSummaryPanel_pages_text() + value.getResultDomain().getPageViewsInLast60Days()); filesDownloadedLabel.setText(Bundle.DomainSummaryPanel_downloads_text() + value.getResultDomain().getFilesDownloaded()); + knownAccountTypesLabel.setText(Bundle.DomainSummaryPanel_userRole_text() + value.getResultDomain().getAccountTypes()); if (value.getThumbnail() == null) { numberOfImagesLabel.setText(Bundle.DomainSummaryPanel_loadingImages_text()); sampleImageLabel.setIcon(null);