storage file fixes
This commit is contained in:
parent
4417532935
commit
2a766dc37f
|
@ -1,10 +0,0 @@
|
|||
package eu.eudat.configurations.filepath;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(FilePathsProperties.class)
|
||||
public class FilePathsConfiguration {
|
||||
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package eu.eudat.configurations.filepath;
|
||||
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "file-paths")
|
||||
public class FilePathsProperties {
|
||||
private String externalUrls;
|
||||
private String semantics;
|
||||
private String h2020template;
|
||||
private String h2020DatasetTemplate;
|
||||
private String pidLinks;
|
||||
|
||||
public String getExternalUrls() {
|
||||
return externalUrls;
|
||||
}
|
||||
|
||||
public void setExternalUrls(String externalUrls) {
|
||||
this.externalUrls = externalUrls;
|
||||
}
|
||||
|
||||
public String getSemantics() {
|
||||
return semantics;
|
||||
}
|
||||
|
||||
public void setSemantics(String semantics) {
|
||||
this.semantics = semantics;
|
||||
}
|
||||
|
||||
public String getH2020template() {
|
||||
return h2020template;
|
||||
}
|
||||
|
||||
public void setH2020template(String h2020template) {
|
||||
this.h2020template = h2020template;
|
||||
}
|
||||
|
||||
public String getH2020DatasetTemplate() {
|
||||
return h2020DatasetTemplate;
|
||||
}
|
||||
|
||||
public void setH2020DatasetTemplate(String h2020DatasetTemplate) {
|
||||
this.h2020DatasetTemplate = h2020DatasetTemplate;
|
||||
}
|
||||
|
||||
public String getPidLinks() {
|
||||
return pidLinks;
|
||||
}
|
||||
|
||||
public void setPidLinks(String pidLinks) {
|
||||
this.pidLinks = pidLinks;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
package eu.eudat.data.types;public class SQLXMLType {
|
||||
}
|
|
@ -6,7 +6,6 @@ import eu.eudat.authorization.Permission;
|
|||
import eu.eudat.commons.JsonHandlingService;
|
||||
import eu.eudat.commons.XmlHandlingService;
|
||||
import eu.eudat.commons.enums.*;
|
||||
import eu.eudat.configurations.filepath.FilePathsProperties;
|
||||
import eu.eudat.commons.scope.user.UserScope;
|
||||
import eu.eudat.commons.types.descriptiontemplate.*;
|
||||
import eu.eudat.commons.types.descriptiontemplate.fielddata.BaseFieldDataEntity;
|
||||
|
@ -38,6 +37,7 @@ import eu.eudat.service.fielddatahelper.FieldDataHelperServiceProvider;
|
|||
import eu.eudat.service.mail.MailService;
|
||||
import eu.eudat.service.mail.SimpleMail;
|
||||
import eu.eudat.service.responseutils.ResponseUtilsService;
|
||||
import eu.eudat.service.storage.StorageFileService;
|
||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||
import gr.cite.tools.data.builder.BuilderFactory;
|
||||
import gr.cite.tools.data.deleter.DeleterFactory;
|
||||
|
@ -103,7 +103,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
|||
private final MailService mailService;
|
||||
private final Environment environment;
|
||||
private final ResponseUtilsService responseUtilsService;
|
||||
private final FilePathsProperties filePathsProperties;
|
||||
private final StorageFileService storageFileService;
|
||||
private final JsonHandlingService jsonHandlingService;
|
||||
|
||||
@Autowired
|
||||
|
@ -115,7 +115,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
|||
ConventionService conventionService,
|
||||
MessageSource messageSource,
|
||||
XmlHandlingService xmlHandlingService,
|
||||
FieldDataHelperServiceProvider fieldDataHelperServiceProvider, QueryFactory queryFactory, ErrorThesaurusProperties errors, ValidationService validationService, MailService mailService, Environment environment, ResponseUtilsService responseUtilsService, FilePathsProperties filePathsProperties, JsonHandlingService jsonHandlingService) {
|
||||
FieldDataHelperServiceProvider fieldDataHelperServiceProvider, QueryFactory queryFactory, ErrorThesaurusProperties errors, ValidationService validationService, MailService mailService, Environment environment, ResponseUtilsService responseUtilsService, StorageFileService storageFileService, JsonHandlingService jsonHandlingService) {
|
||||
this.entityManager = entityManager;
|
||||
this.userScope = userScope;
|
||||
this.authorizationService = authorizationService;
|
||||
|
@ -131,7 +131,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
|||
this.mailService = mailService;
|
||||
this.environment = environment;
|
||||
this.responseUtilsService = responseUtilsService;
|
||||
this.filePathsProperties = filePathsProperties;
|
||||
this.storageFileService = storageFileService;
|
||||
this.jsonHandlingService = jsonHandlingService;
|
||||
}
|
||||
|
||||
|
@ -191,6 +191,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
|||
data.setId(UUID.randomUUID());
|
||||
data.setIsActive(IsActive.Active);
|
||||
data.setCreatedAt(Instant.now());
|
||||
data.setUpdatedAt(Instant.now());
|
||||
data.setDescriptionTemplateId(id);
|
||||
data.setUserId(user.getUserId());
|
||||
data.setRole(user.getRole());
|
||||
|
@ -230,6 +231,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
|||
data.setId(UUID.randomUUID());
|
||||
data.setIsActive(IsActive.Active);
|
||||
data.setCreatedAt(Instant.now());
|
||||
data.setUpdatedAt(Instant.now());
|
||||
data.setRole(UserDescriptionTemplateRole.Owner);
|
||||
data.setUserId(userScope.getUserId());
|
||||
data.setDescriptionTemplateId(descriptionTemplateEntity.getId());
|
||||
|
@ -826,32 +828,17 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
|||
}
|
||||
|
||||
private void loadSemantics() throws IOException {
|
||||
String filePath = this.filePathsProperties.getSemantics();
|
||||
logger.info("Loaded also config file: " + filePath);
|
||||
if (filePath != null) {
|
||||
InputStream stream = null;
|
||||
byte[] bytes = this.storageFileService.getSemanticsFile();
|
||||
if (bytes != null) {
|
||||
try {
|
||||
stream = getStreamFromPath(filePath);
|
||||
String json = new String(getStreamFromPath(filePath).readAllBytes(), StandardCharsets.UTF_8);
|
||||
String json = new String(bytes, StandardCharsets.UTF_8);
|
||||
semantics = List.of(jsonHandlingService.fromJson(Semantic[].class, json));
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
if (stream != null) stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getStreamFromPath(String filePath) {
|
||||
try {
|
||||
return new FileInputStream(filePath);
|
||||
} catch (FileNotFoundException e) {
|
||||
logger.info("loading from classpath");
|
||||
return getClass().getClassLoader().getResourceAsStream(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import eu.eudat.model.builder.LanguageBuilder;
|
|||
import eu.eudat.model.deleter.LanguageDeleter;
|
||||
import eu.eudat.model.persist.LanguagePersist;
|
||||
import eu.eudat.service.dmpblueprint.DmpBlueprintServiceImpl;
|
||||
import eu.eudat.service.storage.StorageFileService;
|
||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||
import gr.cite.tools.data.builder.BuilderFactory;
|
||||
import gr.cite.tools.data.deleter.DeleterFactory;
|
||||
|
@ -27,8 +28,6 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.management.InvalidApplicationException;
|
||||
|
@ -52,11 +51,12 @@ public class LanguageServiceImpl implements LanguageService {
|
|||
private final MessageSource messageSource;
|
||||
private final ErrorThesaurusProperties errors;
|
||||
private final Environment environment;
|
||||
private final StorageFileService storageFileService;
|
||||
|
||||
|
||||
public LanguageServiceImpl(
|
||||
EntityManager entityManager, AuthorizationService authorizationService, DeleterFactory deleterFactory, BuilderFactory builderFactory,
|
||||
ConventionService conventionService, MessageSource messageSource, ErrorThesaurusProperties errors, Environment environment){
|
||||
ConventionService conventionService, MessageSource messageSource, ErrorThesaurusProperties errors, Environment environment, StorageFileService storageFileService){
|
||||
this.entityManager = entityManager;
|
||||
this.authorizationService = authorizationService;
|
||||
this.deleterFactory = deleterFactory;
|
||||
|
@ -65,6 +65,7 @@ public class LanguageServiceImpl implements LanguageService {
|
|||
this.messageSource = messageSource;
|
||||
this.errors = errors;
|
||||
this.environment = environment;
|
||||
this.storageFileService = storageFileService;
|
||||
}
|
||||
|
||||
|
||||
|
@ -105,12 +106,7 @@ public class LanguageServiceImpl implements LanguageService {
|
|||
public String getPayload(String code) throws IOException {
|
||||
this.authorizationService.authorizeForce(Permission.BrowseLanguage);
|
||||
|
||||
String fileName = this.environment.getProperty("language.path") + code + ".json";
|
||||
InputStream is = new FileInputStream(fileName);
|
||||
|
||||
byte[] content = new byte[is.available()];
|
||||
is.read(content);
|
||||
is.close();
|
||||
byte[] content = this.storageFileService.getLanguage(code);
|
||||
|
||||
return new String(content, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ import java.util.List;
|
|||
@ConfigurationProperties(prefix = "storage.service")
|
||||
public class StorageFileProperties {
|
||||
private List<StorageConfig> storages;
|
||||
private List<StaticFilesConfig> staticFiles;
|
||||
private StaticFilesConfig staticFiles;
|
||||
private MaterialConfig materialFiles;
|
||||
private String defaultLanguage;
|
||||
|
||||
private int tempStoreLifetimeSeconds;
|
||||
|
||||
|
@ -29,6 +31,30 @@ public class StorageFileProperties {
|
|||
this.tempStoreLifetimeSeconds = tempStoreLifetimeSeconds;
|
||||
}
|
||||
|
||||
public StaticFilesConfig getStaticFiles() {
|
||||
return staticFiles;
|
||||
}
|
||||
|
||||
public void setStaticFiles(StaticFilesConfig staticFiles) {
|
||||
this.staticFiles = staticFiles;
|
||||
}
|
||||
|
||||
public String getDefaultLanguage() {
|
||||
return defaultLanguage;
|
||||
}
|
||||
|
||||
public void setDefaultLanguage(String defaultLanguage) {
|
||||
this.defaultLanguage = defaultLanguage;
|
||||
}
|
||||
|
||||
public MaterialConfig getMaterialFiles() {
|
||||
return materialFiles;
|
||||
}
|
||||
|
||||
public void setMaterialFiles(MaterialConfig materialFiles) {
|
||||
this.materialFiles = materialFiles;
|
||||
}
|
||||
|
||||
public static class StorageConfig{
|
||||
private StorageType type;
|
||||
private String basePath;
|
||||
|
@ -49,12 +75,132 @@ public class StorageFileProperties {
|
|||
this.basePath = basePath;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MaterialConfig {
|
||||
private String localizedNameLanguageKey;
|
||||
private String userGuide;
|
||||
private String userGuideNamePattern;
|
||||
private String about;
|
||||
private String aboutNamePattern;
|
||||
private String termsOfService;
|
||||
private String termsOfServiceNamePattern;
|
||||
private String glossary;
|
||||
private String glossaryNamePattern;
|
||||
private String language;
|
||||
private String languageNamePattern;
|
||||
private String faq;
|
||||
private String faqNamePattern;
|
||||
|
||||
public String getLocalizedNameLanguageKey() {
|
||||
return localizedNameLanguageKey;
|
||||
}
|
||||
|
||||
public void setLocalizedNameLanguageKey(String localizedNameLanguageKey) {
|
||||
this.localizedNameLanguageKey = localizedNameLanguageKey;
|
||||
}
|
||||
|
||||
public String getUserGuide() {
|
||||
return userGuide;
|
||||
}
|
||||
|
||||
public void setUserGuide(String userGuide) {
|
||||
this.userGuide = userGuide;
|
||||
}
|
||||
|
||||
public String getAbout() {
|
||||
return about;
|
||||
}
|
||||
|
||||
public void setAbout(String about) {
|
||||
this.about = about;
|
||||
}
|
||||
|
||||
public String getTermsOfService() {
|
||||
return termsOfService;
|
||||
}
|
||||
|
||||
public void setTermsOfService(String termsOfService) {
|
||||
this.termsOfService = termsOfService;
|
||||
}
|
||||
|
||||
public String getGlossary() {
|
||||
return glossary;
|
||||
}
|
||||
|
||||
public void setGlossary(String glossary) {
|
||||
this.glossary = glossary;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public String getFaq() {
|
||||
return faq;
|
||||
}
|
||||
|
||||
public void setFaq(String faq) {
|
||||
this.faq = faq;
|
||||
}
|
||||
|
||||
public String getUserGuideNamePattern() {
|
||||
return userGuideNamePattern;
|
||||
}
|
||||
|
||||
public void setUserGuideNamePattern(String userGuideNamePattern) {
|
||||
this.userGuideNamePattern = userGuideNamePattern;
|
||||
}
|
||||
|
||||
public String getAboutNamePattern() {
|
||||
return aboutNamePattern;
|
||||
}
|
||||
|
||||
public void setAboutNamePattern(String aboutNamePattern) {
|
||||
this.aboutNamePattern = aboutNamePattern;
|
||||
}
|
||||
|
||||
public String getTermsOfServiceNamePattern() {
|
||||
return termsOfServiceNamePattern;
|
||||
}
|
||||
|
||||
public void setTermsOfServiceNamePattern(String termsOfServiceNamePattern) {
|
||||
this.termsOfServiceNamePattern = termsOfServiceNamePattern;
|
||||
}
|
||||
|
||||
public String getGlossaryNamePattern() {
|
||||
return glossaryNamePattern;
|
||||
}
|
||||
|
||||
public void setGlossaryNamePattern(String glossaryNamePattern) {
|
||||
this.glossaryNamePattern = glossaryNamePattern;
|
||||
}
|
||||
|
||||
public String getLanguageNamePattern() {
|
||||
return languageNamePattern;
|
||||
}
|
||||
|
||||
public void setLanguageNamePattern(String languageNamePattern) {
|
||||
this.languageNamePattern = languageNamePattern;
|
||||
}
|
||||
|
||||
public String getFaqNamePattern() {
|
||||
return faqNamePattern;
|
||||
}
|
||||
|
||||
public void setFaqNamePattern(String faqNamePattern) {
|
||||
this.faqNamePattern = faqNamePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StaticFilesConfig{
|
||||
private String externalUrls;
|
||||
private String semantics;
|
||||
private String h2020template;
|
||||
private String h2020DatasetTemplate;
|
||||
private String h2020Template;
|
||||
private String h2020DescriptionTemplate;
|
||||
private String pidLinks;
|
||||
|
||||
public String getExternalUrls() {
|
||||
|
@ -73,20 +219,20 @@ public class StorageFileProperties {
|
|||
this.semantics = semantics;
|
||||
}
|
||||
|
||||
public String getH2020template() {
|
||||
return h2020template;
|
||||
public String getH2020Template() {
|
||||
return h2020Template;
|
||||
}
|
||||
|
||||
public void setH2020template(String h2020template) {
|
||||
this.h2020template = h2020template;
|
||||
public void setH2020Template(String h2020Template) {
|
||||
this.h2020Template = h2020Template;
|
||||
}
|
||||
|
||||
public String getH2020DatasetTemplate() {
|
||||
return h2020DatasetTemplate;
|
||||
public String getH2020DescriptionTemplate() {
|
||||
return h2020DescriptionTemplate;
|
||||
}
|
||||
|
||||
public void setH2020DatasetTemplate(String h2020DatasetTemplate) {
|
||||
this.h2020DatasetTemplate = h2020DatasetTemplate;
|
||||
public void setH2020DescriptionTemplate(String h2020DescriptionTemplate) {
|
||||
this.h2020DescriptionTemplate = h2020DescriptionTemplate;
|
||||
}
|
||||
|
||||
public String getPidLinks() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import gr.cite.tools.fieldset.FieldSet;
|
|||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Instant;
|
||||
|
@ -28,4 +29,40 @@ public interface StorageFileService extends ApplicationListener<ApplicationReady
|
|||
byte[] readAsBytesSafe(UUID fileId);
|
||||
String readByFileRefAsTextSafe(String fileRef, StorageType storageType, Charset charset);
|
||||
byte[] readByFileRefAsBytesSafe(String fileRef, StorageType storageType);
|
||||
|
||||
byte[] getSemanticsFile();
|
||||
|
||||
byte[] getExternalUrlsFile();
|
||||
|
||||
byte[] getPidLinksFile();
|
||||
|
||||
byte[] getH2020TemplateFile();
|
||||
|
||||
byte[] getH2020DescriptionTemplateFile();
|
||||
|
||||
byte[] getUserGuide(String language);
|
||||
|
||||
void setUserGuide(String language, byte[] payload);
|
||||
|
||||
byte[] getAbout(String language);
|
||||
|
||||
void setAbout(String language, byte[] payload);
|
||||
|
||||
byte[] getTermsOfService(String language);
|
||||
|
||||
void setTermsOfService(String language, byte[] payload);
|
||||
|
||||
byte[] getGlossary(String language);
|
||||
|
||||
void setGlossary(String language, byte[] payload);
|
||||
|
||||
byte[] getLanguage(String language);
|
||||
|
||||
File getLanguageFileName(String language);
|
||||
|
||||
void setLanguage(String language, byte[] payload);
|
||||
|
||||
byte[] getFaq(String language);
|
||||
|
||||
void setFaq(String language, byte[] payload);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
|
@ -60,6 +59,7 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
|
||||
}
|
||||
|
||||
//region storage management
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationReadyEvent event) {
|
||||
this.bootstrap();
|
||||
|
@ -84,7 +84,7 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
public StorageFile persistBytes(StorageFilePersist model, byte[] payload, FieldSet fields) throws IOException {
|
||||
StorageFileEntity storageFile = this.buildDataEntry(model);
|
||||
|
||||
File file = ResourceUtils.getFile(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
if (!file.exists()) throw new FileAlreadyExistsException(storageFile.getName());
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
|
@ -123,10 +123,10 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
if (storageFile == null) return false;
|
||||
this.authorizeForce(storageFile, StorageFilePermission.Read);
|
||||
|
||||
File file = ResourceUtils.getFile(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
if (!file.exists()) return false;
|
||||
|
||||
File destinationFile = ResourceUtils.getFile(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
File destinationFile = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
boolean fileCopied = FileCopyUtils.copy(file, destinationFile) > 0;
|
||||
if (!fileCopied) return false;
|
||||
return file.delete();
|
||||
|
@ -144,10 +144,10 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
if (storageFile == null) return false;
|
||||
this.authorizeForce(storageFile, StorageFilePermission.Read);
|
||||
|
||||
File file = ResourceUtils.getFile(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
if (!file.exists()) return false;
|
||||
|
||||
File destinationFile = ResourceUtils.getFile(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
File destinationFile = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
return FileCopyUtils.copy(file, destinationFile) > 0;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
@ -163,7 +163,7 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
if (storageFile == null) return false;
|
||||
this.authorizeForce(storageFile, StorageFilePermission.Read);
|
||||
|
||||
File file = ResourceUtils.getFile(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
return file.exists();
|
||||
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
public boolean fileRefExists(String fileRef, StorageType storageType) {
|
||||
File file = null;
|
||||
try {
|
||||
file = ResourceUtils.getFile(this.filePath(fileRef, storageType));
|
||||
file = new File(this.filePath(fileRef, storageType));
|
||||
return file.exists();
|
||||
} catch (Exception ex) {
|
||||
logger.warn("problem reading byte content of storage file " + fileRef, ex);
|
||||
|
@ -207,7 +207,7 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
this.entityManager.merge(storageFile);
|
||||
this.entityManager.flush();
|
||||
|
||||
File file = ResourceUtils.getFile(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
|
||||
if (!file.exists()) return true;
|
||||
|
||||
return file.delete();
|
||||
|
@ -249,20 +249,13 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
|
||||
@Override
|
||||
public byte[] readByFileRefAsBytesSafe(String fileRef, StorageType storageType) {
|
||||
|
||||
byte[] bytes = null;
|
||||
try {
|
||||
File file = ResourceUtils.getFile(this.filePath(fileRef, storageType));
|
||||
if (!file.exists()) return null;
|
||||
try(InputStream inputStream = new FileInputStream(file)){
|
||||
bytes = inputStream.readAllBytes();
|
||||
};
|
||||
|
||||
return this.readFileBytes(this.filePath(fileRef, storageType));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("problem reading byte content of storage file " + fileRef, ex);
|
||||
return null;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private String filePath(String fileRef, StorageType storageType)
|
||||
|
@ -278,4 +271,179 @@ public class StorageFileServiceImpl implements StorageFileService {
|
|||
}
|
||||
this.authorizationService.authorizeForce(Permission.BrowseStorageFile);
|
||||
}
|
||||
|
||||
public byte[] readFileBytes(String path) throws IOException {
|
||||
|
||||
byte[] bytes;
|
||||
File file = new File(path);
|
||||
if (!file.exists()) return null;
|
||||
try(InputStream inputStream = new FileInputStream(file)){
|
||||
bytes = inputStream.readAllBytes();
|
||||
};
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region file paths
|
||||
|
||||
@Override
|
||||
public byte[] getSemanticsFile() {
|
||||
try {
|
||||
return this.readFileBytes(this.config.getStaticFiles().getSemantics());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("problem reading semantics file", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getExternalUrlsFile() {
|
||||
try {
|
||||
return this.readFileBytes(this.config.getStaticFiles().getExternalUrls());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("problem reading ExternalUrls file", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPidLinksFile() {
|
||||
try {
|
||||
return this.readFileBytes(this.config.getStaticFiles().getPidLinks());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("problem reading PidLinks file", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getH2020TemplateFile() {
|
||||
try {
|
||||
return this.readFileBytes(this.config.getStaticFiles().getH2020Template());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("problem reading H2020Template file", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getH2020DescriptionTemplateFile() {
|
||||
try {
|
||||
return this.readFileBytes(this.config.getStaticFiles().getH2020DescriptionTemplate());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("problem reading H2020DescriptionTemplate file", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region materials
|
||||
|
||||
@Override
|
||||
public byte[] getUserGuide(String language) {
|
||||
return this.getLocalized(this.config.getMaterialFiles().getUserGuide(), this.config.getMaterialFiles().getUserGuideNamePattern(), language);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserGuide(String language, byte[] payload) {
|
||||
this.setLocalized(this.config.getMaterialFiles().getUserGuide(), this.config.getMaterialFiles().getUserGuideNamePattern(), language, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getAbout(String language) {
|
||||
return this.getLocalized(this.config.getMaterialFiles().getAbout(), this.config.getMaterialFiles().getAboutNamePattern(), language);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAbout(String language, byte[] payload) {
|
||||
this.setLocalized(this.config.getMaterialFiles().getAbout(), this.config.getMaterialFiles().getAboutNamePattern(), language, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getTermsOfService(String language) {
|
||||
return this.getLocalized(this.config.getMaterialFiles().getTermsOfService(), this.config.getMaterialFiles().getTermsOfServiceNamePattern(), language);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTermsOfService(String language, byte[] payload) {
|
||||
this.setLocalized(this.config.getMaterialFiles().getTermsOfService(), this.config.getMaterialFiles().getTermsOfServiceNamePattern(), language, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getGlossary(String language) {
|
||||
return this.getLocalized(this.config.getMaterialFiles().getGlossary(), this.config.getMaterialFiles().getGlossaryNamePattern(), language);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGlossary(String language, byte[] payload) {
|
||||
this.setLocalized(this.config.getMaterialFiles().getGlossary(), this.config.getMaterialFiles().getGlossaryNamePattern(), language, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getLanguage(String language) {
|
||||
return this.getLocalized(this.config.getMaterialFiles().getLanguage(), this.config.getMaterialFiles().getLanguageNamePattern(), language);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLanguageFileName(String language) {
|
||||
return this.getLocalizedFile(this.config.getMaterialFiles().getLanguage(), this.config.getMaterialFiles().getLanguageNamePattern(), language);
|
||||
}
|
||||
@Override
|
||||
public void setLanguage(String language, byte[] payload) {
|
||||
this.setLocalized(this.config.getMaterialFiles().getLanguage(), this.config.getMaterialFiles().getLanguageNamePattern(), language, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getFaq(String language) {
|
||||
return this.getLocalized(this.config.getMaterialFiles().getFaq(), this.config.getMaterialFiles().getFaqNamePattern(), language);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFaq(String language, byte[] payload) {
|
||||
this.setLocalized(this.config.getMaterialFiles().getFaq(), this.config.getMaterialFiles().getFaqNamePattern(), language, payload);
|
||||
}
|
||||
|
||||
private byte[] getLocalized(String baseDir, String pattern, String language) {
|
||||
try {
|
||||
File file = this.getLocalizedFile(baseDir, pattern, language);
|
||||
if (!file.exists()) file = this.getLocalizedFile(baseDir, pattern, this.config.getDefaultLanguage());
|
||||
if (!file.exists()) return null;
|
||||
return this.readFileBytes(file.getAbsolutePath());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("problem reading " + baseDir + " " + language, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private File getLocalizedFile(String baseDir, String pattern, String language) {
|
||||
return new File(baseDir + this.getLocalizedNamePattern(pattern, language));
|
||||
}
|
||||
|
||||
private void setLocalized(String baseDir, String pattern, String language, byte[] payload) {
|
||||
try {
|
||||
File file = this.getLocalizedFile(baseDir, pattern, language);
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(payload);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("problem write " + baseDir + " " + language, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getLocalizedNamePattern(String pattern, String language){
|
||||
return pattern.replace(this.config.getMaterialFiles().getLocalizedNameLanguageKey(), language);
|
||||
}
|
||||
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package eu.eudat.controllers;
|
|||
|
||||
import eu.eudat.authorization.Permission;
|
||||
import eu.eudat.models.data.helpers.responses.ResponseItem;
|
||||
import eu.eudat.service.storage.StorageFileService;
|
||||
import eu.eudat.types.ApiMessageCode;
|
||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -21,22 +22,22 @@ public class LanguageController {
|
|||
|
||||
private Environment environment;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final StorageFileService storageFileService;
|
||||
|
||||
@Autowired
|
||||
public LanguageController(Environment environment, AuthorizationService authorizationService) {
|
||||
public LanguageController(Environment environment, AuthorizationService authorizationService, StorageFileService storageFileService) {
|
||||
this.environment = environment;
|
||||
this.authorizationService = authorizationService;
|
||||
this.storageFileService = storageFileService;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "update/{lang}", method = RequestMethod.POST)
|
||||
public @ResponseBody
|
||||
ResponseEntity<ResponseItem<String>> updateLang(@PathVariable String lang, @RequestBody String json) throws Exception {
|
||||
this.authorizationService.authorizeForce(Permission.EditLanguage);
|
||||
|
||||
this.storageFileService.setLanguage(lang, lang.getBytes());
|
||||
|
||||
String fileName = this.environment.getProperty("language.path") + lang + ".json";
|
||||
OutputStream os = new FileOutputStream(fileName);
|
||||
os.write(json.getBytes());
|
||||
os.close();
|
||||
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<String>().status(ApiMessageCode.SUCCESS_MESSAGE).message("Updated").payload("Updated"));
|
||||
}
|
||||
|
||||
|
@ -45,19 +46,16 @@ public class LanguageController {
|
|||
|
||||
this.authorizationService.authorizeForce(Permission.BrowseLanguage);
|
||||
|
||||
String fileName = this.environment.getProperty("language.path") + lang + ".json";
|
||||
InputStream is = new FileInputStream(fileName);
|
||||
File file = this.storageFileService.getLanguageFileName(lang);
|
||||
byte[] content = this.storageFileService.getLanguage(lang);
|
||||
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentLength(is.available());
|
||||
responseHeaders.setContentLength(content.length);
|
||||
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
|
||||
responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName);
|
||||
responseHeaders.set("Content-Disposition", "attachment;filename=" + file.getName());
|
||||
responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition");
|
||||
responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type");
|
||||
|
||||
byte[] content = new byte[is.available()];
|
||||
is.read(content);
|
||||
is.close();
|
||||
|
||||
return new ResponseEntity<>(content, responseHeaders, HttpStatus.OK);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ package eu.eudat.logic.proxy.config.configloaders;
|
|||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import eu.eudat.configurations.filepath.FilePathsProperties;
|
||||
import eu.eudat.logic.proxy.config.ExternalUrls;
|
||||
import eu.eudat.models.data.pid.PidLinks;
|
||||
import eu.eudat.service.storage.StorageFileService;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -25,6 +25,7 @@ import javax.xml.xpath.XPathConstants;
|
|||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -41,104 +42,77 @@ public class DefaultConfigLoader implements ConfigLoader {
|
|||
private Map<String, String> keyToSourceMap;
|
||||
|
||||
@Autowired
|
||||
private FilePathsProperties config;
|
||||
private StorageFileService storageFileService;
|
||||
|
||||
|
||||
private void setExternalUrls() {
|
||||
String fileUrl = this.config.getExternalUrls();
|
||||
logger.info("Loaded also config file: " + fileUrl);
|
||||
InputStream is = null;
|
||||
byte[] bytes = this.storageFileService.getExternalUrlsFile();
|
||||
try {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(ExternalUrls.class);
|
||||
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
|
||||
is = getStreamFromPath(fileUrl);
|
||||
externalUrls = (ExternalUrls) jaxbUnmarshaller.unmarshal(is);
|
||||
externalUrls = (ExternalUrls) jaxbUnmarshaller.unmarshal(new ByteArrayInputStream(bytes));
|
||||
} catch (Exception ex) {
|
||||
logger.error("Cannot find resource", ex);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) is.close();
|
||||
} catch (IOException | NullPointerException e) {
|
||||
logger.warn("Warning: Could not close a stream after reading from file: " + fileUrl, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setDocument() {
|
||||
String filePath = this.config.getH2020template();
|
||||
logger.info("Loaded also config file: " + filePath);
|
||||
InputStream is = null;
|
||||
byte[] bytes = this.storageFileService.getH2020TemplateFile();
|
||||
try {
|
||||
is = getStreamFromPath(filePath);
|
||||
this.document = new XWPFDocument(is);
|
||||
} catch (IOException | NullPointerException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) is.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Warning: Could not close a stream after reading from file: " + filePath, e);
|
||||
}
|
||||
this.document = new XWPFDocument(new ByteArrayInputStream(bytes));
|
||||
} catch (Exception ex) {
|
||||
logger.error("Cannot find resource", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDatasetDocument() {
|
||||
String filePath = this.config.getH2020DatasetTemplate();
|
||||
logger.info("Loaded also config file: " + filePath);
|
||||
InputStream is = null;
|
||||
byte[] bytes = this.storageFileService.getH2020DescriptionTemplateFile();
|
||||
try {
|
||||
is = getStreamFromPath(filePath);
|
||||
this.datasetDocument = new XWPFDocument(is);
|
||||
} catch (IOException | NullPointerException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) is.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Warning: Could not close a stream after reading from file: " + filePath, e);
|
||||
}
|
||||
this.datasetDocument = new XWPFDocument(new ByteArrayInputStream(bytes));
|
||||
} catch (Exception ex) {
|
||||
logger.error("Cannot find resource", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void setPidLinks() {
|
||||
String filePath = this.config.getPidLinks();
|
||||
logger.info("Loaded also config file: " + filePath);
|
||||
InputStream is = null;
|
||||
byte[] bytes = this.storageFileService.getPidLinksFile();
|
||||
try {
|
||||
is = getStreamFromPath(filePath);
|
||||
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
this.pidLinks = mapper.readValue(is, PidLinks.class);
|
||||
} catch (IOException | NullPointerException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) is.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Warning: Could not close a stream after reading from file: " + filePath, e);
|
||||
}
|
||||
this.pidLinks = mapper.readValue(new ByteArrayInputStream(bytes), PidLinks.class);
|
||||
} catch (Exception ex) {
|
||||
logger.error("Cannot find resource", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void setKeyToSourceMap() {
|
||||
String filePath = this.config.getExternalUrls();
|
||||
logger.info("Loaded also config file: " + filePath);
|
||||
Document doc = getXmlDocumentFromFilePath(filePath);
|
||||
if (doc == null) {
|
||||
this.keyToSourceMap = null;
|
||||
return;
|
||||
}
|
||||
String xpathExpression = "//key";
|
||||
Map<String, String> keysToSourceMap = new HashMap<>();
|
||||
List<String> keys = getXmlValuesFromXPath(doc, xpathExpression);
|
||||
keys = keys.stream().distinct().collect(Collectors.toList());
|
||||
for (String key : keys) {
|
||||
String sourceExpression = String.format("//urlConfig[key='%s']/label", key);
|
||||
List<String> sources = getXmlValuesFromXPath(doc, sourceExpression);
|
||||
if (sources.size() != 0) {
|
||||
keysToSourceMap.put(key, sources.get(0));
|
||||
byte[] bytes = this.storageFileService.getExternalUrlsFile();
|
||||
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
this.pidLinks = mapper.readValue(new ByteArrayInputStream(bytes), PidLinks.class);
|
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
Document doc = documentBuilder.parse(new ByteArrayInputStream(bytes));
|
||||
if (doc == null) {
|
||||
this.keyToSourceMap = null;
|
||||
return;
|
||||
}
|
||||
String xpathExpression = "//key";
|
||||
Map<String, String> keysToSourceMap = new HashMap<>();
|
||||
List<String> keys = getXmlValuesFromXPath(doc, xpathExpression);
|
||||
keys = keys.stream().distinct().collect(Collectors.toList());
|
||||
for (String key : keys) {
|
||||
String sourceExpression = String.format("//urlConfig[key='%s']/label", key);
|
||||
List<String> sources = getXmlValuesFromXPath(doc, sourceExpression);
|
||||
if (sources.size() != 0) {
|
||||
keysToSourceMap.put(key, sources.get(0));
|
||||
}
|
||||
}
|
||||
this.keyToSourceMap = keysToSourceMap;
|
||||
|
||||
} catch (Exception ex) {
|
||||
logger.error("Cannot find resource", ex);
|
||||
}
|
||||
this.keyToSourceMap = keysToSourceMap;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ spring:
|
|||
optional:classpath:config/actuator.yml[.yml], optional:classpath:config/actuator-${spring.profiles.active}.yml[.yml], optional:file:../config/actuator-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/email.yml[.yml], optional:classpath:config/email-${spring.profiles.active}.yml[.yml], optional:file:../config/email-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/elasticsearch.yml[.yml], optional:classpath:config/elasticsearch-${spring.profiles.active}.yml[.yml], optional:file:../config/elasticsearch-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/file-path.yml[.yml], optional:classpath:config/file-path-${spring.profiles.active}.yml[.yml], optional:file:../config/file-path-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/idpclaims.yml[.yml], optional:classpath:config/idpclaims-${spring.profiles.active}.yml[.yml], optional:file:../config/idpclaims-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/external.yml[.yml], optional:classpath:config/external-${spring.profiles.active}.yml[.yml], optional:file:../config/external-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/cors.yml[.yml], optional:classpath:config/cors-${spring.profiles.active}.yml[.yml], optional:file:../config/cors-${spring.profiles.active}.yml[.yml],
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
file-paths:
|
||||
externalUrls: externalUrls/ExternalUrls.xml
|
||||
semantics: Semantics.json
|
||||
h2020template: documents/h2020.docx
|
||||
h2020DatasetTemplate: documents/h2020_dataset.docx
|
||||
pidLinks: pidLinks.json
|
||||
|
||||
userguide:
|
||||
path: dmp-backend/web/src/main/resources/material/user-guide
|
||||
|
||||
about:
|
||||
path: dmp-backend/web/src/main/resources/material/about
|
||||
|
||||
termsofservice:
|
||||
path: dmp-backend/web/src/main/resources/material/terms-of-service
|
||||
|
||||
glossary:
|
||||
path: dmp-backend/web/src/main/resources/material/glossary
|
||||
|
||||
faq:
|
||||
path: dmp-backend/web/src/main/resources/material/faq
|
||||
|
||||
temp:
|
||||
temp: ${TEMP_STORAGE:}
|
||||
|
||||
file:
|
||||
storage: ${FILE_STORAGE:}
|
||||
|
||||
language:
|
||||
path: dmp-frontend/src/assets/i18n/
|
|
@ -1,7 +1,21 @@
|
|||
storage:
|
||||
service:
|
||||
defaultLanguage: en
|
||||
storages:
|
||||
- type: Temp
|
||||
basePath: ./storage/temp
|
||||
- type: Main
|
||||
basePath: ./storage/main
|
||||
static-files:
|
||||
externalUrls: externalUrls/ExternalUrls.xml
|
||||
semantics: Semantics.json
|
||||
h2020Template: documents/h2020.docx
|
||||
h2020DescriptionTemplate: documents/h2020_dataset.docx
|
||||
pidLinks: pidLinks.json
|
||||
material-files:
|
||||
userGuide: dmp-backend/web/src/main/resources/material/user-guide/
|
||||
about: dmp-backend/web/src/main/resources/material/about/
|
||||
termsOfService: dmp-backend/web/src/main/resources/material/terms-of-service/
|
||||
glossary: dmp-backend/web/src/main/resources/material/glossary/
|
||||
faq: dmp-backend/web/src/main/resources/material/faq/
|
||||
language: dmp-frontend/src/assets/i18n/
|
||||
|
|
|
@ -4,3 +4,11 @@ storage:
|
|||
intervalSeconds: 600
|
||||
service:
|
||||
tempStoreLifetimeSeconds: 7200
|
||||
material-files:
|
||||
localizedNameLanguageKey: "{lang}"
|
||||
userGuideNamePattern: "UserGuide_{lang}.html"
|
||||
aboutNamePattern: "About_{lang}.html"
|
||||
termsOfServiceNamePattern: "TermsOfService_{lang}.html"
|
||||
glossaryNamePattern: "Glossary_{lang}.html"
|
||||
faqNamePattern: "Faq_{lang}.html"
|
||||
languageNamePattern: "{lang}.json"
|
||||
|
|
Loading…
Reference in New Issue