WIP add fields and returnTemplateData parametera to office reads
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
package org.mifosng.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@@ -20,8 +18,7 @@ public class OfficeData implements Serializable {
|
||||
private String hierarchy;
|
||||
private String parentName;
|
||||
private Long parentId;
|
||||
|
||||
private List<OfficeData> allowedParents = new ArrayList<OfficeData>();
|
||||
private OfficeTemplateData officeTemplateData;
|
||||
|
||||
public OfficeData() {
|
||||
//
|
||||
@@ -87,14 +84,6 @@ public class OfficeData implements Serializable {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public List<OfficeData> getAllowedParents() {
|
||||
return allowedParents;
|
||||
}
|
||||
|
||||
public void setAllowedParents(List<OfficeData> allowedParents) {
|
||||
this.allowedParents = allowedParents;
|
||||
}
|
||||
|
||||
public String getHierarchy() {
|
||||
return hierarchy;
|
||||
}
|
||||
@@ -102,4 +91,12 @@ public class OfficeData implements Serializable {
|
||||
public void setHierarchy(String hierarchy) {
|
||||
this.hierarchy = hierarchy;
|
||||
}
|
||||
|
||||
public OfficeTemplateData getOfficeTemplateData() {
|
||||
return officeTemplateData;
|
||||
}
|
||||
|
||||
public void setOfficeTemplateData(OfficeTemplateData officeTemplateData) {
|
||||
this.officeTemplateData = officeTemplateData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.mifosng.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlRootElement(name = "office lookup")
|
||||
public class OfficeLookup implements Serializable {
|
||||
|
||||
private Long id;
|
||||
private String name;
|
||||
|
||||
public OfficeLookup() {
|
||||
//
|
||||
}
|
||||
|
||||
public OfficeLookup(final Long id, final String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.mifosng.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
@XmlRootElement(name = "office template")
|
||||
public class OfficeTemplateData implements Serializable {
|
||||
|
||||
private LocalDate defaultOpeningDate;
|
||||
private List<OfficeLookup> allowedParents = new ArrayList<OfficeLookup>();
|
||||
|
||||
public OfficeTemplateData() {
|
||||
//
|
||||
}
|
||||
|
||||
public OfficeTemplateData(final LocalDate defaultOpeningDate,
|
||||
final List<OfficeLookup> allowedParents) {
|
||||
this.defaultOpeningDate = defaultOpeningDate;
|
||||
this.allowedParents = allowedParents;
|
||||
}
|
||||
|
||||
public LocalDate getDefaultOpeningDate() {
|
||||
return defaultOpeningDate;
|
||||
}
|
||||
|
||||
public void setDefaultOpeningDate(LocalDate defaultOpeningDate) {
|
||||
this.defaultOpeningDate = defaultOpeningDate;
|
||||
}
|
||||
|
||||
public List<OfficeLookup> getAllowedParents() {
|
||||
return allowedParents;
|
||||
}
|
||||
|
||||
public void setAllowedParents(List<OfficeLookup> allowedParents) {
|
||||
this.allowedParents = allowedParents;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,9 +13,12 @@ import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.codehaus.jackson.JsonGenerationException;
|
||||
import org.codehaus.jackson.map.JsonMappingException;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.ser.FilterProvider;
|
||||
import org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter;
|
||||
@@ -23,10 +26,10 @@ import org.codehaus.jackson.map.ser.impl.SimpleFilterProvider;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.data.EntityIdentifier;
|
||||
import org.mifosng.data.OfficeData;
|
||||
import org.mifosng.data.OfficeList;
|
||||
import org.mifosng.data.OfficeTemplateData;
|
||||
import org.mifosng.data.command.OfficeCommand;
|
||||
import org.mifosng.platform.ReadExtraDataAndReportingServiceImpl;
|
||||
import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
|
||||
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
|
||||
import org.mifosng.platform.organisation.service.OfficeReadPlatformService;
|
||||
import org.mifosng.platform.organisation.service.OfficeWritePlatformService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -42,73 +45,56 @@ public class OfficeApiResource {
|
||||
|
||||
private final static Logger logger = LoggerFactory
|
||||
.getLogger(OfficeApiResource.class);
|
||||
@Autowired
|
||||
|
||||
@Autowired
|
||||
private OfficeReadPlatformService readPlatformService;
|
||||
|
||||
@Autowired
|
||||
private OfficeWritePlatformService writePlatformService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private ApiDataConversionService apiDataConversionService;
|
||||
/*
|
||||
@GET
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public Response retrieveOffices() {
|
||||
|
||||
Collection<OfficeData> offices = this.readPlatformService.retrieveAllOffices();
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveOffices(@QueryParam("fields") String fields) {
|
||||
|
||||
return Response.ok().entity(new OfficeList(offices)).build();
|
||||
}*/
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public String retrieveOffices() {
|
||||
|
||||
Collection<OfficeData> offices = this.readPlatformService.retrieveAllOffices();
|
||||
Collection<OfficeData> offices = this.readPlatformService
|
||||
.retrieveAllOffices();
|
||||
|
||||
Set<String> filterProperties = new HashSet<String>();
|
||||
/*StringTokenizer st = new StringTokenizer(fields, ",");
|
||||
while (st.hasMoreTokens()) {
|
||||
filterProperties.add(st.nextToken());
|
||||
}*/
|
||||
filterProperties.add("name");
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",
|
||||
SimpleBeanPropertyFilter.filterOutAllExcept(filterProperties));
|
||||
Set<String> excludeFields = new HashSet<String>();
|
||||
excludeFields.add("officeTemplateData");
|
||||
return convertDataObjectJSON(offices, fields, excludeFields);
|
||||
|
||||
try {
|
||||
String json = mapper.filteredWriter(filters).writeValueAsString(offices);
|
||||
logger.info("office list json: " + json);
|
||||
return json;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return e.getMessage();
|
||||
}
|
||||
|
||||
//return Response.ok().entity(new OfficeList(offices)).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public Response retrieveOfficeTemplate() {
|
||||
|
||||
OfficeData newOfficeTemplate = this.readPlatformService.retrieveNewOfficeTemplate();
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveOfficeTemplate() {
|
||||
|
||||
OfficeData officeData = new OfficeData();
|
||||
OfficeTemplateData officeTemplateData = this.readPlatformService
|
||||
.retrieveNewOfficeTemplate();
|
||||
officeData.setOfficeTemplateData(officeTemplateData);
|
||||
|
||||
Set<String> excludeFields = new HashSet<String>();
|
||||
String includeFields = "officeTemplateData";
|
||||
return convertDataObjectJSON(officeData, includeFields, excludeFields);
|
||||
}
|
||||
|
||||
return Response.ok().entity(newOfficeTemplate).build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
@Produces({ MediaType.APPLICATION_JSON})
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response createOffice(final OfficeCommand command) {
|
||||
|
||||
LocalDate openingDate = apiDataConversionService.convertFrom(command.getOpeningDateFormatted(), "openingDateFormatted", command.getDateFormat());
|
||||
|
||||
LocalDate openingDate = apiDataConversionService.convertFrom(
|
||||
command.getOpeningDateFormatted(), "openingDateFormatted",
|
||||
command.getDateFormat());
|
||||
command.setOpeningDate(openingDate);
|
||||
|
||||
|
||||
Long officeId = this.writePlatformService.createOffice(command);
|
||||
|
||||
return Response.ok().entity(new EntityIdentifier(officeId)).build();
|
||||
@@ -117,21 +103,55 @@ public class OfficeApiResource {
|
||||
@GET
|
||||
@Path("{officeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
@Produces({ MediaType.APPLICATION_JSON})
|
||||
public Response retreiveOffice(@PathParam("officeId") final Long officeId) {
|
||||
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retreiveOffice(@PathParam("officeId") final Long officeId,
|
||||
@QueryParam("fields") String fields,
|
||||
@QueryParam("returnTemplateData") String returnTemplateData) {
|
||||
|
||||
Boolean isReturnTemplate = false;
|
||||
if (returnTemplateData != null
|
||||
&& returnTemplateData.equalsIgnoreCase("true")) {
|
||||
isReturnTemplate = true;
|
||||
}
|
||||
|
||||
Boolean isIncludeFields = false;
|
||||
if (!(fields == null || fields.equals(""))) {
|
||||
isIncludeFields = true;
|
||||
}
|
||||
|
||||
OfficeData office = this.readPlatformService.retrieveOffice(officeId);
|
||||
|
||||
return Response.ok().entity(office).build();
|
||||
}
|
||||
if (isReturnTemplate) {
|
||||
OfficeTemplateData officeTemplateData = this.readPlatformService
|
||||
.retrieveExistingOfficeTemplate(officeId);
|
||||
office.setOfficeTemplateData(officeTemplateData);
|
||||
}
|
||||
|
||||
String includeFields = fields;
|
||||
Set<String> excludeFields = new HashSet<String>();
|
||||
if (isIncludeFields) {
|
||||
if (isReturnTemplate) {
|
||||
includeFields += ",officeTemplateData";
|
||||
}
|
||||
} else {
|
||||
if (!(isReturnTemplate)) {
|
||||
excludeFields.add("officeTemplateData");
|
||||
}
|
||||
}
|
||||
|
||||
return convertDataObjectJSON(office, includeFields, excludeFields);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{officeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
@Produces({ MediaType.APPLICATION_JSON})
|
||||
public Response updateOffice(@PathParam("officeId") final Long officeId, final OfficeCommand command) {
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response updateOffice(@PathParam("officeId") final Long officeId,
|
||||
final OfficeCommand command) {
|
||||
|
||||
LocalDate openingDate = apiDataConversionService.convertFrom(command.getOpeningDateFormatted(), "openingDateFormatted", command.getDateFormat());
|
||||
LocalDate openingDate = apiDataConversionService.convertFrom(
|
||||
command.getOpeningDateFormatted(), "openingDateFormatted",
|
||||
command.getDateFormat());
|
||||
command.setOpeningDate(openingDate);
|
||||
command.setId(officeId);
|
||||
|
||||
@@ -139,4 +159,53 @@ public class OfficeApiResource {
|
||||
|
||||
return Response.ok().entity(new EntityIdentifier(entityId)).build();
|
||||
}
|
||||
|
||||
private String convertDataObjectJSON(Object dataObject, String fields,
|
||||
Set<String> excludeFields) {
|
||||
|
||||
if (excludeFields == null) {
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.excludeFields.null",
|
||||
"JPW (might be wrong error msg) Parameter excludeFields is unexpectedly null.",
|
||||
null);
|
||||
}
|
||||
|
||||
String json = "";
|
||||
String myFilter = "myFilter";
|
||||
Set<String> includeFields = new HashSet<String>();
|
||||
FilterProvider filters = null;
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
if (fields == null || fields.equals("")) {
|
||||
filters = new SimpleFilterProvider().addFilter(myFilter,
|
||||
SimpleBeanPropertyFilter.serializeAllExcept(excludeFields));
|
||||
|
||||
} else {
|
||||
|
||||
StringTokenizer st = new StringTokenizer(fields, ",");
|
||||
while (st.hasMoreTokens()) {
|
||||
includeFields.add(st.nextToken().trim());
|
||||
}
|
||||
|
||||
filters = new SimpleFilterProvider().addFilter(myFilter,
|
||||
SimpleBeanPropertyFilter.filterOutAllExcept(includeFields));
|
||||
}
|
||||
|
||||
try {
|
||||
json = mapper.filteredWriter(filters)
|
||||
.writeValueAsString(dataObject);
|
||||
} catch (JsonGenerationException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (JsonMappingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
logger.info("office list json: " + json);
|
||||
return json;
|
||||
}
|
||||
|
||||
}
|
||||
+7
-1
@@ -3,12 +3,18 @@ package org.mifosng.platform.organisation.service;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosng.data.OfficeData;
|
||||
import org.mifosng.data.OfficeLookup;
|
||||
import org.mifosng.data.OfficeTemplateData;
|
||||
|
||||
public interface OfficeReadPlatformService {
|
||||
|
||||
Collection<OfficeData> retrieveAllOffices();
|
||||
|
||||
Collection<OfficeLookup> retrieveAllOfficesForLookup();
|
||||
|
||||
OfficeData retrieveOffice(Long officeId);
|
||||
|
||||
OfficeData retrieveNewOfficeTemplate();
|
||||
OfficeTemplateData retrieveNewOfficeTemplate();
|
||||
|
||||
OfficeTemplateData retrieveExistingOfficeTemplate(Long officeId);
|
||||
}
|
||||
+89
-36
@@ -8,9 +8,10 @@ import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.data.OfficeData;
|
||||
import org.mifosng.data.OfficeLookup;
|
||||
import org.mifosng.data.OfficeTemplateData;
|
||||
import org.mifosng.platform.exceptions.PlatformResourceNotFoundException;
|
||||
import org.mifosng.platform.security.PlatformSecurityContext;
|
||||
import org.mifosng.platform.user.domain.AppUser;
|
||||
@@ -27,7 +28,8 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService
|
||||
private final PlatformSecurityContext context;
|
||||
|
||||
@Autowired
|
||||
public OfficeReadPlatformServiceImpl(final PlatformSecurityContext context, final DataSource dataSource) {
|
||||
public OfficeReadPlatformServiceImpl(final PlatformSecurityContext context,
|
||||
final DataSource dataSource) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
|
||||
}
|
||||
@@ -51,24 +53,62 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService
|
||||
Long parentId = rs.getLong("parentId");
|
||||
String parentName = rs.getString("parentName");
|
||||
|
||||
return new OfficeData(id, name, externalId, openingDate, hierarchy, parentId, parentName);
|
||||
return new OfficeData(id, name, externalId, openingDate, hierarchy,
|
||||
parentId, parentName);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class OfficeLookupMapper implements
|
||||
RowMapper<OfficeLookup> {
|
||||
|
||||
public String officeLookupSchema() {
|
||||
return " o.id as id, o.name as name from org_office o ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfficeLookup mapRow(final ResultSet rs, final int rowNum)
|
||||
throws SQLException {
|
||||
|
||||
Long id = rs.getLong("id");
|
||||
String name = rs.getString("name");
|
||||
|
||||
return new OfficeLookup(id, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OfficeData> retrieveAllOffices() {
|
||||
|
||||
|
||||
AppUser currentUser = context.authenticatedUser();
|
||||
|
||||
|
||||
String hierarchy = currentUser.getOffice().getHierarchy();
|
||||
String hierarchySearchString = hierarchy + "%";
|
||||
|
||||
OfficeMapper rm = new OfficeMapper();
|
||||
String sql = "select " + rm.officeSchema() + "where o.org_id = ? and o.hierarchy like ? order by o.hierarchy";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] {currentUser.getOrganisation().getId(), hierarchySearchString});
|
||||
OfficeMapper rm = new OfficeMapper();
|
||||
String sql = "select "
|
||||
+ rm.officeSchema()
|
||||
+ "where o.org_id = ? and o.hierarchy like ? order by o.hierarchy";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] {
|
||||
currentUser.getOrganisation().getId(), hierarchySearchString });
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<OfficeLookup> retrieveAllOfficesForLookup() {
|
||||
AppUser currentUser = context.authenticatedUser();
|
||||
|
||||
String hierarchy = currentUser.getOffice().getHierarchy();
|
||||
String hierarchySearchString = hierarchy + "%";
|
||||
|
||||
OfficeLookupMapper rm = new OfficeLookupMapper();
|
||||
String sql = "select "
|
||||
+ rm.officeLookupSchema()
|
||||
+ "where o.org_id = ? and o.hierarchy like ? order by o.hierarchy";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] {
|
||||
currentUser.getOrganisation().getId(), hierarchySearchString });
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfficeData retrieveOffice(final Long officeId) {
|
||||
|
||||
@@ -83,38 +123,51 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService
|
||||
rm, new Object[] { currentUser.getOrganisation().getId(),
|
||||
officeId });
|
||||
|
||||
List<OfficeData> allowedParents = new ArrayList<OfficeData>();
|
||||
|
||||
if (StringUtils.isNotBlank(selectedOffice.getParentName())) {
|
||||
Collection<OfficeData> allOffices = retrieveAllOffices();
|
||||
|
||||
for (OfficeData office : allOffices) {
|
||||
|
||||
if (!office.getId().equals(selectedOffice.getId())) {
|
||||
allowedParents.add(office);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedOffice.setAllowedParents(allowedParents);
|
||||
|
||||
return selectedOffice;
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
throw new PlatformResourceNotFoundException("error.msg.office.id.invalid", "Office with identifier {0} does not exist.", officeId);
|
||||
throw new PlatformResourceNotFoundException(
|
||||
"error.msg.office.id.invalid",
|
||||
"Office with identifier {0} does not exist.", officeId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfficeData retrieveNewOfficeTemplate() {
|
||||
|
||||
public OfficeTemplateData retrieveNewOfficeTemplate() {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
List<OfficeData> allowedParents = new ArrayList<OfficeData>(retrieveAllOffices());
|
||||
|
||||
OfficeData officeTemplate = new OfficeData();
|
||||
officeTemplate.setAllowedParents(allowedParents);
|
||||
officeTemplate.setOpeningDate(new LocalDate());
|
||||
|
||||
return officeTemplate;
|
||||
|
||||
List<OfficeLookup> parentLookups = new ArrayList<OfficeLookup>(
|
||||
retrieveAllOfficesForLookup());
|
||||
|
||||
OfficeTemplateData officeTemplateData = new OfficeTemplateData();
|
||||
officeTemplateData.setAllowedParents(parentLookups);
|
||||
officeTemplateData.setDefaultOpeningDate(new LocalDate());
|
||||
|
||||
return officeTemplateData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfficeTemplateData retrieveExistingOfficeTemplate(Long officeId) {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
List<OfficeLookup> parentLookups = new ArrayList<OfficeLookup>(
|
||||
retrieveAllOfficesForLookup());
|
||||
List<OfficeLookup> filterParentLookups = new ArrayList<OfficeLookup>();
|
||||
|
||||
for (OfficeLookup office : parentLookups) {
|
||||
|
||||
if (!office.getId().equals(officeId)) {
|
||||
filterParentLookups.add(office);
|
||||
}
|
||||
}
|
||||
|
||||
OfficeTemplateData officeTemplateData = new OfficeTemplateData();
|
||||
officeTemplateData.setAllowedParents(filterParentLookups);
|
||||
officeTemplateData.setDefaultOpeningDate(new LocalDate());
|
||||
|
||||
return officeTemplateData;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user