Merge branch 'develop' into feature/SUPPORT-8593_marker_verify

This commit is contained in:
Eduard Tihomirov 2024-11-26 16:19:56 +03:00
commit b6072d9bf4
172 changed files with 3812 additions and 6689 deletions

2
.gitignore vendored
View file

@ -65,3 +65,5 @@ npm-debug.log
#Sublime project files #Sublime project files
*.sublime-project *.sublime-project
*.sublime-workspace *.sublime-workspace
sync-backend.ps1
sync-frontend.ps1

View file

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ru.micord.ervu.lkrp</groupId> <groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>fl</artifactId> <artifactId>fl</artifactId>
<version>1.8.2</version> <version>1.10.0-SNAPSHOT</version>
</parent> </parent>
<groupId>ru.micord.ervu.lkrp.fl</groupId> <groupId>ru.micord.ervu.lkrp.fl</groupId>
<artifactId>backend</artifactId> <artifactId>backend</artifactId>

View file

@ -1,11 +1,16 @@
import java.util.List;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.FilterType;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/** /**
* Root application context * Root application context
@ -30,10 +35,15 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@EnableAspectJAutoProxy(proxyTargetClass = true) @EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableWebMvc @EnableWebMvc
@EnableScheduling @EnableScheduling
public class AppConfig { public class AppConfig implements WebMvcConfigurer {
@Bean @Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){ public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer(); return new PropertySourcesPlaceholderConfigurer();
} }
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ResourceHttpMessageConverter());
}
} }

View file

@ -4,15 +4,15 @@ import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.kafka.common.utils.Bytes; import org.apache.kafka.common.utils.Bytes;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import ru.micord.ervu.converter.SummonsResponseDataConverter; import ru.micord.ervu.converter.SummonsResponseDataConverter;
import ru.micord.ervu.dto.SubpoenaRequestDto; import ru.micord.ervu.dto.SubpoenaRequestDto;
import ru.micord.ervu.dto.SubpoenaResponseDto; import ru.micord.ervu.dto.SubpoenaResponseDto;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import proto.ervu.rp.summons.SummonsResponseData; import proto.ervu.rp.summons.SummonsResponseData;
import ru.micord.ervu.kafka.service.ReplyingKafkaService; import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService; import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
/** /**
* @author gulnaz * @author gulnaz
@ -20,7 +20,6 @@ import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
@RestController @RestController
public class ErvuDataController { public class ErvuDataController {
private final JwtTokenService jwtTokenService;
private final ReplyingKafkaService<Object, Bytes> replyingKafkaService; private final ReplyingKafkaService<Object, Bytes> replyingKafkaService;
private final SummonsResponseDataConverter converter; private final SummonsResponseDataConverter converter;
@ -30,20 +29,23 @@ public class ErvuDataController {
private String recruitReplyTopic; private String recruitReplyTopic;
public ErvuDataController( public ErvuDataController(
JwtTokenService jwtTokenService, @Qualifier("recruit") ReplyingKafkaService<Object, Bytes> replyingKafkaService,
@Qualifier("subpoena") ReplyingKafkaService<Object, Bytes> replyingKafkaService,
SummonsResponseDataConverter converter) { SummonsResponseDataConverter converter) {
this.jwtTokenService = jwtTokenService;
this.replyingKafkaService = replyingKafkaService; this.replyingKafkaService = replyingKafkaService;
this.converter = converter; this.converter = converter;
} }
@PostMapping( @GetMapping(
value = "/get-data", value = "/recruit",
produces = MediaType.APPLICATION_JSON_VALUE produces = MediaType.APPLICATION_JSON_VALUE
) )
public SubpoenaResponseDto getData() { public SubpoenaResponseDto getData() {
SubpoenaRequestDto subpoenaRequestDto = new SubpoenaRequestDto(jwtTokenService.getErvuId()); String ervuId = SecurityUtil.getErvuId();
if (ervuId == null) {
return new SubpoenaResponseDto.Builder().build();
}
SubpoenaRequestDto subpoenaRequestDto = new SubpoenaRequestDto(ervuId);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(recruitRequestTopic, byte[] reply = replyingKafkaService.sendMessageAndGetReply(recruitRequestTopic,
recruitReplyTopic, subpoenaRequestDto).get(); recruitReplyTopic, subpoenaRequestDto).get();

View file

@ -0,0 +1,66 @@
package ru.micord.ervu.controller;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.kafka.common.utils.Bytes;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import rtl.pgs.ervu.proto.ExtractRegistry;
import rtl.pgs.ervu.proto.ResponseData;
import ru.micord.ervu.dto.ExtractRequestDto;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
/**
* @author gulnaz
*/
@RestController
public class ExtractController {
private final ReplyingKafkaService<Object, Bytes> replyingKafkaService;
@Value("${ervu.kafka.registry.extract.request.topic}")
private String registryExtractRequestTopic;
@Value("${ervu.kafka.registry.extract.reply.topic}")
private String registryExtractReplyTopic;
public ExtractController(ReplyingKafkaService<Object, Bytes> replyingKafkaService) {
this.replyingKafkaService = replyingKafkaService;
}
@GetMapping(value = "/extract/{formatRegistry}")
public ResponseEntity<Resource> getExtract(@PathVariable String formatRegistry) {
String ervuId = SecurityUtil.getErvuId();
if (ervuId == null) {
return ResponseEntity.noContent().build();
}
ExtractRequestDto request = new ExtractRequestDto(ervuId, formatRegistry);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(registryExtractRequestTopic,
registryExtractReplyTopic, request).get();
try {
ResponseData responseData = ResponseData.parseFrom(reply);
ExtractRegistry extractRegistry = responseData.getDataRegistryInformation()
.getExtractRegistry();
String encodedFilename = URLEncoder.encode(extractRegistry.getFileName(), StandardCharsets.UTF_8);
InputStreamResource resource = new InputStreamResource(extractRegistry.getFile().newInput());
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encodedFilename)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}
catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Failed to parse data", e);
}
}
}

View file

@ -1,13 +1,10 @@
package ru.micord.ervu.converter; package ru.micord.ervu.converter;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import ru.micord.ervu.dto.SubpoenaResponseDto; import ru.micord.ervu.dto.SubpoenaResponseDto;
import ru.micord.ervu.util.DateUtil;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import proto.ervu.rp.summons.MeasuresTemporary; import proto.ervu.rp.summons.MeasuresTemporary;
import proto.ervu.rp.summons.ResponseDataAddress; import proto.ervu.rp.summons.ResponseDataAddress;
@ -16,6 +13,7 @@ import proto.ervu.rp.summons.SummonsResponseData;
import static ru.micord.ervu.util.DateUtil.convertToLocalDate; import static ru.micord.ervu.util.DateUtil.convertToLocalDate;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static ru.micord.ervu.util.DateUtil.getDaysTill;
/** /**
* @author gulnaz * @author gulnaz
@ -48,6 +46,8 @@ public class SummonsResponseDataConverter {
if (summonsInfoOpt.isPresent()) { if (summonsInfoOpt.isPresent()) {
SummonsInfo summonsInfo = summonsInfoOpt.get(); SummonsInfo summonsInfo = summonsInfoOpt.get();
String estimatedDate = summonsInfo.getEstimatedDateSummons();
builder.seriesAndNumber(summonsInfo.getSummonsSeries(), summonsInfo.getSummonsNumber()) builder.seriesAndNumber(summonsInfo.getSummonsSeries(), summonsInfo.getSummonsNumber())
.visitDateTime(summonsInfo.getVisitDate(), summonsInfo.getVisitTime()) .visitDateTime(summonsInfo.getVisitDate(), summonsInfo.getVisitTime())
.militaryCommissariatName(summonsInfo.getMilitaryCommissariatName()) .militaryCommissariatName(summonsInfo.getMilitaryCommissariatName())
@ -60,12 +60,11 @@ public class SummonsResponseDataConverter {
.reasonName(summonsInfo.getReasonName()) .reasonName(summonsInfo.getReasonName())
.summonsStatusName(summonsInfo.getSummonsStatusName()) .summonsStatusName(summonsInfo.getSummonsStatusName())
.deliveryTypeSummonsName(summonsInfo.getDeliveryTypeSummonsName()) .deliveryTypeSummonsName(summonsInfo.getDeliveryTypeSummonsName())
.estimatedDateSummons(summonsInfo.getEstimatedDateSummons()) .estimatedDateSummons(estimatedDate)
.recruitmentStatusCode( .recruitmentStatusCode(
Integer.parseInt(responseData.getRecruitmentInfo().getRecruitmentStatusCode())) Integer.parseInt(responseData.getRecruitmentInfo().getRecruitmentStatusCode()))
.recruitmentStartDate(responseData.getRecruitmentInfo().getRecruitmentStart()) .recruitmentStartDate(responseData.getRecruitmentInfo().getRecruitmentStart())
.daysToAppearance((int) ChronoUnit.DAYS.between( .daysToAppearance(getDaysTill(convertToLocalDate(estimatedDate)))
LocalDate.now(), DateUtil.convertToLocalDate(summonsInfo.getEstimatedDateSummons())))
.otherRestrictionCount(-1); .otherRestrictionCount(-1);
summonsInfo.getMeasuresTemporaryPackagesList() summonsInfo.getMeasuresTemporaryPackagesList()

View file

@ -1,7 +0,0 @@
package ru.micord.ervu.dto;
/**
* @author gulnaz
*/
public record FileData(String fileName, String fileType, byte[] file) {
}

View file

@ -3,6 +3,7 @@ package ru.micord.ervu.dto;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.springframework.util.StringUtils.hasText;
import static ru.micord.ervu.util.DateUtil.convertToLocalDate; import static ru.micord.ervu.util.DateUtil.convertToLocalDate;
import static ru.micord.ervu.util.DateUtil.convertToString; import static ru.micord.ervu.util.DateUtil.convertToString;
@ -55,7 +56,15 @@ public record SubpoenaResponseDto(String personName, String birthDate, String do
} }
public Builder personName(String firstName, String middleName, String lastName) { public Builder personName(String firstName, String middleName, String lastName) {
this.personName = lastName + " " + firstName + " " + middleName; StringBuilder stringBuilder = new StringBuilder(lastName)
.append(" ")
.append(firstName);
if (hasText(middleName)) {
stringBuilder.append(" ")
.append(middleName);
}
this.personName = stringBuilder.toString();
return this; return this;
} }
@ -70,7 +79,7 @@ public record SubpoenaResponseDto(String personName, String birthDate, String do
} }
public Builder docNumber(String series, String number) { public Builder docNumber(String series, String number) {
this.docNumber = series + " " + number; this.docNumber = hasText(series) ? series + " " + number : number;
return this; return this;
} }

View file

@ -147,7 +147,7 @@ public class ReplyingKafkaConfig {
return replyingKafkaTemplate; return replyingKafkaTemplate;
} }
@Bean("subpoena") @Bean("recruit")
public ReplyingKafkaTemplate<String, Object, Bytes> subpoenaReplyingKafkaTemplate( public ReplyingKafkaTemplate<String, Object, Bytes> subpoenaReplyingKafkaTemplate(
ProducerFactory<String, Object> subpoenaProducerFactory, ProducerFactory<String, Object> subpoenaProducerFactory,
ConcurrentMessageListenerContainer<String, Bytes> subpoenaReplyContainer) { ConcurrentMessageListenerContainer<String, Bytes> subpoenaReplyContainer) {

View file

@ -16,7 +16,7 @@ import ru.micord.ervu.dto.SubpoenaRequestDto;
* @author gulnaz * @author gulnaz
*/ */
@Service @Service
@Qualifier("subpoena") @Qualifier("recruit")
public class SubpoenaReplyingKafkaService extends BaseReplyingKafkaService<Object, Bytes> { public class SubpoenaReplyingKafkaService extends BaseReplyingKafkaService<Object, Bytes> {
private final ReplyingKafkaTemplate<String, Object, Bytes> replyingKafkaTemplate; private final ReplyingKafkaTemplate<String, Object, Bytes> replyingKafkaTemplate;
@ -26,7 +26,7 @@ public class SubpoenaReplyingKafkaService extends BaseReplyingKafkaService<Objec
@Value("${ervu.kafka.extract.header.class}") @Value("${ervu.kafka.extract.header.class}")
private String extractHeaderClass; private String extractHeaderClass;
public SubpoenaReplyingKafkaService(@Qualifier("subpoena") public SubpoenaReplyingKafkaService(@Qualifier("recruit")
ReplyingKafkaTemplate<String, Object, Bytes> replyingKafkaTemplate) { ReplyingKafkaTemplate<String, Object, Bytes> replyingKafkaTemplate) {
this.replyingKafkaTemplate = replyingKafkaTemplate; this.replyingKafkaTemplate = replyingKafkaTemplate;
} }

View file

@ -59,6 +59,8 @@ public class SecurityConfig {
protected void httpConfigure(HttpSecurity httpSecurity) throws Exception { protected void httpConfigure(HttpSecurity httpSecurity) throws Exception {
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse(); CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
tokenRepository.setCookieName(TokenConstants.CSRF_TOKEN_NAME);
tokenRepository.setHeaderName(TokenConstants.CSRF_HEADER_NAME);
tokenRepository.setCookiePath("/"); tokenRepository.setCookiePath("/");
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler(); XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
delegate.setCsrfRequestAttributeName(null); delegate.setCsrfRequestAttributeName(null);

View file

@ -0,0 +1,10 @@
package ru.micord.ervu.security;
public final class TokenConstants {
public static String CSRF_TOKEN_NAME = "XSRF-TOKEN-LKRP-FL";
public static String CSRF_HEADER_NAME = "X-XSRF-TOKEN-LKRP-FL";
private TokenConstants() {
//must be empty
}
}

View file

@ -39,8 +39,8 @@ public class EsiaController {
} }
@GetMapping(value = "/esia/auth", params = "code") @GetMapping(value = "/esia/auth", params = "code")
public ResponseEntity<?> esiaAuth(@RequestParam("code") String code, HttpServletRequest request, HttpServletResponse response) { public ResponseEntity<?> esiaAuth(@RequestParam("code") String code, @RequestParam("error") String error, HttpServletRequest request, HttpServletResponse response) {
return esiaAuthService.getEsiaTokensByCode(code, request, response); return esiaAuthService.getEsiaTokensByCode(code, error, request, response);
} }
@RequestMapping(value = "/esia/refresh") @RequestMapping(value = "/esia/refresh")

View file

@ -1,6 +1,7 @@
package ru.micord.ervu.security.esia.service; package ru.micord.ervu.security.esia.service;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.lang.invoke.MethodHandles;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -14,21 +15,24 @@ import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import ru.micord.ervu.kafka.model.Document; import ru.micord.ervu.kafka.model.Document;
import ru.micord.ervu.kafka.model.ErrorData;
import ru.micord.ervu.kafka.model.Person; import ru.micord.ervu.kafka.model.Person;
import ru.micord.ervu.kafka.model.Response; import ru.micord.ervu.kafka.model.Response;
import ru.micord.ervu.kafka.service.ReplyingKafkaService; import ru.micord.ervu.kafka.service.ReplyingKafkaService;
@ -49,11 +53,16 @@ import ru.micord.ervu.security.esia.model.PersonModel;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService; import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
import ru.micord.ervu.security.webbpm.jwt.model.Token; import ru.micord.ervu.security.webbpm.jwt.model.Token;
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUsername;
/** /**
* @author Eduard Tihomirov * @author Eduard Tihomirov
*/ */
@Service @Service
public class EsiaAuthService { public class EsiaAuthService {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final Long EXPIRES_IN = 3600L;
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@ -150,8 +159,16 @@ public class EsiaAuthService {
return uriBuilder.toString(); return uriBuilder.toString();
} }
public ResponseEntity<?> getEsiaTokensByCode(String esiaAuthCode, HttpServletRequest request, HttpServletResponse response) { public ResponseEntity<?> getEsiaTokensByCode(String esiaAuthCode, String error,
HttpServletRequest request, HttpServletResponse response) {
try { try {
if (error != null) {
createTokenAndAddCookie(response, null, null, EXPIRES_IN);
return new ResponseEntity<>(
"Произошла неизвестная ошибка. Обратитесь к системному администратору",
HttpStatus.FORBIDDEN
);
}
String clientId = esiaConfig.getClientId(); String clientId = esiaConfig.getClientId();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
ZonedDateTime dt = ZonedDateTime.now(); ZonedDateTime dt = ZonedDateTime.now();
@ -215,19 +232,7 @@ public class EsiaAuthService {
TokensStore.addAccessToken(prnOid, accessToken, expiresIn); TokensStore.addAccessToken(prnOid, accessToken, expiresIn);
TokensStore.addRefreshToken(prnOid, refreshToken, expiresIn); TokensStore.addRefreshToken(prnOid, refreshToken, expiresIn);
Response ervuIdResponse = getErvuIdResponse(accessToken); Response ervuIdResponse = getErvuIdResponse(accessToken);
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId()); createTokenAndAddCookie(response, esiaAccessToken.getSbj_id(), ervuIdResponse.getErvuId(), expiresIn);
int expiry = tokenResponse.getExpires_in().intValue();
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry);
response.addCookie(accessCookie);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
SecurityContext context = SecurityContextHolder.createEmptyContext();
JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
esiaAccessToken.getSbj_id(), token.getValue());
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiry);
response.addCookie(authMarkerCookie);
if (ervuIdResponse.getErrorData() != null) { if (ervuIdResponse.getErrorData() != null) {
return new ResponseEntity<>( return new ResponseEntity<>(
"Доступ запрещен. " + ervuIdResponse.getErrorData().getName(), "Доступ запрещен. " + ervuIdResponse.getErrorData().getName(),
@ -237,7 +242,14 @@ public class EsiaAuthService {
return ResponseEntity.ok("Authentication successful"); return ResponseEntity.ok("Authentication successful");
} }
catch (Exception e) { catch (Exception e) {
throw new RuntimeException(e); createTokenAndAddCookie(response, null, null, EXPIRES_IN);
String messageId = getMessageId(e);
String messageWithId = String.format("[%s] %s", messageId, e.getMessage());
LOGGER.error(messageWithId, e);
return new ResponseEntity<>(
"Произошла ошибка " + messageId + ". Обратитесь к системному администратору",
HttpStatus.FORBIDDEN
);
} }
} }
@ -299,19 +311,7 @@ public class EsiaAuthService {
TokensStore.addAccessToken(prnOid, accessToken, expiresIn); TokensStore.addAccessToken(prnOid, accessToken, expiresIn);
TokensStore.addRefreshToken(prnOid, newRefreshToken, expiresIn); TokensStore.addRefreshToken(prnOid, newRefreshToken, expiresIn);
Response ervuIdResponse = getErvuIdResponse(accessToken); Response ervuIdResponse = getErvuIdResponse(accessToken);
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId()); createTokenAndAddCookie(response, esiaAccessToken.getSbj_id(), ervuIdResponse.getErvuId(), expiresIn);
int expiry = tokenResponse.getExpires_in().intValue();
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry);
response.addCookie(accessCookie);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
SecurityContext context = SecurityContextHolder.createEmptyContext();
JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
esiaAccessToken.getSbj_id(), token.getValue());
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiry);
response.addCookie(authMarkerCookie);
} }
catch (Exception e) { catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -398,9 +398,32 @@ public class EsiaAuthService {
return person; return person;
} }
private String getMessageId(Exception exception) {
return Integer.toUnsignedString(Objects
.hashCode(getCurrentUsername()), 36)
+ "-"
+ Integer.toUnsignedString(exception.hashCode(), 36);
}
private void createTokenAndAddCookie(HttpServletResponse response, String userId, String ervuId,
Long expiresIn) {
Token token = jwtTokenService.createAccessToken(userId, expiresIn, ervuId);
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiresIn.intValue());
response.addCookie(accessCookie);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
SecurityContext context = SecurityContextHolder.createEmptyContext();
JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
userId, token.getValue());
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiresIn.intValue());
response.addCookie(authMarkerCookie);
}
private String verifyToken(String accessToken) { private String verifyToken(String accessToken) {
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken); EsiaAccessToken esiaAccessToken = personalDataService.readToken(accessToken);
EsiaHeader esiaHeader = ulDataService.readHeader(accessToken); EsiaHeader esiaHeader = personalDataService.readHeader(accessToken);
if (!esiaHeader.getSbt().equals("access")) { if (!esiaHeader.getSbt().equals("access")) {
return "Token invalid. Token sbt: " + esiaHeader.getSbt() + " invalid"; return "Token invalid. Token sbt: " + esiaHeader.getSbt() + " invalid";
} }

View file

@ -7,54 +7,54 @@ import java.util.concurrent.ConcurrentHashMap;
* @author Eduard Tihomirov * @author Eduard Tihomirov
*/ */
public class TokensStore { public class TokensStore {
private static final Map<String, ExpiringToken> accessTokensMap = new ConcurrentHashMap<>(); private static final Map<String, ExpiringToken> ACCESS_TOKENS_MAP = new ConcurrentHashMap<>();
private static final Map<String, ExpiringToken> refreshTokensMap = new ConcurrentHashMap<>(); private static final Map<String, ExpiringToken> REFRESH_TOKENS_MAP = new ConcurrentHashMap<>();
public static void addAccessToken(String prnOid, String token, long expiresIn) { public static void addAccessToken(String prnOid, String token, long expiresIn) {
if (token != null) { if (token != null) {
long expiryTime = System.currentTimeMillis() + 1000L * expiresIn; long expiryTime = System.currentTimeMillis() + 1000L * expiresIn;
accessTokensMap.put(prnOid, new ExpiringToken(token, expiryTime)); ACCESS_TOKENS_MAP.put(prnOid, new ExpiringToken(token, expiryTime));
} }
} }
public static String getAccessToken(String prnOid) { public static String getAccessToken(String prnOid) {
return accessTokensMap.get(prnOid).getAccessToken(); return ACCESS_TOKENS_MAP.get(prnOid).getAccessToken();
} }
public static void removeExpiredAccessToken() { public static void removeExpiredAccessToken() {
for (String key : accessTokensMap.keySet()) { for (String key : ACCESS_TOKENS_MAP.keySet()) {
ExpiringToken token = accessTokensMap.get(key); ExpiringToken token = ACCESS_TOKENS_MAP.get(key);
if (token != null && token.isExpired()) { if (token != null && token.isExpired()) {
accessTokensMap.remove(key); ACCESS_TOKENS_MAP.remove(key);
} }
} }
} }
public static void removeExpiredRefreshToken() { public static void removeExpiredRefreshToken() {
for (String key : refreshTokensMap.keySet()) { for (String key : REFRESH_TOKENS_MAP.keySet()) {
ExpiringToken token = refreshTokensMap.get(key); ExpiringToken token = REFRESH_TOKENS_MAP.get(key);
if (token != null && token.isExpired()) { if (token != null && token.isExpired()) {
refreshTokensMap.remove(key); REFRESH_TOKENS_MAP.remove(key);
} }
} }
} }
public static void removeAccessToken(String prnOid) { public static void removeAccessToken(String prnOid) {
accessTokensMap.remove(prnOid); ACCESS_TOKENS_MAP.remove(prnOid);
} }
public static void addRefreshToken(String prnOid, String token, long expiresIn) { public static void addRefreshToken(String prnOid, String token, long expiresIn) {
if (token != null) { if (token != null) {
long expiryTime = System.currentTimeMillis() + 1000L * expiresIn; long expiryTime = System.currentTimeMillis() + 1000L * expiresIn;
refreshTokensMap.put(prnOid, new ExpiringToken(token, expiryTime)); REFRESH_TOKENS_MAP.put(prnOid, new ExpiringToken(token, expiryTime));
} }
} }
public static String getRefreshToken(String prnOid) { public static String getRefreshToken(String prnOid) {
return refreshTokensMap.get(prnOid).getAccessToken(); return REFRESH_TOKENS_MAP.get(prnOid).getAccessToken();
} }
public static void removeRefreshToken(String prnOid) { public static void removeRefreshToken(String prnOid) {
refreshTokensMap.remove(prnOid); REFRESH_TOKENS_MAP.remove(prnOid);
} }
} }

View file

@ -32,7 +32,7 @@ public class JwtTokenService {
@Value("${webbpm.security.token.issuer:#{null}}") @Value("${webbpm.security.token.issuer:#{null}}")
private final String tokenIssuerName = private final String tokenIssuerName =
ResourceMetadataUtils.PROJECT_GROUP_ID + "." + ResourceMetadataUtils.PROJECT_ARTIFACT_ID; ResourceMetadataUtils.PROJECT_GROUP_ID + "." + ResourceMetadataUtils.PROJECT_ARTIFACT_ID;
private final SecretKey SIGNING_KEY; private final SecretKey signingKey;
@Autowired @Autowired
private HttpServletRequest request; private HttpServletRequest request;
@ -41,7 +41,7 @@ public class JwtTokenService {
public JwtTokenService(@Value("${webbpm.security.token.secret.key:ZjE5ZjMxNmYtODViZC00ZTQ5LWIxZmYtOGEzYzE3Yjc1MDVk}") public JwtTokenService(@Value("${webbpm.security.token.secret.key:ZjE5ZjMxNmYtODViZC00ZTQ5LWIxZmYtOGEzYzE3Yjc1MDVk}")
String secretKey) { String secretKey) {
byte[] encodedKey = Base64.getDecoder().decode(secretKey); byte[] encodedKey = Base64.getDecoder().decode(secretKey);
this.SIGNING_KEY = Keys.hmacShaKeyFor(encodedKey); this.signingKey = Keys.hmacShaKeyFor(encodedKey);
} }
public Token createAccessToken(String userAccountId, Long expiresIn, String ervuId) { public Token createAccessToken(String userAccountId, Long expiresIn, String ervuId) {
@ -52,7 +52,7 @@ public class JwtTokenService {
.setIssuer(tokenIssuerName) .setIssuer(tokenIssuerName)
.setIssuedAt(new Date(System.currentTimeMillis())) .setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(expirationDate) .setExpiration(expirationDate)
.signWith(SIGNING_KEY) .signWith(signingKey)
.compact(); .compact();
return new Token(userAccountId + ":" + ervuId, tokenIssuerName, expirationDate, value); return new Token(userAccountId + ":" + ervuId, tokenIssuerName, expirationDate, value);
} }
@ -72,7 +72,7 @@ public class JwtTokenService {
public Token getToken(String token) { public Token getToken(String token) {
Claims claims = Jwts.parser() Claims claims = Jwts.parser()
.setSigningKey(SIGNING_KEY) .setSigningKey(signingKey)
.parseClaimsJws(token) .parseClaimsJws(token)
.getBody(); .getBody();

View file

@ -2,12 +2,16 @@ package ru.micord.ervu.security.webbpm.jwt.util;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Optional;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST; import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST;
@ -42,4 +46,22 @@ public final class SecurityUtil {
Cookie cookie = WebUtils.getCookie(httpRequest, AUTH_TOKEN); Cookie cookie = WebUtils.getCookie(httpRequest, AUTH_TOKEN);
return cookie != null ? cookie.getValue() : null; return cookie != null ? cookie.getValue() : null;
} }
public static String getErvuId() {
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
.map(a -> ((JwtAuthentication) a).getUserAccountId())
.map(userAccountId -> {
String ervuId = userAccountId.split(":")[1];
return "null".equals(ervuId) ? null : ervuId;
})
.orElse(null);
}
public static String getCurrentUsername() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
return auth.getName();
}
return null;
}
} }

View file

@ -1,61 +0,0 @@
package ru.micord.ervu.service.rpc;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.kafka.common.utils.Bytes;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import ru.micord.ervu.dto.FileData;
import ru.micord.ervu.dto.ExtractRequestDto;
import rtl.pgs.ervu.proto.ExtractRegistry;
import rtl.pgs.ervu.proto.ResponseData;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
import ru.cg.webbpm.modules.standard_annotations.validation.NotNull;
import ru.cg.webbpm.modules.webkit.annotations.RpcCall;
import ru.cg.webbpm.modules.webkit.annotations.RpcService;
import ru.cg.webbpm.modules.webkit.beans.Behavior;
/**
* @author gulnaz
*/
@RpcService
public class ExtractRpcService extends Behavior {
private final JwtTokenService jwtTokenService;
private final ReplyingKafkaService<Object, Bytes> replyingKafkaService;
@Value("${ervu.kafka.registry.extract.request.topic}")
private String registryExtractRequestTopic;
@Value("${ervu.kafka.registry.extract.reply.topic}")
private String registryExtractReplyTopic;
@NotNull()
public String formatExtractRegistry;
public ExtractRpcService(
JwtTokenService jwtTokenService,
@Qualifier("subpoena") ReplyingKafkaService<Object, Bytes> replyingKafkaService) {
this.jwtTokenService = jwtTokenService;
this.replyingKafkaService = replyingKafkaService;
}
@RpcCall
public FileData getExtract() {
ExtractRequestDto request = new ExtractRequestDto(jwtTokenService.getErvuId(),
formatExtractRegistry);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(registryExtractRequestTopic,
registryExtractReplyTopic, request).get();
try {
ResponseData responseData = ResponseData.parseFrom(reply);
ExtractRegistry extractRegistry = responseData.getDataRegistryInformation()
.getExtractRegistry();
return new FileData(extractRegistry.getFileName(), extractRegistry.getFileType(),
extractRegistry.getFile().toByteArray());
}
catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Failed to parse data", e);
}
}
}

View file

@ -2,6 +2,7 @@ package ru.micord.ervu.util;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -27,4 +28,9 @@ public final class DateUtil {
public static String convertToString(LocalDate date, DateTimeFormatter formatter) { public static String convertToString(LocalDate date, DateTimeFormatter formatter) {
return date == null ? "" : date.format(formatter); return date == null ? "" : date.format(formatter);
} }
public static int getDaysTill(LocalDate end) {
return (int) ChronoUnit.DAYS.between(
LocalDate.now(), end == null ? LocalDate.now() : end);
}
} }

View file

@ -1,11 +1,11 @@
TZ=Europe/Moscow TZ=Europe/Moscow
# App datasource # App datasource
DB_APP_USERNAME=ervu-lkrp-fl DB_APP_USERNAME=ervu_lkrp_fl
DB_APP_PASSWORD=ervu-lkrp-fl DB_APP_PASSWORD=ervu_lkrp_fl
DB_APP_HOST=10.10.31.119 DB_APP_HOST=10.10.31.119
DB_APP_PORT=5432 DB_APP_PORT=5432
DB_APP_NAME=ervu-lkrp-fl DB_APP_NAME=ervu_lkrp_fl
ESIA_SCOPES=snils, fullname, birthdate, id_doc ESIA_SCOPES=snils, fullname, birthdate, id_doc
ESIA_BASE_URI=https://esia-portal1.test.gosuslugi.ru/ ESIA_BASE_URI=https://esia-portal1.test.gosuslugi.ru/
@ -30,8 +30,8 @@ ERVU_KAFKA_RECRUIT_REPLY_TOPIC=ervu.recruit.info.response
ERVU_KAFKA_RECRUIT_HEADER_CLASS=Request@urn://rostelekom.ru/RP-SummonsTR/1.0.5 ERVU_KAFKA_RECRUIT_HEADER_CLASS=Request@urn://rostelekom.ru/RP-SummonsTR/1.0.5
ERVU_KAFKA_REGISTRY_EXTRACT_REQUEST_TOPIC=ervu.extract.info.request ERVU_KAFKA_REGISTRY_EXTRACT_REQUEST_TOPIC=ervu.extract.info.request
ERVU_KAFKA_REGISTRY_EXTRACT_REPLY_TOPIC=ervu.extract.info.response ERVU_KAFKA_REGISTRY_EXTRACT_REPLY_TOPIC=ervu.extract.info.response
ERVU_KAFKA_EXTRACT_HEADER_CLASS=Request@urn://rostelekom.ru/ERVU-extractFromRegistryTR/1.0.3 ERVU_KAFKA_EXTRACT_HEADER_CLASS=request@urn://rostelekom.ru/ERVU-extractFromRegistryTR/1.0.3
ERVU_KAFKA_DOC_LOGIN_MODULE=org.apache.kafka.common.security.scram.ScramLoginModule ERVU_KAFKA_DOC_LOGIN_MODULE=org.apache.kafka.common.security.scram.ScramLoginModule
ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * * ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * *
COOKIE_PATH=/fl COOKIE_PATH=/fl

View file

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ru.micord.ervu.lkrp</groupId> <groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>fl</artifactId> <artifactId>fl</artifactId>
<version>1.8.2</version> <version>1.10.0-SNAPSHOT</version>
</parent> </parent>
<groupId>ru.micord.ervu.lkrp.fl</groupId> <groupId>ru.micord.ervu.lkrp.fl</groupId>

105
ervu_lkrp_fl-openapi.yaml Normal file
View file

@ -0,0 +1,105 @@
openapi: 3.0.3
info:
title: ervu-lkrp-fl API
description: API сервиса ervu-lkrp-fl
version: 1.9.1
servers:
- url: https://fl-lkrp-ervu-test.pgs.rtlabs.ru
paths:
/esia/auth:
get:
summary: Получение маркера доступа
operationId: esiaAuth
description: Получение маркера доступа в обмен на код от ЕСИА и создание внутреннего токена
parameters:
- name: code
in: query
required: true
description: Код, присланный ЕСИА после успешной авторизации
schema:
type: string
responses:
"200":
description: Authentication successful
/esia/person:
get:
summary: Получение информации о пользователе
operationId: getPersonModel
description: Получение информации о пользователе ЕСИА
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
/esia/refresh:
post:
summary: Обновление токена
operationId: refreshToken
description: Обновление токена
responses:
"200":
description: OK
/esia/url:
get:
summary: Получение URL ЕСИА
operationId: getEsiaUrl
description: Получение URL ЕСИА
responses:
"200":
description: OK
content:
application/json:
schema:
type: string
/esia/userfullname:
get:
summary: Получение полного имени пользователя
operationId: getUserFullname
description: Получение полного имени пользователя ЕСИА
responses:
"200":
description: OK
content:
application/json:
schema:
type: string
/extract/{formatRegistry}:
get:
summary: Получение выписки
operationId: getExtract
description: Получение выписки из реестров повесток и воинского учета
parameters:
- name: formatRegistry
in: path
required: true
description: Формат выписки (1 - по воинскому учету; 2 - по повесткам)
schema:
type: string
responses:
"200":
description: OK
headers:
Content-Disposition:
schema:
type: string
example: attachment; filename*=UTF-8''encodedfilename.zip
content:
application/octet-stream:
schema:
type: object
"204":
description: No Content
/recruit:
get:
summary: Получение информации о повестке, временных мерах и воинскому учету
operationId: getData
description: Получение информации о повестке, временных мерах и воинскому учету
responses:
"200":
description: OK
content:
application/json:
schema:
type: object

View file

@ -1,10 +0,0 @@
{
"port": 8000,
"open": false,
"files": [
"./**/*.{html,htm,css,js}"
],
"server": {
"baseDir": "./"
}
}

View file

@ -13,6 +13,16 @@
</div> </div>
<div class="container"> <div class="container">
<div id="browser-check-info">
<div class="browser-check-content">
<div class="browser-check-text">
<div class="plain-text">Для обеспечения защищённого соединения с сайтом реестра повесток необходимо установить браузер Яндекс или Chromium GOST.</div>
</div>
</div>
</div>
<script>
document.getElementById("browser-check-info").hidden = navigator.userAgent.indexOf("Chromium GOST") > -1 || navigator.userAgent.indexOf("YaBrowser") > -1;
</script>
<div class="container-inside"> <div class="container-inside">
<div class="list-group lk-what"> <div class="list-group lk-what">
<div> <div>

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,6 @@
"name": "ervu_lkrp_fl", "name": "ervu_lkrp_fl",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"lite": "node ./node_modules/lite-server/bin/lite-server",
"cleanup": "npm run cleanup-ngc && node ./node_modules/rimraf/bin ./build ./dist", "cleanup": "npm run cleanup-ngc && node ./node_modules/rimraf/bin ./build ./dist",
"cleanup-ngc": "node ./node_modules/rimraf/bin ./src/ts/**/*.js ./src/ts/**/*.json ./src/ts/page.routing.ts", "cleanup-ngc": "node ./node_modules/rimraf/bin ./src/ts/**/*.js ./src/ts/**/*.json ./src/ts/page.routing.ts",
"cleanup-and-ngc": "npm run cleanup && npm run ngc", "cleanup-and-ngc": "npm run cleanup && npm run ngc",
@ -26,8 +25,8 @@
"@angular/platform-browser": "7.2.15", "@angular/platform-browser": "7.2.15",
"@angular/platform-browser-dynamic": "7.2.15", "@angular/platform-browser-dynamic": "7.2.15",
"@angular/router": "7.2.15", "@angular/router": "7.2.15",
"@ng-bootstrap/ng-bootstrap": "4.1.1", "@ng-bootstrap/ng-bootstrap": "4.2.2-micord.1",
"@webbpm/base-package": "3.182.0", "@webbpm/base-package": "3.185.0",
"ag-grid-angular": "29.0.0-micord.4", "ag-grid-angular": "29.0.0-micord.4",
"ag-grid-community": "29.0.0-micord.4", "ag-grid-community": "29.0.0-micord.4",
"angular-calendar": "0.28.28", "angular-calendar": "0.28.28",
@ -40,16 +39,16 @@
"core-js": "2.4.1", "core-js": "2.4.1",
"date-fns": "2.29.3", "date-fns": "2.29.3",
"downloadjs": "1.4.8", "downloadjs": "1.4.8",
"eonasdan-bootstrap-datetimepicker": "4.17.47-micord.4", "eonasdan-bootstrap-datetimepicker": "4.17.47-micord.5",
"esmarttokenjs": "2.2.1-cg", "esmarttokenjs": "2.2.1-cg",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"google-libphonenumber": "3.0.9", "google-libphonenumber": "3.0.9",
"inputmask": "5.0.5-cg.2", "inputmask": "5.0.5-cg.2",
"jquery": "3.3.1", "jquery": "3.7.1",
"js-year-calendar": "1.0.0-cg.2", "js-year-calendar": "1.0.0-cg.2",
"jsgantt-improved": "2.0.10-cg", "jsgantt-improved": "2.0.10-cg",
"moment": "2.17.1", "moment": "2.30.1",
"moment-timezone": "0.5.11", "moment-timezone": "0.5.46",
"ngx-cookie": "3.0.1", "ngx-cookie": "3.0.1",
"ngx-international-phone-number": "1.0.6", "ngx-international-phone-number": "1.0.6",
"ngx-toastr": "10.2.0-cg", "ngx-toastr": "10.2.0-cg",
@ -61,7 +60,7 @@
"systemjs": "0.21.4", "systemjs": "0.21.4",
"systemjs-plugin-babel": "0.0.25", "systemjs-plugin-babel": "0.0.25",
"tslib": "1.9.3", "tslib": "1.9.3",
"zone.js": "0.8.29" "zone.js": "0.11.8"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-optimizer": "0.13.9", "@angular-devkit/build-optimizer": "0.13.9",
@ -69,33 +68,33 @@
"@angular/cli": "7.3.9", "@angular/cli": "7.3.9",
"@angular/compiler-cli": "7.2.15", "@angular/compiler-cli": "7.2.15",
"@angular/platform-server": "7.2.15", "@angular/platform-server": "7.2.15",
"@babel/core": "7.9.6", "@babel/core": "7.18.10",
"@babel/preset-env": "7.9.6", "@babel/preset-env": "7.18.10",
"@types/bootstrap": "3.3.39", "@types/bootstrap": "3.3.39",
"@types/jquery": "2.0.49", "@types/eslint": "7.2.5",
"@types/jquery": "3.5.5",
"@types/node": "7.0.5", "@types/node": "7.0.5",
"@types/selectize": "0.12.33", "@types/selectize": "0.12.33",
"ajv": "8.8.2",
"angular-router-loader": "0.8.5", "angular-router-loader": "0.8.5",
"angular2-template-loader": "0.6.2", "angular2-template-loader": "0.6.2",
"babel-loader": "8.1.0", "babel-loader": "9.1.2",
"codelyzer": "5.2.1", "codelyzer": "5.2.1",
"copy-webpack-plugin": "5.0.3", "copy-webpack-plugin": "5.0.3",
"cross-env": "5.2.1", "cross-env": "5.2.1",
"css-loader": "2.1.0", "css-loader": "6.11.0",
"del": "2.2.2", "del": "2.2.2",
"file-loader": "3.0.1", "file-loader": "6.2.0",
"html-webpack-plugin": "4.5.2", "html-webpack-plugin": "5.6.0",
"lite-server": "2.3.0", "mini-css-extract-plugin": "2.9.1",
"mini-css-extract-plugin": "0.6.0", "mkdirp": "3.0.1",
"mkdirp": "0.5.1", "raw-loader": "4.0.2",
"raw-loader": "1.0.0", "style-loader": "3.3.4",
"style-loader": "0.23.1", "terser-webpack-plugin": "5.3.10",
"terser-webpack-plugin": "1.2.4",
"tslint": "5.13.1", "tslint": "5.13.1",
"typescript": "3.2.4", "typescript": "3.2.4",
"typescript-parser": "2.6.1-cg-fork", "typescript-parser": "2.6.1-cg.2",
"webpack": "4.32.2", "webpack": "5.90.1",
"webpack-bundle-analyzer": "3.3.2", "webpack-cli": "5.0.2"
"webpack-cli": "3.3.2"
} }
} }

View file

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ru.micord.ervu.lkrp</groupId> <groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>fl</artifactId> <artifactId>fl</artifactId>
<version>1.8.2</version> <version>1.10.0-SNAPSHOT</version>
</parent> </parent>
<groupId>ru.micord.ervu.lkrp.fl</groupId> <groupId>ru.micord.ervu.lkrp.fl</groupId>

View file

@ -4,7 +4,7 @@
"filter_cleanup_interval_hours": 720, "filter_cleanup_interval_hours": 720,
"filter_cleanup_check_period_minutes": 30, "filter_cleanup_check_period_minutes": 30,
"auth_method": "form", "auth_method": "form",
"enable.version.in.url": "false", "enable.version.in.url": "%enable.version.in.url%",
"backend.context": "fl", "backend.context": "fl",
"guard.confirm_exit": false, "guard.confirm_exit": false,
"message_service_error_timeout": "", "message_service_error_timeout": "",

View file

@ -1 +1 @@
1.8.2-SNAPSHOT %project.version%

View file

@ -530,6 +530,10 @@
margin-top: 16px; margin-top: 16px;
} }
.webbpm.ervu_lkrp_fl #restriction in-memory-static-grid .ag-header {
display: none;
}
@media only screen and (max-width: 480px) { @media only screen and (max-width: 480px) {
.webbpm.ervu_lkrp_fl .warning-text .horizontal-container { .webbpm.ervu_lkrp_fl .warning-text .horizontal-container {
flex-wrap: wrap !important; flex-wrap: wrap !important;

View file

@ -187,12 +187,15 @@ body.webbpm.ervu_lkrp_fl {
bottom: 0; bottom: 0;
} }
.webbpm.ervu_lkrp_fl .container-inside { .webbpm.ervu_lkrp_fl .container-inside {
display: flex;
flex-direction: column;
font-family: 'Inter'; font-family: 'Inter';
height: 100%; height: 100%;
padding: 0; padding: 0;
overflow: auto; overflow: auto;
} }
.webbpm.ervu_lkrp_fl .container-inside > div { .webbpm.ervu_lkrp_fl .container-inside > div {
flex: 1;
padding: var(--indent-huge) var(--w-screen); padding: var(--indent-huge) var(--w-screen);
} }

View file

@ -624,6 +624,29 @@ a.btn:is(:hover, :focus, :active) {
color: var(--color-link); color: var(--color-link);
} }
.browser-check-content {
font-family: 'Golos';
font-size: var(--size-text-secondary);
padding: var(--indent-mini) var(--w-screen) var(--indent-mini) calc(var(--w-screen) + 38px);
background-color: var(--bg-warn);
}
.browser-check-text {
position: relative;
padding-left: 40px;
}
.browser-check-text::before {
position: absolute;
content: url(../img/svg/info.svg);
left: 0;
top: calc((100% - 24px) / 2);
}
.text-header {
color: var(--color-link);
font-family: 'GolosB';
font-size: var(--size-text-primary);
margin-bottom: 4px;
}
/*@media ((max-width: 780px) or ((orientation: landscape) and (max-device-width : 1024px))) {*/ /*@media ((max-width: 780px) or ((orientation: landscape) and (max-device-width : 1024px))) {*/
@media (max-width: 1024px) { @media (max-width: 1024px) {
body { body {

View file

@ -3,9 +3,10 @@ import {
AnalyticalScope, AnalyticalScope,
Behavior, Behavior,
Event, Event,
NotNull,
Visible Visible
} from "@webbpm/base-package"; } from "@webbpm/base-package";
import {ExtractRpcService} from "../../../generated/ru/micord/ervu/service/rpc/ExtractRpcService"; import {HttpClient} from "@angular/common/http";
@AnalyticalScope(AbstractButton) @AnalyticalScope(AbstractButton)
export class ExtractLoadService extends Behavior { export class ExtractLoadService extends Behavior {
@ -14,34 +15,34 @@ export class ExtractLoadService extends Behavior {
public successEvent: Event<boolean> = new Event<boolean>(); public successEvent: Event<boolean> = new Event<boolean>();
@Visible("false") @Visible("false")
public errorEvent: Event<boolean> = new Event<boolean>(); public errorEvent: Event<boolean> = new Event<boolean>();
@NotNull()
public formatRegistry: string;
private button: AbstractButton; private button: AbstractButton;
private rpc: ExtractRpcService; private httpClient: HttpClient;
private onClickFunction: Function; private onClickFunction: Function;
initialize() { initialize() {
super.initialize(); super.initialize();
this.button = this.getScript(AbstractButton); this.button = this.getScript(AbstractButton);
this.rpc = this.getScript(ExtractRpcService); this.httpClient = this.injector.get(HttpClient);
this.onClickFunction = () => { this.onClickFunction = () => {
this.rpc.getExtract() this.httpClient.get('extract/' + this.formatRegistry, {
.then(fileData => { responseType: 'blob',
const newBlob = new Blob([fileData['file']], observe: 'response'
{ type: fileData['fileType'] }); }).toPromise()
.then((response) => {
if (window.navigator && window.navigator.msSaveOrOpenBlob) { const data = window.URL.createObjectURL(response.body);
window.navigator.msSaveBlob(newBlob); const link = document.createElement("a");
} link.href = data;
else { const contentDisposition = response.headers.get('Content-Disposition');
const data = window.URL.createObjectURL(newBlob); const fileNameMatch = contentDisposition.match(/filename\*=?UTF-8''(.+)/i);
const link = document.createElement("a"); link.download = decodeURIComponent(fileNameMatch[1].replace(/\+/g, '%20'));
link.href = data; document.body.appendChild(link);
link.download = fileData['fileName']; link.click();
link.click(); window.URL.revokeObjectURL(data);
URL.revokeObjectURL(data); link.remove();
link.remove(); this.successEvent.trigger();
this.successEvent.trigger();
}
}) })
.catch(() => { .catch(() => {
this.errorEvent.trigger(); this.errorEvent.trigger();

View file

@ -12,7 +12,8 @@ import {
PinnedType PinnedType
} from "@webbpm/base-package"; } from "@webbpm/base-package";
import {Moment} from "moment"; import {Moment} from "moment";
import moment from "moment-timezone"; import * as moment_ from "moment-timezone";
const moment = moment_;
import {StaticGridColumn} from "../../../generated/ru/micord/ervu/property/grid/StaticGridColumn"; import {StaticGridColumn} from "../../../generated/ru/micord/ervu/property/grid/StaticGridColumn";
export class StaticColumnInitializer { export class StaticColumnInitializer {

View file

@ -13,6 +13,6 @@ export class ValueWithPrefixRenderer implements GridCellValueRenderer {
render(params: ICellRendererParams): HTMLElement | string { render(params: ICellRendererParams): HTMLElement | string {
let value = params.valueFormatted ? params.valueFormatted : params.value; let value = params.valueFormatted ? params.valueFormatted : params.value;
return `${this.label} ${value}`; return value ? `${this.label} ${value}` : value;
} }
} }

View file

@ -11,8 +11,7 @@ export class ErvuDataService {
} }
public getData(): any { public getData(): any {
this.httpClient this.httpClient.get("recruit",
.post("get-data", null,
{ {
headers: { headers: {
"Content-type": "application/json" "Content-type": "application/json"

View file

@ -0,0 +1,12 @@
export class EsiaErrorDetail {
private static errors: { [code: string]: string } = {
'ESIA-007071': 'Запрос персональных данных по физическим лицам может быть выполнен только с указанием согласий',
'ESIA-007055': 'Вход в систему осуществляется с неподтвержденной учетной записью',
'ESIA-007036': 'Учетная запись заблокирована',
'ESIA-007008': 'Сервис авторизации в настоящее время не может выполнить запрос из-за большой нагрузки или технических работ на сервере',
};
public static getDescription(code: string): string {
return this.errors[code] || 'Доступ запрещен. Обратитесь к системному администратору. Ошибка ' + code;
}
}

View file

@ -1,4 +1,4 @@
export class TokenConstants { export class TokenConstants {
public static readonly CSRF_TOKEN_NAME = "XSRF-TOKEN"; public static readonly CSRF_TOKEN_NAME = "XSRF-TOKEN-LKRP-FL";
public static readonly CSRF_HEADER_NAME = "X-XSRF-TOKEN"; public static readonly CSRF_HEADER_NAME = "X-XSRF-TOKEN-LKRP-FL";
} }

View file

@ -1,8 +1,6 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import {Observable} from "rxjs";
import {CookieService} from "ngx-cookie"; import {CookieService} from "ngx-cookie";
import {tap} from "rxjs/operators";
import {AppConfigService} from "@webbpm/base-package"; import {AppConfigService} from "@webbpm/base-package";
@Injectable({providedIn: 'root'}) @Injectable({providedIn: 'root'})
@ -14,7 +12,7 @@ export class AuthenticationService {
} }
checkAuthentication(): Promise<any>{ checkAuthentication(): Promise<any>{
return this.appConfigService.load().then(value => this.http.get<any>("version").toPromise()) return this.appConfigService.load().then(() => this.http.get<any>("version").toPromise())
} }
logout(): Promise<string> { logout(): Promise<string> {

View file

@ -4,6 +4,7 @@ import {Observable} from "rxjs";
import {HttpClient, HttpParams} from "@angular/common/http"; import {HttpClient, HttpParams} from "@angular/common/http";
import {MessagesService} from "@webbpm/base-package"; import {MessagesService} from "@webbpm/base-package";
import {AuthenticationService} from "../authentication.service"; import {AuthenticationService} from "../authentication.service";
import {EsiaErrorDetail} from "../EsiaErrorDetail";
@Injectable({providedIn:'root'}) @Injectable({providedIn:'root'})
export abstract class AuthGuard implements CanActivate { export abstract class AuthGuard implements CanActivate {
@ -29,13 +30,8 @@ export abstract class AuthGuard implements CanActivate {
if (isAccess) { if (isAccess) {
return true; return true;
} }
else if (error) { if (code || error) {
let errorMessage = error + ', error description =' + errorDescription; const params = new HttpParams().set('code', code).set('error', error);
this.messageService.error(errorMessage)
throw new Error(errorMessage);
}
else if (code) {
const params = new HttpParams().set('code', code);
this.httpClient.get("esia/auth", this.httpClient.get("esia/auth",
{ {
params: params, responseType: 'text', observe: 'response', headers: { params: params, responseType: 'text', observe: 'response', headers: {
@ -51,8 +47,20 @@ export abstract class AuthGuard implements CanActivate {
let errorMessage = reason.error.messages != null let errorMessage = reason.error.messages != null
? reason.error.messages ? reason.error.messages
: reason.error.replaceAll('\\', ''); : reason.error.replaceAll('\\', '');
this.messageService.error(errorMessage); if (error) {
console.error(reason); errorMessage = 'Произошла неизвестная ошибка. Обратитесь к системному администратору';
let errorCode = this.extractCode(errorDescription);
if (errorCode) {
errorMessage = EsiaErrorDetail.getDescription(errorCode);
}
let consoleError = error + ', error description = ' + errorDescription;
this.messageService.error(errorMessage);
console.error(consoleError);
}
else {
this.messageService.error(errorMessage);
console.error(reason);
}
}); });
return false; return false;
} }
@ -71,4 +79,10 @@ export abstract class AuthGuard implements CanActivate {
private checkAccess(): boolean { private checkAccess(): boolean {
return this.authenticationService.isAuthenticated(); return this.authenticationService.isAuthenticated();
} }
private extractCode(message: string): string | null {
const regex = /ESIA-\d{6}/;
const match = message.match(regex);
return match ? match[0] : null;
}
} }

View file

@ -1,26 +1,33 @@
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
HttpXsrfTokenExtractor
} from '@angular/common/http';
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
import {CookieService} from "ngx-cookie";
import {TokenConstants} from "../../security/TokenConstants"; import {TokenConstants} from "../../security/TokenConstants";
@Injectable() @Injectable()
export class AbsoluteUrlCsrfInterceptor implements HttpInterceptor { export class AbsoluteUrlCsrfInterceptor implements HttpInterceptor {
constructor(private cookieService: CookieService) { constructor(private extractor: HttpXsrfTokenExtractor) {
} }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let requestToForward = req; if (req.method === 'GET' || req.method === 'HEAD') {
let token = this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME) as string; return next.handle(req);
if (token != null) {
let headers = {};
let headerName = TokenConstants.CSRF_HEADER_NAME;
headers[headerName] = token;
requestToForward = req.clone({setHeaders: headers});
} }
return next.handle(requestToForward); let token = this.extractor.getToken();
let headerName = TokenConstants.CSRF_HEADER_NAME;
if (token != null && !req.headers.has(headerName)) {
let headers = {};
headers[headerName] = token;
req = req.clone({setHeaders: headers});
}
return next.handle(req);
} }
} }

View file

@ -12,6 +12,7 @@
"skipLibCheck": true, "skipLibCheck": true,
"noImplicitAny": false, "noImplicitAny": false,
"outDir": "build_dev/js", "outDir": "build_dev/js",
"allowSyntheticDefaultImports": true,
"typeRoots": [ "typeRoots": [
"node_modules/@types" "node_modules/@types"
], ],

View file

@ -3,7 +3,6 @@ const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserPlugin = require('terser-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
@ -14,9 +13,9 @@ function _path(p) {
module.exports = { module.exports = {
mode: 'production', mode: 'production',
entry: { entry: {
'polyfills': './build/scripts/polyfills.js', polyfills: './build/scripts/polyfills.js',
'vendor': './build/scripts/vendor.js', vendor: './build/scripts/vendor.js',
'main': './build/scripts/main.aot.js' main: './build/scripts/main.aot.js',
}, },
context: process.cwd(), context: process.cwd(),
@ -24,26 +23,33 @@ module.exports = {
output: { output: {
path: path.join(process.cwd(), './dist'), path: path.join(process.cwd(), './dist'),
filename: '[name].[chunkhash].bundle.js', filename: '[name].[chunkhash].bundle.js',
chunkFilename: '[id].[chunkhash].chunk.js' chunkFilename: '[id].[chunkhash].chunk.js',
assetModuleFilename: 'src/resources/[base]',
publicPath: 'auto'
}, },
module: { module: {
rules: [ rules: [
{ {
test: /\.js$/, test: /\.js$/,
use: {
loader: 'babel-loader', loader: 'babel-loader',
include: [path.resolve(__dirname, "node_modules")],
options: { options: {
presets: ['@babel/preset-env'] presets: ['@babel/preset-env']
} }
}
}, },
{ {
test: /\.js$/, test: /\.js$/,
use: {
loader: 'angular-router-loader?aot=true' loader: 'angular-router-loader?aot=true'
}
}, },
{ {
test: /\.html$/, test: /\.html$/,
use: {
loader: 'raw-loader' loader: 'raw-loader'
}
}, },
{ {
test: /\.css$/, test: /\.css$/,
@ -56,12 +62,14 @@ module.exports = {
// publicPath: '../' // publicPath: '../'
} }
}, },
"css-loader" {
loader: 'css-loader'
}
] ]
}, },
{ {
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf)$/, test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf)$/,
loader: 'file-loader?name=src/resources/[name].[hash].[ext]' type: 'asset/resource'
} }
] ]
}, },
@ -69,7 +77,6 @@ module.exports = {
optimization: { optimization: {
minimizer: [ minimizer: [
new TerserPlugin({ new TerserPlugin({
cache: true,
parallel: true, parallel: true,
terserOptions: { terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
@ -83,7 +90,6 @@ module.exports = {
}, },
plugins: [ plugins: [
// new BundleAnalyzerPlugin(),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
template: 'index.webpack.html', template: 'index.webpack.html',
filename: 'index.html', filename: 'index.html',
@ -97,11 +103,11 @@ module.exports = {
{from: 'src/resources/app-config.json', to: 'src/resources/app-config.json'}, {from: 'src/resources/app-config.json', to: 'src/resources/app-config.json'},
{from: 'src/resources/app.version', to: 'src/resources/app.version'}, {from: 'src/resources/app.version', to: 'src/resources/app.version'},
{from: 'src/resources/landing', to: 'src/resources/landing'} {from: 'src/resources/landing', to: 'src/resources/landing'}
]), ]),
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
filename: '[name].[hash].css', filename: '[name].[fullhash].css',
chunkFilename: '[id].[hash].css' chunkFilename: '[id].[fullhash].css'
}), }),
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
$: "jquery", $: "jquery",

View file

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>ru.cg.webbpm.packages.base</groupId> <groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>resources</artifactId> <artifactId>resources</artifactId>
<version>3.182.0</version> <version>3.185.0</version>
<organization> <organization>
<name>Micord</name> <name>Micord</name>
</organization> </organization>
@ -28,13 +28,13 @@
<jooq.version>3.19.3</jooq.version> <jooq.version>3.19.3</jooq.version>
<jupiter.version>5.10.2</jupiter.version> <jupiter.version>5.10.2</jupiter.version>
<enforcer.manageVersions>true</enforcer.manageVersions> <enforcer.manageVersions>true</enforcer.manageVersions>
<webbpm-platform.version>3.182.0</webbpm-platform.version> <webbpm-platform.version>3.185.0</webbpm-platform.version>
<h2.version>1.4.200</h2.version> <h2.version>1.4.200</h2.version>
<build.timestamp>1004163046</build.timestamp> <build.timestamp>1107112530</build.timestamp>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<junit.platform.version>1.10.0</junit.platform.version> <junit.platform.version>1.10.0</junit.platform.version>
<enforcer.manageExclusions>true</enforcer.manageExclusions> <enforcer.manageExclusions>true</enforcer.manageExclusions>
<revision>3.182.0</revision> <revision>3.185.0</revision>
<metadata.ts.filename>typescript.metadata.json</metadata.ts.filename> <metadata.ts.filename>typescript.metadata.json</metadata.ts.filename>
<package.repository.url>https://repo.micord.ru</package.repository.url> <package.repository.url>https://repo.micord.ru</package.repository.url>
<maven.build.timestamp.format>MMddHHmmss</maven.build.timestamp.format> <maven.build.timestamp.format>MMddHHmmss</maven.build.timestamp.format>
@ -47,19 +47,19 @@
<dependency> <dependency>
<groupId>ru.cg.webbpm.packages.base</groupId> <groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>converters</artifactId> <artifactId>converters</artifactId>
<version>3.182.0</version> <version>3.185.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ru.cg.webbpm.packages.base</groupId> <groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>backend</artifactId> <artifactId>backend</artifactId>
<version>3.182.0</version> <version>3.185.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ru.cg.webbpm.packages.base</groupId> <groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>frontend</artifactId> <artifactId>frontend</artifactId>
<version>3.182.0</version> <version>3.185.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -60,7 +60,7 @@
<ul> <ul>
<li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x435;&#x448;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>https://www.wildberries.ru/catalog/${sku}/detail.aspx</code></li> <li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x435;&#x448;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>https://www.wildberries.ru/catalog/${sku}/detail.aspx</code></li>
<li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x443;&#x442;&#x440;&#x435;&#x43d;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>products/ru.cg.webbpm.packages.base:resources:jar:3.182.0</code></li> <li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x443;&#x442;&#x440;&#x435;&#x43d;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>products/ru.cg.webbpm.packages.base:resources:jar:3.185.0</code></li>
</ul> </ul>
</li> </li>
<li> <li>

View file

@ -4,17 +4,17 @@
<description>Base webbpm package</description> <description>Base webbpm package</description>
<groupId>ru.cg.webbpm.packages.base</groupId> <groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>resources</artifactId> <artifactId>resources</artifactId>
<version>3.182.0</version> <version>3.185.0</version>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<backendModule> <backendModule>
<groupId>ru.cg.webbpm.packages.base</groupId> <groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>backend</artifactId> <artifactId>backend</artifactId>
<version>3.182.0</version> <version>3.185.0</version>
</backendModule> </backendModule>
<frontendModule> <frontendModule>
<packageName>@webbpm/base-package</packageName> <packageName>@webbpm/base-package</packageName>
<version>3.182.0</version> <version>3.185.0</version>
</frontendModule> </frontendModule>
</packageInfo> </packageInfo>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка.html</documentation> <documentation>component/buttons/Кнопка.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_отмены.html</documentation> <documentation>component/buttons/Кнопка_отмены.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_очистки_фильтра.html</documentation> <documentation>component/buttons/Кнопка_очистки_фильтра.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_удаления.html</documentation> <documentation>component/buttons/Кнопка_удаления.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопкаагрузки.html</documentation> <documentation>component/buttons/Кнопкаагрузки.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_вызова_ошибки.html</documentation> <documentation>component/buttons/Кнопка_вызова_ошибки.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<documentation>component/buttons/Кнопка_выполнения_бизнес-процесса.html</documentation> <documentation>component/buttons/Кнопка_выполнения_бизнес-процесса.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_выполнения_SQL.html</documentation> <documentation>component/buttons/Кнопка_выполнения_SQL.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_для_фильтрации.html</documentation> <documentation>component/buttons/Кнопка_для_фильтрации.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопкаавигации.html</documentation> <documentation>component/buttons/Кнопкаавигации.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_сохранения.html</documentation> <documentation>component/buttons/Кнопка_сохранения.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_выбора.html</documentation> <documentation>component/buttons/Кнопка_выбора.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_подписи.html</documentation> <documentation>component/buttons/Кнопка_подписи.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопкаапуска_бизнес-процесса.html</documentation> <documentation>component/buttons/Кнопкаапуска_бизнес-процесса.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/reporting/Кнопка_печати_из_графа_сущности.html</documentation> <documentation>component/buttons/reporting/Кнопка_печати_из_графа_сущности.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/reporting/Кнопка_печати_отчета_из_формы.html</documentation> <documentation>component/buttons/reporting/Кнопка_печати_отчета_из_формы.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Сворачиваемая_панель.html</documentation> <documentation>component/containers/Сворачиваемая_панель.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Диалог.html</documentation> <documentation>component/containers/Диалог.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Контейнер_с_кнопками.html</documentation> <documentation>component/containers/Контейнер_с_кнопками.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Группа_полей.html</documentation> <documentation>component/containers/Группа_полей.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Набор_фильтров.html</documentation> <documentation>component/containers/Набор_фильтров.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Форма.html</documentation> <documentation>component/containers/Форма.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Горизонтальный_контейнер.html</documentation> <documentation>component/containers/Горизонтальный_контейнер.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Контейнер_вкладок.html</documentation> <documentation>component/containers/Контейнер_вкладок.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Вкладка.html</documentation> <documentation>component/containers/Вкладка.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Вертикальный_контейнер.html</documentation> <documentation>component/containers/Вертикальный_контейнер.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Окно.html</documentation> <documentation>component/containers/Окно.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/editable-grids/EditableGrid.html</documentation> <documentation>component/editable-grids/EditableGrid.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/autocomplete</localization> <localization>META-INF/components/localization/editable-grids/autocomplete</localization>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/check-box</localization> <localization>META-INF/components/localization/editable-grids/check-box</localization>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/combo-box</localization> <localization>META-INF/components/localization/editable-grids/combo-box</localization>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/date-time-picker</localization> <localization>META-INF/components/localization/editable-grids/date-time-picker</localization>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/money-field</localization> <localization>META-INF/components/localization/editable-grids/money-field</localization>
<internal>true</internal> <internal>true</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/number-field</localization> <localization>META-INF/components/localization/editable-grids/number-field</localization>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/one-to-many</localization> <localization>META-INF/components/localization/editable-grids/one-to-many</localization>
<internal>true</internal> <internal>true</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/one-to-many</localization> <localization>META-INF/components/localization/editable-grids/one-to-many</localization>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -6,11 +6,11 @@
<localization>META-INF/components/localization/editable-grids/read-only</localization> <localization>META-INF/components/localization/editable-grids/read-only</localization>
<internal>true</internal> <internal>true</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -5,11 +5,11 @@
<category>editable-grids</category> <category>editable-grids</category>
<internal>true</internal> <internal>true</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>Статичный_выпадающий_список_колонки_таблицы.html</documentation> <documentation>Статичный_выпадающий_список_колонки_таблицы.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/text-area</localization> <localization>META-INF/components/localization/editable-grids/text-area</localization>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/text-field</localization> <localization>META-INF/components/localization/editable-grids/text-field</localization>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/time-picker</localization> <localization>META-INF/components/localization/editable-grids/time-picker</localization>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/ФИАС.html</documentation> <documentation>component/fields/ФИАС.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Поле_ввода_с_подбором_значения.html</documentation> <documentation>component/fields/Поле_ввода_с_подбором_значения.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Флаг.html</documentation> <documentation>component/fields/Флаг.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Выпадающий_список.html</documentation> <documentation>component/fields/Выпадающий_список.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Дата.html</documentation> <documentation>component/fields/Дата.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/EditableOneToMany.html</documentation> <documentation>component/fields/EditableOneToMany.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Файл.html</documentation> <documentation>component/fields/Файл.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Файл.html</documentation> <documentation>component/fields/Файл.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/ManyToMany.html</documentation> <documentation>component/fields/ManyToMany.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/ManyToManyField.html</documentation> <documentation>component/fields/ManyToManyField.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Денежное_поле.html</documentation> <documentation>component/fields/Денежное_поле.html</documentation>
<internal>true</internal> <internal>true</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Числовое_поле.html</documentation> <documentation>component/fields/Числовое_поле.html</documentation>
<internal>false</internal> <internal>false</internal>
<versions> <versions>
<studioVersion>3.182.0</studioVersion> <studioVersion>3.185.0</studioVersion>
<packageVersions> <packageVersions>
<entry> <entry>
<key>ru.cg.webbpm.packages.base.resources</key> <key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value> <value>3.185.0</value>
</entry> </entry>
</packageVersions> </packageVersions>
</versions> </versions>

Some files were not shown because too many files have changed in this diff Show more