Added support for multiple block/follow lists.

Any list with the following prefix will be used in block/follow logic:

blockedNames
blockedAddresses
followedNames

For instance, any names in a list named "blockedNames_CustomBlockList" would also be blocked, along with those in the standard "blockedNames" list.

This will ultimately allow apps to offer custom block/follow lists to users (once list functionality is added to the Q-Apps API).
This commit is contained in:
CalDescent 2023-04-02 14:42:49 +01:00
parent 2086a2c476
commit 35def54ecc
4 changed files with 217 additions and 5 deletions

View File

@ -52,6 +52,15 @@ public class ResourceList {
String jsonString = ResourceList.listToJSONString(this.list); String jsonString = ResourceList.listToJSONString(this.list);
Path filePath = this.getFilePath(); Path filePath = this.getFilePath();
// Don't create list if it's empty
if (this.list != null && this.list.isEmpty()) {
if (filePath != null && Files.exists(filePath)) {
// Delete empty list
Files.delete(filePath);
}
return;
}
// Create parent directory if needed // Create parent directory if needed
try { try {
Files.createDirectories(filePath.getParent()); Files.createDirectories(filePath.getParent());
@ -109,6 +118,13 @@ public class ResourceList {
this.list.remove(resource); this.list.remove(resource);
} }
public void clear() {
if (this.list == null) {
return;
}
this.list.clear();
}
public boolean contains(String resource, boolean caseSensitive) { public boolean contains(String resource, boolean caseSensitive) {
if (resource == null || this.list == null) { if (resource == null || this.list == null) {
return false; return false;

View File

@ -2,8 +2,11 @@ package org.qortal.list;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.qortal.settings.Settings;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -18,6 +21,7 @@ public class ResourceListManager {
public ResourceListManager() { public ResourceListManager() {
this.lists = this.fetchLists();
} }
public static synchronized ResourceListManager getInstance() { public static synchronized ResourceListManager getInstance() {
@ -27,6 +31,38 @@ public class ResourceListManager {
return instance; return instance;
} }
public static synchronized void reset() {
if (instance != null) {
instance = null;
}
}
private List<ResourceList> fetchLists() {
List<ResourceList> lists = new ArrayList<>();
Path listsPath = Paths.get(Settings.getInstance().getListsPath());
if (listsPath.toFile().isDirectory()) {
String[] files = listsPath.toFile().list();
for (String fileName : files) {
try {
// Remove .json extension
if (fileName.endsWith(".json")) {
fileName = fileName.substring(0, fileName.length() - 5);
}
ResourceList list = new ResourceList(fileName);
if (list != null) {
lists.add(list);
}
} catch (IOException e) {
// Ignore this list
}
}
}
return lists;
}
private ResourceList getList(String listName) { private ResourceList getList(String listName) {
for (ResourceList list : this.lists) { for (ResourceList list : this.lists) {
if (Objects.equals(list.getName(), listName)) { if (Objects.equals(list.getName(), listName)) {
@ -48,6 +84,18 @@ public class ResourceListManager {
} }
private List<ResourceList> getListsByPrefix(String listNamePrefix) {
List<ResourceList> lists = new ArrayList<>();
for (ResourceList list : this.lists) {
if (list != null && list.getName() != null && list.getName().startsWith(listNamePrefix)) {
lists.add(list);
}
}
return lists;
}
public boolean addToList(String listName, String item, boolean save) { public boolean addToList(String listName, String item, boolean save) {
ResourceList list = this.getList(listName); ResourceList list = this.getList(listName);
if (list == null) { if (list == null) {
@ -95,6 +143,16 @@ public class ResourceListManager {
return list.contains(item, caseSensitive); return list.contains(item, caseSensitive);
} }
public boolean listWithPrefixContains(String listNamePrefix, String item, boolean caseSensitive) {
List<ResourceList> lists = getListsByPrefix(listNamePrefix);
for (ResourceList list : lists) {
if (list.contains(item, caseSensitive)) {
return true;
}
}
return false;
}
public void saveList(String listName) { public void saveList(String listName) {
ResourceList list = this.getList(listName); ResourceList list = this.getList(listName);
if (list == null) { if (list == null) {
@ -133,6 +191,15 @@ public class ResourceListManager {
return list.getList(); return list.getList();
} }
public List<String> getStringsInListsWithPrefix(String listNamePrefix) {
List<String> items = new ArrayList<>();
List<ResourceList> lists = getListsByPrefix(listNamePrefix);
for (ResourceList list : lists) {
items.addAll(list.getList());
}
return items;
}
public int getItemCountForList(String listName) { public int getItemCountForList(String listName) {
ResourceList list = this.getList(listName); ResourceList list = this.getList(listName);
if (list == null) { if (list == null) {

View File

@ -9,26 +9,26 @@ public class ListUtils {
/* Blocking */ /* Blocking */
public static List<String> blockedNames() { public static List<String> blockedNames() {
return ResourceListManager.getInstance().getStringsInList("blockedNames"); return ResourceListManager.getInstance().getStringsInListsWithPrefix("blockedNames");
} }
public static boolean isNameBlocked(String name) { public static boolean isNameBlocked(String name) {
return ResourceListManager.getInstance().listContains("blockedNames", name, false); return ResourceListManager.getInstance().listWithPrefixContains("blockedNames", name, false);
} }
public static boolean isAddressBlocked(String address) { public static boolean isAddressBlocked(String address) {
return ResourceListManager.getInstance().listContains("blockedAddresses", address, true); return ResourceListManager.getInstance().listWithPrefixContains("blockedAddresses", address, true);
} }
/* Following */ /* Following */
public static List<String> followedNames() { public static List<String> followedNames() {
return ResourceListManager.getInstance().getStringsInList("followedNames"); return ResourceListManager.getInstance().getStringsInListsWithPrefix("followedNames");
} }
public static boolean isFollowingName(String name) { public static boolean isFollowingName(String name) {
return ResourceListManager.getInstance().listContains("followedNames", name, false); return ResourceListManager.getInstance().listWithPrefixContains("followedNames", name, false);
} }
public static int followedNamesCount() { public static int followedNamesCount() {

View File

@ -0,0 +1,129 @@
package org.qortal.test;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.qortal.list.ResourceList;
import org.qortal.list.ResourceListManager;
import org.qortal.repository.DataException;
import org.qortal.settings.Settings;
import org.qortal.test.common.Common;
import org.qortal.utils.ListUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import static org.junit.Assert.*;
public class ListTests {
@Before
public void beforeTest() throws DataException, IOException {
Common.useDefaultSettings();
this.cleanup();
}
@After
public void afterTest() throws DataException, IOException {
this.cleanup();
}
private void cleanup() throws IOException {
// Delete custom lists created by test methods
ResourceList followedNamesTestList = new ResourceList("followedNames_test");
followedNamesTestList.clear();
followedNamesTestList.save();
ResourceList blockedNamesTestList = new ResourceList("blockedNames_test");
blockedNamesTestList.clear();
blockedNamesTestList.save();
// Clear resource list manager instance
ResourceListManager.reset();
}
@Test
public void testSingleList() {
ResourceListManager resourceListManager = ResourceListManager.getInstance();
String listName = "followedNames_test";
String name = "testName";
resourceListManager.addToList(listName, name, false);
List<String> followedNames = resourceListManager.getStringsInList(listName);
assertEquals(1, followedNames.size());
assertEquals(followedNames.size(), ListUtils.followedNamesCount());
assertEquals(name, followedNames.get(0));
}
@Test
public void testListPrefix() {
ResourceListManager resourceListManager = ResourceListManager.getInstance();
List<String> initialFollowedNames = resourceListManager.getStringsInListsWithPrefix("followedNames");
assertEquals(0, initialFollowedNames.size());
List<String> initialBlockedNames = resourceListManager.getStringsInListsWithPrefix("blockedNames");
assertEquals(0, initialBlockedNames.size());
// Add to multiple lists
resourceListManager.addToList("followedNames_CustomList1", "testName1", false);
resourceListManager.addToList("followedNames_CustomList1", "testName2", false);
resourceListManager.addToList("followedNames_CustomList2", "testName3", false);
resourceListManager.addToList("followedNames_CustomList3", "testName4", false);
resourceListManager.addToList("blockedNames_CustomList1", "testName5", false);
// Check followedNames
List<String> followedNames = resourceListManager.getStringsInListsWithPrefix("followedNames");
assertEquals(4, followedNames.size());
assertEquals(followedNames.size(), ListUtils.followedNamesCount());
assertTrue(followedNames.contains("testName1"));
assertTrue(followedNames.contains("testName2"));
assertTrue(followedNames.contains("testName3"));
assertTrue(followedNames.contains("testName4"));
assertFalse(followedNames.contains("testName5"));
// Check blockedNames
List<String> blockedNames = resourceListManager.getStringsInListsWithPrefix("blockedNames");
assertEquals(1, blockedNames.size());
assertEquals(blockedNames.size(), ListUtils.blockedNames().size());
assertTrue(blockedNames.contains("testName5"));
}
@Test
public void testDataPersistence() {
// Ensure lists are empty to begin with
assertEquals(0, ResourceListManager.getInstance().getStringsInListsWithPrefix("followedNames").size());
assertEquals(0, ResourceListManager.getInstance().getStringsInListsWithPrefix("blockedNames").size());
// Add some items to multiple lists
ResourceListManager.getInstance().addToList("followedNames_test", "testName1", true);
ResourceListManager.getInstance().addToList("followedNames_test", "testName2", true);
ResourceListManager.getInstance().addToList("blockedNames_test", "testName3", true);
// Ensure they are added
assertEquals(2, ResourceListManager.getInstance().getStringsInListsWithPrefix("followedNames").size());
assertEquals(1, ResourceListManager.getInstance().getStringsInListsWithPrefix("blockedNames").size());
// Clear local state
ResourceListManager.reset();
// Ensure items are automatically loaded back in from disk
assertEquals(2, ResourceListManager.getInstance().getStringsInListsWithPrefix("followedNames").size());
assertEquals(1, ResourceListManager.getInstance().getStringsInListsWithPrefix("blockedNames").size());
// Delete followedNames file
File followedNamesFile = Paths.get(Settings.getInstance().getListsPath(), "followedNames_test.json").toFile();
followedNamesFile.delete();
// Clear local state again
ResourceListManager.reset();
// Ensure only the blocked names are loaded back in
assertEquals(0, ResourceListManager.getInstance().getStringsInListsWithPrefix("followedNames").size());
assertEquals(1, ResourceListManager.getInstance().getStringsInListsWithPrefix("blockedNames").size());
}
}