Pull request #620: RED-6162 - fixed swagger url
Merge in RED/persistence-service from RED-6162 to master * commit 'cf6af4ba916b2666df6aab21730fc16fa69e2585': RED-5255 - ported to merged version - pmd fix RED-5255 - ported to merged version RED-6162 - fixed swagger url
This commit is contained in:
commit
e26fba1c63
@ -76,11 +76,9 @@ public class SecuredKeyCloakConfiguration extends KeycloakWebSecurityConfigurerA
|
||||
|
||||
web.ignoring().antMatchers("/actuator/health/**",
|
||||
"/redaction-gateway-v1/async/download/with-ott/**",
|
||||
"/api/async/download/with-ott/**",
|
||||
"/api/docs",
|
||||
"/api/docs/**",
|
||||
"/",
|
||||
"/api",
|
||||
"/redaction-gateway-v1/docs/**",
|
||||
"/redaction-gateway-v1/docs",
|
||||
"/redaction-gateway-v1",
|
||||
"/internal-api/**");
|
||||
|
||||
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
|
||||
|
||||
@ -38,7 +38,7 @@ public class GeneralConfigurationService {
|
||||
return GeneralConfigurationModel.builder()
|
||||
.auxiliaryName(auxiliaryName)
|
||||
.displayName(realm.getDisplayNameHtml())
|
||||
.forgotPasswordFunctionEnabled(realm.isResetPasswordAllowed())
|
||||
.forgotPasswordFunctionEnabled(realm.isResetPasswordAllowed() != null && realm.isResetPasswordAllowed())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@ -50,7 +50,6 @@ public class SwaggerAutoConfiguration {
|
||||
private static final String DESCRIPTION = "Description for redaction";
|
||||
private static final String VERSION = "1.0";
|
||||
private static final String OAUTH_NAME = "RED-OAUTH";
|
||||
|
||||
private static final String PROTOCOL_URL_FORMAT = "/auth/realms/%s/protocol/openid-connect";
|
||||
|
||||
@Autowired
|
||||
|
||||
@ -29,10 +29,10 @@ public class SwaggerHomeController {
|
||||
}
|
||||
|
||||
|
||||
@GetMapping({"/api", "/"})
|
||||
@GetMapping({"/redaction-gateway-v1", "/","redaction-gateway-v1/docs"})
|
||||
public void home(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
|
||||
response.sendRedirect(contextPath + "/api/docs/swagger-ui");
|
||||
response.sendRedirect(contextPath + "/redaction-gateway-v1/docs/swagger-ui");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
package com.iqser.red.service.persistence.management.v1.processor.acl;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@ -10,15 +10,15 @@ import lombok.Getter;
|
||||
|
||||
public class RedPermission extends AbstractPermission implements Permission {
|
||||
|
||||
public static final RedPermission VIEW_OBJECT = new RedPermission("VIEW_OBJECT", 1 << 0, 'V', 100); // 1
|
||||
public static final RedPermission VIEW_OBJECT = new RedPermission("VIEW_OBJECT", 1 << 0, 'V', 100, false); // 1
|
||||
|
||||
public static final RedPermission ACCESS_OBJECT = new RedPermission("ACCESS_OBJECT", 1 << 1, 'A', 200); // 2
|
||||
public static final RedPermission ACCESS_OBJECT = new RedPermission("ACCESS_OBJECT", 1 << 1, 'A', 200, false); // 2
|
||||
|
||||
public static final RedPermission REVIEW = new RedPermission("REVIEW", 1 << 2, 'W', 30); // 4
|
||||
public static final RedPermission REVIEW = new RedPermission("REVIEW", 1 << 2, 'W', 30, true); // 4
|
||||
|
||||
public static final RedPermission APPROVE = new RedPermission("APPROVE", 1 << 3, 'A', 20); // 8
|
||||
public static final RedPermission APPROVE = new RedPermission("APPROVE", 1 << 3, 'A', 20, true); // 8
|
||||
|
||||
public static final RedPermission OWNER = new RedPermission("OWNER", 1 << 4, 'O', 10); // 16
|
||||
public static final RedPermission OWNER = new RedPermission("OWNER", 1 << 4, 'O', 10, false); // 16
|
||||
|
||||
private static final List<RedPermission> ALL_PERMISSIONS = List.of(VIEW_OBJECT, ACCESS_OBJECT, REVIEW, APPROVE, OWNER);
|
||||
|
||||
@ -28,12 +28,16 @@ public class RedPermission extends AbstractPermission implements Permission {
|
||||
@Getter
|
||||
private final int sort;
|
||||
|
||||
@Getter
|
||||
private final boolean isChangeable;
|
||||
|
||||
protected RedPermission(String name, int mask, char code, int sort) {
|
||||
|
||||
protected RedPermission(String name, int mask, char code, int sort, boolean isChangeable) {
|
||||
|
||||
super(mask, code);
|
||||
this.name = name;
|
||||
this.sort = sort;
|
||||
this.isChangeable = isChangeable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -4,8 +4,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.permission.
|
||||
|
||||
public final class CustomPermissionConstants {
|
||||
|
||||
public static final CustomPermissionModel EVERYONE_ELSE = new CustomPermissionModel(-1, "EVERYONE_ELSE", 100_000);
|
||||
|
||||
public static final CustomPermissionModel EVERYONE_ELSE = new CustomPermissionModel(-1, "EVERYONE_ELSE", 100_000, true);
|
||||
|
||||
private CustomPermissionConstants() {}
|
||||
|
||||
|
||||
@ -25,28 +25,30 @@ public class CustomDossierPermissionsACLInitializer implements IACLInitializer {
|
||||
private static final String TARGET_OBJECT = "Dossier";
|
||||
private final CustomPermissionService customPermissionService;
|
||||
|
||||
|
||||
public void initialize() {
|
||||
if (customPermissionService.getCustomPermissionMappings(TARGET_OBJECT).isEmpty()) {
|
||||
|
||||
log.info("No custom permissions seeded. Adding defaults!");
|
||||
if (customPermissionService.getCustomPermissionMappings(TARGET_OBJECT).isEmpty()) {
|
||||
|
||||
List<CustomPermissionMappingModel> mappingModels = new ArrayList<>();
|
||||
log.info("No custom permissions seeded. Adding defaults!");
|
||||
|
||||
var viewObjectPermission = convert(RedPermission.VIEW_OBJECT);
|
||||
var viewObjectMappedPermissions = List.of(convert(RedPermission.OWNER), convert(RedPermission.APPROVE), convert(RedPermission.REVIEW), EVERYONE_ELSE);
|
||||
List<CustomPermissionMappingModel> mappingModels = new ArrayList<>();
|
||||
|
||||
mappingModels.add(new CustomPermissionMappingModel(viewObjectPermission, viewObjectMappedPermissions));
|
||||
var viewObjectPermission = convert(RedPermission.VIEW_OBJECT);
|
||||
var viewObjectMappedPermissions = List.of(convert(RedPermission.OWNER), convert(RedPermission.APPROVE), convert(RedPermission.REVIEW), EVERYONE_ELSE);
|
||||
|
||||
var accessObjectPermission = convert(RedPermission.ACCESS_OBJECT);
|
||||
var accessObjectMappedPermissions = List.of(convert(RedPermission.OWNER), convert(RedPermission.APPROVE), convert(RedPermission.REVIEW), EVERYONE_ELSE);
|
||||
mappingModels.add(new CustomPermissionMappingModel(viewObjectPermission, viewObjectMappedPermissions));
|
||||
|
||||
mappingModels.add(new CustomPermissionMappingModel(accessObjectPermission, accessObjectMappedPermissions));
|
||||
var accessObjectPermission = convert(RedPermission.ACCESS_OBJECT);
|
||||
var accessObjectMappedPermissions = List.of(convert(RedPermission.OWNER), convert(RedPermission.APPROVE), convert(RedPermission.REVIEW), EVERYONE_ELSE);
|
||||
|
||||
customPermissionService.saveCustomPermissionMappings(TARGET_OBJECT, mappingModels);
|
||||
mappingModels.add(new CustomPermissionMappingModel(accessObjectPermission, accessObjectMappedPermissions));
|
||||
|
||||
}
|
||||
customPermissionService.saveCustomPermissionMappings(TARGET_OBJECT, mappingModels);
|
||||
|
||||
customPermissionService.syncAllCustomPermissions();
|
||||
}
|
||||
|
||||
customPermissionService.syncAllCustomPermissions();
|
||||
}
|
||||
|
||||
|
||||
@ -59,7 +61,7 @@ public class CustomDossierPermissionsACLInitializer implements IACLInitializer {
|
||||
|
||||
private CustomPermissionModel convert(RedPermission redPermission) {
|
||||
|
||||
return new CustomPermissionModel(redPermission.getMask(), redPermission.getName(), redPermission.getSort());
|
||||
return new CustomPermissionModel(redPermission.getMask(), redPermission.getName(), redPermission.getSort(), redPermission.isChangeable());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -98,7 +99,7 @@ public class CustomPermissionService {
|
||||
|
||||
private CustomPermissionModel fromPermission(RedPermission redPermission) {
|
||||
|
||||
return new CustomPermissionModel(redPermission.getMask(), redPermission.getName(), redPermission.getSort());
|
||||
return new CustomPermissionModel(redPermission.getMask(), redPermission.getName(), redPermission.getSort(), redPermission.isChangeable());
|
||||
}
|
||||
|
||||
|
||||
@ -169,12 +170,29 @@ public class CustomPermissionService {
|
||||
public void saveCustomPermissionMappings(String targetObject, List<CustomPermissionMappingModel> customPermissionMappingModel) {
|
||||
|
||||
List<CustomPermissionEntity> entities = new ArrayList<>();
|
||||
// retrieve the defined configuration of permissions
|
||||
List<CustomPermissionMappingModel> existingPermissionDefined = getExistingPermissions(targetObject);
|
||||
|
||||
customPermissionMappingModel.forEach(c -> c.getMappedPermissions().forEach(value -> {
|
||||
CustomPermissionEntity entity = new CustomPermissionEntity(targetObject, c.getTargetPermission().getMask(), value.getMask());
|
||||
entities.add(entity);
|
||||
}));
|
||||
|
||||
for (CustomPermissionMappingModel permissionMappingModel : existingPermissionDefined) {
|
||||
CustomPermissionModel targetPermission = permissionMappingModel.getTargetPermission();
|
||||
List<CustomPermissionModel> mappedPermissionsDefined = permissionMappingModel.getMappedPermissions();
|
||||
//retrieve the permissions which can be changeable in order to add them
|
||||
List<Integer> mappedPermissionMasksNotChangeable = mappedPermissionsDefined.stream().filter(p -> !p.isChangeable()).map(CustomPermissionModel::getMask).toList();
|
||||
for (Integer mappedMask : mappedPermissionMasksNotChangeable) {
|
||||
Optional<CustomPermissionEntity> existingPermissionEntity = entities.stream()
|
||||
.filter(e -> e.getTargetPermissionMask() == targetPermission.getMask() && e.getExistingPermissionMask() == mappedMask)
|
||||
.findAny();
|
||||
if (existingPermissionEntity.isEmpty()) { //the required permission was disabled but it is not allowed
|
||||
entities.add(new CustomPermissionEntity(targetObject, targetPermission.getMask(), mappedMask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customPermissionRepository.deleteByTargetObject(targetObject);
|
||||
customPermissionRepository.saveAll(entities);
|
||||
|
||||
|
||||
@ -66,6 +66,9 @@ public class DossierTemplateEntity {
|
||||
@Column(name = "soft_delete_time")
|
||||
private OffsetDateTime softDeleteTime;
|
||||
|
||||
@Column(name = "keep_image_metadata")
|
||||
private boolean keepImageMetadata;
|
||||
|
||||
@Builder.Default
|
||||
@Column(columnDefinition = "text", name = "download_file_types")
|
||||
@Convert(converter = JSONDownloadFileTypeConverter.class)
|
||||
@ -104,4 +107,6 @@ public class DossierTemplateEntity {
|
||||
@Transient
|
||||
private DossierTemplateStatus dossierTemplateStatus;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -22,11 +22,13 @@ import com.iqser.red.service.pdftron.redaction.v1.api.model.RedactionResultMessa
|
||||
import com.iqser.red.service.pdftron.redaction.v1.api.model.RedactionType;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.ColorsService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPersistenceService;
|
||||
@ -65,16 +67,16 @@ public class DownloadPreparationService {
|
||||
DownloadReportCleanupService downloadReportCleanupService;
|
||||
ColorsService colorsService;
|
||||
FileManagementServiceSettings settings;
|
||||
DossierTemplatePersistenceService dossierTemplatePersistenceService;
|
||||
|
||||
|
||||
@Transactional
|
||||
public void createDownload(ReportResultMessage reportResultMessage) throws JsonProcessingException {
|
||||
|
||||
DownloadStatusEntity downloadStatus = downloadStatusPersistenceService.getStatus(reportResultMessage.getDownloadId());
|
||||
|
||||
DossierEntity dossier = downloadStatus.getDossier();
|
||||
|
||||
String storedPreviewColor = downloadStatus.getRedactionPreviewColor();
|
||||
var downloadStatus = downloadStatusPersistenceService.getStatus(reportResultMessage.getDownloadId());
|
||||
var dossier = downloadStatus.getDossier();
|
||||
var storedPreviewColor = downloadStatus.getRedactionPreviewColor();
|
||||
// var dossierTemplate = dossierTemplatePersistenceService.getDossierTemplate(dossier.getDossierTemplateId());
|
||||
|
||||
final String previewColor;
|
||||
if (storedPreviewColor == null || storedPreviewColor.isBlank()) {
|
||||
@ -96,6 +98,7 @@ public class DownloadPreparationService {
|
||||
.map(FileEntity::getId)
|
||||
.collect(Collectors.toSet()))
|
||||
.redactionPreviewColor(previewColor)
|
||||
// .keepImageMetaData(dossierTemplate.isKeepImageMetadata())
|
||||
.appliedRedactionColor(appliedRedactionColor)
|
||||
.build();
|
||||
|
||||
|
||||
@ -121,3 +121,5 @@ databaseChangeLog:
|
||||
file: db/changelog/tenant/sql/201-acl-duplicate-cleanup.sql
|
||||
- include:
|
||||
file: db/changelog/tenant/sql/202-acl-duplicate-cleanup.sql
|
||||
- include:
|
||||
file: db/changelog/tenant/sql/46-add-keep_image_metadata.sql
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
-- add new column
|
||||
alter table dossier_template add column keep_image_metadata BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
@ -56,6 +56,12 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
|
||||
@ -61,7 +61,7 @@ commons:
|
||||
keycloak:
|
||||
applicationClientId: redaction
|
||||
clientId: redaction-system
|
||||
clientSecret: NSQmQWTBAPH4HwJXIFQQdazigWP8EHgg
|
||||
clientSecret: G5E1qLU8ZNdDv7HY5BNLPdt5nXdeF7cU
|
||||
realm: redaction
|
||||
serverUrl: http://localhost:8080
|
||||
issuer: ''
|
||||
|
||||
@ -154,11 +154,11 @@ commons:
|
||||
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
path: /api/docs/swagger-ui
|
||||
path: /redaction-gateway-v1/docs/swagger-ui
|
||||
operations-sorter: alpha
|
||||
tags-sorter: alpha
|
||||
api-docs:
|
||||
path: /api/docs
|
||||
path: /redaction-gateway-v1/docs
|
||||
pre-loading-enabled: true
|
||||
packages-to-scan: ['com.iqser.red.persistence.service.v1.external.api']
|
||||
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package com.iqser.red.service.peristence.v1.server.integration.client;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.CustomPermissionMappingResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.ViewedPagesResource;
|
||||
|
||||
@FeignClient(name = "CustomPermissionClient", url = "http://localhost:${server.port}")
|
||||
public interface CustomPermissionClient extends CustomPermissionMappingResource {
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package com.iqser.red.service.peristence.v1.server.integration.tests;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.MANAGE_ACL_PERMISSIONS;
|
||||
import static com.iqser.red.service.persistence.service.v1.api.external.resource.CustomPermissionMappingResource.PERMISSION_REST_PATH;
|
||||
import static com.iqser.red.service.persistence.service.v1.api.external.resource.CustomPermissionMappingResource.TARGET_OBJECT_VARIABLE;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
import com.iqser.red.service.peristence.v1.server.integration.client.CustomPermissionClient;
|
||||
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.acl.RedPermission;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.acl.custom.constants.CustomPermissionConstants;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.permission.CustomPermissionMappingModel;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.permission.CustomPermissionModel;
|
||||
|
||||
public class CustomPermissionTest extends AbstractPersistenceServerServiceTest {
|
||||
|
||||
@Autowired
|
||||
private CustomPermissionClient customPermissionClient;
|
||||
|
||||
|
||||
@Test
|
||||
public void testSaveCustomPermissions() {
|
||||
|
||||
CustomPermissionModel targetViewPermission = new CustomPermissionModel(RedPermission.VIEW_OBJECT.getMask(),
|
||||
RedPermission.VIEW_OBJECT.getName(),
|
||||
RedPermission.VIEW_OBJECT.getSort(),
|
||||
false);
|
||||
List<CustomPermissionModel> mappedViewPermissions = new ArrayList<>();
|
||||
mappedViewPermissions.add(new CustomPermissionModel(RedPermission.APPROVE.getMask(), RedPermission.APPROVE.getName(), RedPermission.APPROVE.getSort(), true));
|
||||
mappedViewPermissions.add(new CustomPermissionModel(RedPermission.REVIEW.getMask(), RedPermission.REVIEW.getName(), RedPermission.REVIEW.getSort(), true));
|
||||
|
||||
CustomPermissionModel targetAccessPermission = new CustomPermissionModel(RedPermission.ACCESS_OBJECT.getMask(),
|
||||
RedPermission.ACCESS_OBJECT.getName(),
|
||||
RedPermission.ACCESS_OBJECT.getSort(),
|
||||
false);
|
||||
List<CustomPermissionModel> mappedAccessPermissions = new ArrayList<>();
|
||||
mappedAccessPermissions.add(new CustomPermissionModel(RedPermission.APPROVE.getMask(), RedPermission.APPROVE.getName(), RedPermission.APPROVE.getSort(), true));
|
||||
mappedAccessPermissions.add(new CustomPermissionModel(RedPermission.REVIEW.getMask(), RedPermission.REVIEW.getName(), RedPermission.REVIEW.getSort(), true));
|
||||
mappedAccessPermissions.add(new CustomPermissionModel(CustomPermissionConstants.EVERYONE_ELSE.getMask(),
|
||||
CustomPermissionConstants.EVERYONE_ELSE.getName(),
|
||||
CustomPermissionConstants.EVERYONE_ELSE.getSort(),
|
||||
true));
|
||||
|
||||
List<CustomPermissionMappingModel> customPermissionMappingModels = new ArrayList<>();
|
||||
|
||||
customPermissionMappingModels.add(new CustomPermissionMappingModel(targetViewPermission, mappedViewPermissions));
|
||||
customPermissionMappingModels.add(new CustomPermissionMappingModel(targetAccessPermission, mappedAccessPermissions));
|
||||
|
||||
customPermissionClient.saveCustomPermissionMappings("Dossier", customPermissionMappingModels);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -53,6 +53,9 @@ public class DossierTemplateModel {
|
||||
@Schema(description = "Status of dossier template.")
|
||||
private DossierTemplateStatus dossierTemplateStatus;
|
||||
|
||||
@Schema(description = "Representing the setting if the metadata of images in pdfs should get kept or removed")
|
||||
private boolean keepImageMetadata;
|
||||
|
||||
public String getId(){
|
||||
return dossierTemplateId;
|
||||
}
|
||||
|
||||
@ -30,5 +30,6 @@ public class DossierTemplate {
|
||||
private boolean deleted;
|
||||
private Set<DownloadFileType> downloadFileTypes = new HashSet<>();
|
||||
private DossierTemplateStatus dossierTemplateStatus;
|
||||
private boolean keepImageMetadata;
|
||||
|
||||
}
|
||||
|
||||
@ -12,5 +12,6 @@ public class CustomPermissionModel {
|
||||
private final Integer mask;
|
||||
private final String name;
|
||||
private final int sort;
|
||||
private final boolean isChangeable;
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user