Merge pull request #210 from hapifhir/interceptor-wiring-rework
Combining Repository Validating Interceptor and IG installments
This commit is contained in:
@@ -10,8 +10,8 @@ import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
|||||||
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
||||||
import ca.uhn.fhir.jpa.bulk.provider.BulkDataExportProvider;
|
import ca.uhn.fhir.jpa.bulk.provider.BulkDataExportProvider;
|
||||||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc;
|
import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc;
|
||||||
|
import ca.uhn.fhir.jpa.packages.PackageInstallOutcomeJson;
|
||||||
import ca.uhn.fhir.jpa.packages.PackageInstallationSpec;
|
import ca.uhn.fhir.jpa.packages.PackageInstallationSpec;
|
||||||
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.*;
|
import ca.uhn.fhir.jpa.provider.*;
|
||||||
@@ -97,6 +97,9 @@ public class BaseJpaRestfulServer extends RestfulServer {
|
|||||||
@Autowired
|
@Autowired
|
||||||
ApplicationContext myApplicationContext;
|
ApplicationContext myApplicationContext;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
IRepositoryValidationInterceptorFactory factory;
|
||||||
|
|
||||||
public BaseJpaRestfulServer() {
|
public BaseJpaRestfulServer() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -351,28 +354,24 @@ public class BaseJpaRestfulServer extends RestfulServer {
|
|||||||
if (appProperties.getImplementationGuides() != null) {
|
if (appProperties.getImplementationGuides() != null) {
|
||||||
Map<String, AppProperties.ImplementationGuide> guides = appProperties.getImplementationGuides();
|
Map<String, AppProperties.ImplementationGuide> guides = appProperties.getImplementationGuides();
|
||||||
for (Map.Entry<String, AppProperties.ImplementationGuide> guide : guides.entrySet()) {
|
for (Map.Entry<String, AppProperties.ImplementationGuide> guide : guides.entrySet()) {
|
||||||
packageInstallerSvc.install(new PackageInstallationSpec()
|
packageInstallerSvc.install(new PackageInstallationSpec()
|
||||||
.setPackageUrl(guide.getValue().getUrl())
|
.setPackageUrl(guide.getValue().getUrl())
|
||||||
.setName(guide.getValue().getName())
|
.setName(guide.getValue().getName())
|
||||||
.setVersion(guide.getValue().getVersion())
|
.setVersion(guide.getValue().getVersion())
|
||||||
.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL));
|
.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(factory != null) {
|
||||||
|
interceptorService.registerInterceptor(factory.buildUsingStoredStructureDefinitions());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (appProperties.getLastn_enabled()) {
|
if (appProperties.getLastn_enabled()) {
|
||||||
daoConfig.setLastNEnabled(true);
|
daoConfig.setLastNEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
daoConfig.getModelConfig().setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());
|
daoConfig.getModelConfig().setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());
|
||||||
|
|
||||||
// Repository Validating Interceptor
|
|
||||||
if (Boolean.TRUE.equals(appProperties.getEnable_repository_validating_interceptor())) {
|
|
||||||
RepositoryValidationInterceptorFactory repositoryValidationInterceptorFactory = myApplicationContext.getBean(RepositoryValidationInterceptorFactory.class);
|
|
||||||
RepositoryValidatingInterceptor interceptor = repositoryValidationInterceptorFactory.build();
|
|
||||||
interceptorService.registerInterceptor(interceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,13 +132,6 @@ public class FhirServerConfigCommon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Lazy
|
|
||||||
public RepositoryValidationInterceptorFactory repositoryValidationInterceptorFactory() {
|
|
||||||
return new RepositoryValidationInterceptorFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Primary
|
@Primary
|
||||||
@Bean
|
@Bean
|
||||||
public HibernatePropertiesProvider jpaStarterDialectProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) {
|
public HibernatePropertiesProvider jpaStarterDialectProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||||
|
|
||||||
|
public interface IRepositoryValidationInterceptorFactory {
|
||||||
|
RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions();
|
||||||
|
|
||||||
|
RepositoryValidatingInterceptor build();
|
||||||
|
}
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package ca.uhn.fhir.jpa.starter;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.jpa.interceptor.validation.IRepositoryValidatingRule;
|
|
||||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class can be customized to enable the {@link ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor}
|
|
||||||
* on this server.
|
|
||||||
*
|
|
||||||
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
|
||||||
* in order to use this class.
|
|
||||||
*/
|
|
||||||
public class RepositoryValidationInterceptorFactory {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ApplicationContext myApplicationContext;
|
|
||||||
@Autowired
|
|
||||||
private FhirContext myFhirContext;
|
|
||||||
|
|
||||||
public RepositoryValidatingInterceptor build() {
|
|
||||||
RepositoryValidatingRuleBuilder ruleBuilder = myApplicationContext.getBean(RepositoryValidatingRuleBuilder.class);
|
|
||||||
|
|
||||||
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
|
||||||
// of enabling validation for all Patient resources
|
|
||||||
ruleBuilder.forResourcesOfType("Patient").requireValidationToDeclaredProfiles();
|
|
||||||
|
|
||||||
// Do not customize below this line
|
|
||||||
List<IRepositoryValidatingRule> rules = ruleBuilder.build();
|
|
||||||
return new RepositoryValidatingInterceptor(myFhirContext, rules);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.IRepositoryValidatingRule;
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class can be customized to enable the {@link RepositoryValidatingInterceptor}
|
||||||
|
* on this server.
|
||||||
|
* <p>
|
||||||
|
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||||
|
* in order to use this class.
|
||||||
|
*/
|
||||||
|
@ConditionalOnProperty(prefix = "hapi.fhir", name = "enable_repository_validating_interceptor", havingValue = "true")
|
||||||
|
@Configuration
|
||||||
|
@Conditional(OnDSTU3Condition.class)
|
||||||
|
public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryValidationInterceptorFactory {
|
||||||
|
|
||||||
|
private final FhirContext fhirContext;
|
||||||
|
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||||
|
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||||
|
|
||||||
|
public RepositoryValidationInterceptorFactoryDstu3(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||||
|
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||||
|
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||||
|
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||||
|
|
||||||
|
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||||
|
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||||
|
.stream()
|
||||||
|
.map(StructureDefinition.class::cast)
|
||||||
|
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||||
|
|
||||||
|
structureDefintions.entrySet().forEach(structureDefinitionListEntry ->
|
||||||
|
{
|
||||||
|
String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||||
|
repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||||
|
});
|
||||||
|
|
||||||
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
|
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepositoryValidatingInterceptor build() {
|
||||||
|
|
||||||
|
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||||
|
// of enabling validation for all Patient resources
|
||||||
|
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||||
|
|
||||||
|
// Do not customize below this line
|
||||||
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
|
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.IRepositoryValidatingRule;
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class can be customized to enable the {@link ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor}
|
||||||
|
* on this server.
|
||||||
|
* <p>
|
||||||
|
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||||
|
* in order to use this class.
|
||||||
|
*/
|
||||||
|
@ConditionalOnProperty(prefix = "hapi.fhir", name = "enable_repository_validating_interceptor", havingValue = "true")
|
||||||
|
@Configuration
|
||||||
|
@Conditional(OnR4Condition.class)
|
||||||
|
public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryValidationInterceptorFactory {
|
||||||
|
|
||||||
|
private final FhirContext fhirContext;
|
||||||
|
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||||
|
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||||
|
|
||||||
|
public RepositoryValidationInterceptorFactoryR4(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||||
|
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||||
|
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||||
|
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||||
|
|
||||||
|
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||||
|
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||||
|
.stream()
|
||||||
|
.map(StructureDefinition.class::cast)
|
||||||
|
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||||
|
|
||||||
|
structureDefintions.entrySet().forEach(structureDefinitionListEntry ->
|
||||||
|
{
|
||||||
|
String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||||
|
repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||||
|
});
|
||||||
|
|
||||||
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
|
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepositoryValidatingInterceptor build() {
|
||||||
|
|
||||||
|
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||||
|
// of enabling validation for all Patient resources
|
||||||
|
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||||
|
|
||||||
|
// Do not customize below this line
|
||||||
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
|
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.IRepositoryValidatingRule;
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||||
|
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class can be customized to enable the {@link RepositoryValidatingInterceptor}
|
||||||
|
* on this server.
|
||||||
|
* <p>
|
||||||
|
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||||
|
* in order to use this class.
|
||||||
|
*/
|
||||||
|
@ConditionalOnProperty(prefix = "hapi.fhir", name = "enable_repository_validating_interceptor", havingValue = "true")
|
||||||
|
@Configuration
|
||||||
|
@Conditional(OnR5Condition.class)
|
||||||
|
public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryValidationInterceptorFactory {
|
||||||
|
|
||||||
|
private final FhirContext fhirContext;
|
||||||
|
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||||
|
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||||
|
|
||||||
|
public RepositoryValidationInterceptorFactoryR5(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||||
|
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||||
|
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||||
|
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||||
|
|
||||||
|
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||||
|
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||||
|
.stream()
|
||||||
|
.map(StructureDefinition.class::cast)
|
||||||
|
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||||
|
|
||||||
|
structureDefintions.entrySet().forEach(structureDefinitionListEntry ->
|
||||||
|
{
|
||||||
|
String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||||
|
repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||||
|
});
|
||||||
|
|
||||||
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
|
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepositoryValidatingInterceptor build() {
|
||||||
|
|
||||||
|
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||||
|
// of enabling validation for all Patient resources
|
||||||
|
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||||
|
|
||||||
|
// Do not customize below this line
|
||||||
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
|
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user