Skip to content
Snippets Groups Projects
Commit b2893fe7 authored by OZGCloud's avatar OZGCloud
Browse files

Merge branch 'master' into OZG-4179-Suche-Autocomplete

parents a2955ee5 05ddd3d8
No related branches found
No related tags found
No related merge requests found
Showing
with 278 additions and 165 deletions
......@@ -231,7 +231,7 @@ class WiedervorlageCommandITCase {
@DisplayName("for 41 character in Betreff")
@Test
void createCommandWithLongBetreff() throws Exception {
var content = buildContentWithBetreff(RandomStringUtils.random(41));
var content = buildContentWithBetreff(RandomStringUtils.randomAlphanumeric(41));
doRequest(content).andExpect(status().isUnprocessableEntity());
}
......
......@@ -93,7 +93,7 @@
</executions>
<configuration>
<sources>
<source>src/main/resources/xdomea_3-0-0_XML-Schemata</source>
<source>src/main/resources/ozgcloud_XML-Schemata</source>
</sources>
<packageName>de.xoev.xdomea</packageName>
</configuration>
......
package de.ozgcloud.alfa.export;
package de.ozgcloud.alfa.common;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Optional;
......@@ -12,19 +14,36 @@ import javax.xml.datatype.XMLGregorianCalendar;
import org.springframework.stereotype.Component;
import de.ozgcloud.common.errorhandling.TechnicalException;
import lombok.extern.log4j.Log4j2;
@Component
class DateConverter {
@Log4j2
public class DateConverter {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy", Locale.GERMAN);
public Optional<XMLGregorianCalendar> convertGermanFormatToISO(String dateStr) {
try {
return Optional.of(DatatypeFactory.newInstance().newXMLGregorianCalendar(LocalDate.parse(dateStr, DATE_TIME_FORMATTER).toString()));
} catch (DatatypeConfigurationException e) {
throw new TechnicalException(String.format("Error converting date %s to ISO.", dateStr), e);
return Optional.of(createDatatypeFactory().newXMLGregorianCalendar(LocalDate.parse(dateStr, DATE_TIME_FORMATTER).toString()));
} catch (Exception e) {
LOG.error("Date '{}' cannot be converted to ISO format.", dateStr, e);
return Optional.empty();
}
}
public XMLGregorianCalendar toXmlGregorianCalendar(ZonedDateTime date) {
try {
return createDatatypeFactory().newXMLGregorianCalendar(GregorianCalendar.from(date));
} catch (Exception e) {
throw new TechnicalException("Date '%s' cannot be converted to ISO format.".formatted(date), e);
}
}
DatatypeFactory createDatatypeFactory() {
try {
return DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) {
throw new TechnicalException("Error creating datatype factory.", e);
}
}
}
package de.ozgcloud.alfa.export;
package de.ozgcloud.alfa.common;
enum DatentypCode {
public enum DatentypCode {
STRING("038"), DATE("005");
private String code;
......
package de.ozgcloud.alfa.export;
package de.ozgcloud.alfa.common;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......@@ -6,7 +6,7 @@ import org.springframework.stereotype.Component;
import de.ozgcloud.alfa.common.file.OzgFile;
@Component
class ExportFilenameGenerator {
public class ExportFilenameGenerator {
@Autowired
private UUIDConverter uuidConverter;
......
package de.ozgcloud.alfa.export;
package de.ozgcloud.alfa.common;
import java.util.regex.Pattern;
......@@ -6,7 +6,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@Component
class UUIDConverter {
public class UUIDConverter {
private static final Pattern UUID_SPLIT_PATTERN = Pattern.compile(
"^([a-fA-F0-9]{8})([a-fA-F0-9]{4})([a-fA-F0-9]{4})([a-fA-F0-9]{4})([a-fA-F0-9]+)$");
......
package de.ozgcloud.alfa.export;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
import de.xoev.xdomea.AbgabeAbgabe0401;
@Component
class AbgabeAbgabe0401Creator {
@Autowired
private SchriftgutobjektCreator schriftgutobjektCreator;
@Autowired
private AnwendungsspezifischeErweiterungTypeCreator anwendungsspezifischeErweiterungTypeCreator;
public AbgabeAbgabe0401 create(VorgangWithEingang vorgang) {
var erweiterung = anwendungsspezifischeErweiterungTypeCreator.create(vorgang);
var abgabe = new AbgabeAbgabe0401();
abgabe.getSchriftgutobjekt().add(schriftgutobjektCreator.create(vorgang, erweiterung));
return abgabe;
}
}
package de.ozgcloud.alfa.export;
import java.util.stream.Stream;
import de.xoev.xdomea.AbgabeAbgabe0401;
import de.xoev.xdomea.DokumentType;
import de.xoev.xdomea.NkAbgabeType;
import de.xoev.xdomea.VorgangType;
class AbgabeCreator {
private AbgabeAbgabe0401 abgabeType;
private AbgabeCreator() {
abgabeType = new AbgabeAbgabe0401();
}
public AbgabeCreator withKopf(NkAbgabeType kopfType) {
abgabeType.setKopf(kopfType);
return this;
}
public AbgabeCreator withVorgang(VorgangType vorgangType) {
getSchriftgutobjekt().setVorgang(vorgangType);
return this;
}
public AbgabeCreator withDokumentTypes(Stream<DokumentType> dokumentTypes) {
var vorgang = getVorgang();
if (vorgang == null) {
throw new IllegalStateException("Cannot set documentTypes because Vorgang is null");
}
dokumentTypes.forEach(vorgang.getDokument()::add);
return this;
}
public AbgabeAbgabe0401 create() {
return abgabeType;
}
private AbgabeAbgabe0401.Schriftgutobjekt getSchriftgutobjekt() {
if (abgabeType.getSchriftgutobjekt().isEmpty()) {
abgabeType.getSchriftgutobjekt().add(new AbgabeAbgabe0401.Schriftgutobjekt());
}
return abgabeType.getSchriftgutobjekt().get(0);
}
private VorgangType getVorgang() {
return getSchriftgutobjekt().getVorgang();
}
static AbgabeCreator createAbgabeCreator() {
return new AbgabeCreator();
}
}
......@@ -3,15 +3,15 @@ package de.ozgcloud.alfa.export;
import java.util.HashMap;
import java.util.Map;
import jakarta.xml.bind.Marshaller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import jakarta.xml.bind.Marshaller;
@Configuration
class XDomeaXmlMarshallerConfiguration {
class ExportConfiguration {
static final boolean JAXB_FORMATTED_OUTPUT = true;
static final String PROPERTY_NAMESPACE_PREFIX_MAPPER = "org.glassfish.jaxb.namespacePrefixMapper";
......@@ -21,7 +21,7 @@ class XDomeaXmlMarshallerConfiguration {
private XDomeaNamespacePrefixMapper prefixMapper;
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller() {
var marshaller = new Jaxb2Marshaller();
marshaller.setContextPaths(CONTEXT_PATH);
marshaller.setMarshallerProperties(createMarshallerProperties());
......
......@@ -3,7 +3,7 @@ package de.ozgcloud.alfa.export;
import java.util.stream.Stream;
import de.ozgcloud.alfa.common.file.OzgFile;
import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
import de.xoev.xdomea.AbgabeAbgabe0401;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
......@@ -14,9 +14,8 @@ import lombok.Getter;
@Getter
class ExportData {
private VorgangWithEingang vorgang;
private String exportFilename;
private String xmlFileContent;
private AbgabeAbgabe0401 abgabe;
private Stream<OzgFile> exportFiles;
}
......@@ -13,30 +13,28 @@ import java.util.zip.ZipOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.ozgcloud.alfa.common.ExportFilenameGenerator;
import de.ozgcloud.alfa.common.file.OzgFile;
import de.ozgcloud.alfa.vorgang.VorgangController;
import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
import de.ozgcloud.alfa.file.ExportFileService;
import de.ozgcloud.alfa.vorgang.ExportVorgangService;
import de.ozgcloud.common.binaryfile.TempFileUtils;
import de.ozgcloud.common.errorhandling.TechnicalException;
import de.xoev.xdomea.AbgabeAbgabe0401;
@Service
class XDomeaService {
class ExportService {
static final String EXPORT_FILENAME_SUFFIX = "_Abgabe.Abgabe.0401.xml";
private static final String EXPORT_FILENAME_TEMPLATE = "%s" + EXPORT_FILENAME_SUFFIX;
@Autowired
private XDomeaXmlMarshaller xDomeaXmlMarshaller;
@Autowired
private VorgangController vorgangController;
private ExportFileService exportFileService;
@Autowired
private AbgabeAbgabe0401Creator abgabeCreator;
@Autowired
private ExportFileService exportFileService;
private ExportVorgangService exportVorgangService;
@Autowired
private ExportFilenameGenerator exportFilenameGenerator;
......@@ -48,25 +46,28 @@ class XDomeaService {
}
ExportData collectExportData(String vorgangId, String filenameId) {
var vorgang = vorgangController.getVorgang(vorgangId);
var xmlContent = createXmlContent(vorgang);
var filename = buildXmlFilename(filenameId);
var ozgFiles = exportFileService.getAllPdfs(vorgang.getEingang().getId());
return ExportData.builder().vorgang(vorgang).xmlFileContent(xmlContent).exportFilename(filename).exportFiles(ozgFiles).build();
var vorgangWithEingang = exportVorgangService.getVorgang(vorgangId);
var ozgFiles = exportFileService.getAllPdfs(vorgangWithEingang.getEingang().getId());
var abgabe = AbgabeCreator.createAbgabeCreator()
.withKopf(exportVorgangService.createKopf(vorgangWithEingang))
.withVorgang(exportVorgangService.createVorgangType(vorgangWithEingang))
.withDokumentTypes(exportFileService.createDokumentTypes(ozgFiles, vorgangWithEingang.getEingang().getHeader().getFormEngineName()))
.create();
return ExportData.builder().abgabe(abgabe).exportFilename(buildXmlFilename(filenameId)).exportFiles(ozgFiles).build();
}
String buildXmlFilename(String filenameId) {
return String.format(EXPORT_FILENAME_TEMPLATE, filenameId);
}
String createXmlContent(VorgangWithEingang vorgang) {
return xDomeaXmlMarshaller.marshal(abgabeCreator.create(vorgang));
String createXmlContent(AbgabeAbgabe0401 abgabe) {
return xDomeaXmlMarshaller.marshal(abgabe);
}
File createZipFile(ExportData exportData) {
var file = TempFileUtils.createTmpFile().toFile();
try (var zipOutputStream = new ZipOutputStream(new FileOutputStream(file))) {
putZipEntry(exportData.getExportFilename(), exportData.getXmlFileContent(), zipOutputStream);
putZipEntry(exportData.getExportFilename(), createXmlContent(exportData.getAbgabe()), zipOutputStream);
putFilesIntoZip(exportData.getExportFiles(), zipOutputStream);
return file;
} catch (Exception e) {
......@@ -81,17 +82,19 @@ class XDomeaService {
zipOutputStream.closeEntry();
}
private void putFilesIntoZip(Stream<OzgFile> ozgFiles, ZipOutputStream zipOutputStream) throws IOException {
for (var ozgFile : ozgFiles.toList()) {
putOzgFileIntoZip(ozgFile, zipOutputStream);
}
private void putFilesIntoZip(Stream<OzgFile> ozgFiles, ZipOutputStream zipOutputStream) {
ozgFiles.forEach(ozgFile -> putOzgFileIntoZip(ozgFile, zipOutputStream));
}
void putOzgFileIntoZip(OzgFile ozgFile, ZipOutputStream zipOutputStream) throws IOException {
void putOzgFileIntoZip(OzgFile ozgFile, ZipOutputStream zipOutputStream) {
try {
var entry = new ZipEntry(exportFilenameGenerator.generateExportFilename(ozgFile));
zipOutputStream.putNextEntry(entry);
exportFileService.writeOzgFile(ozgFile.getId(), zipOutputStream);
zipOutputStream.closeEntry();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
void writeZipFileContent(File file, OutputStream outputStream) {
......
......@@ -21,7 +21,7 @@ public class ExportVorgangController {
private static final String EXPORT_FILENAME_TEMPLATE = "%s_Abgabe.Abgabe.0401.xdomea";
@Autowired
private XDomeaService xDomeaService;
private ExportService xDomeaService;
@GetMapping(value = "{vorgangId}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<StreamingResponseBody> exportToXdomea(@PathVariable String vorgangId) {
......
package de.ozgcloud.alfa.export;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import de.ozgcloud.alfa.common.file.OzgFile;
import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
import de.xoev.xdomea.FormatType;
@Component
class FormatTypeCreator {
@Autowired
private PrimaerdokumentTypeCreator primaerdokumentTypeCreator;
public FormatType create(VorgangWithEingang vorgang, OzgFile ozgFile) {
var format = new FormatType();
format.setPrimaerdokument(primaerdokumentTypeCreator.create(vorgang, ozgFile));
format.setName(DateiformatCode.PDF.createDateiformatCodeType());
format.setSonstigerName(StringUtils.EMPTY);
format.setVersion(StringUtils.EMPTY);
return format;
}
}
package de.ozgcloud.alfa.export;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import de.ozgcloud.alfa.common.file.OzgFile;
import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
import de.xoev.xdomea.PrimaerdokumentType;
@Component
class PrimaerdokumentTypeCreator {
@Autowired
private ExportFilenameGenerator exportFilenameGenerator;
public PrimaerdokumentType create(VorgangWithEingang vorgang, OzgFile ozgFile) {
var primaerdokument = new PrimaerdokumentType();
primaerdokument.setDateiname(exportFilenameGenerator.generateExportFilename(ozgFile));
primaerdokument.setDateinameOriginal(ozgFile.getName());
primaerdokument.setErsteller(vorgang.getEingang().getHeader().getFormEngineName());
return primaerdokument;
}
}
package de.ozgcloud.alfa.export;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
import de.xoev.xdomea.AbgabeAbgabe0401.Schriftgutobjekt;
import de.xoev.xdomea.AnwendungsspezifischeErweiterungType;
@Component
class SchriftgutobjektCreator {
@Autowired
private VorgangTypeCreator vorgangTypeCreator;
public Schriftgutobjekt create(VorgangWithEingang vorgang, AnwendungsspezifischeErweiterungType erweiterung) {
var schritftgutobjekt = new Schriftgutobjekt();
schritftgutobjekt.setVorgang(vorgangTypeCreator.create(vorgang, erweiterung));
return schritftgutobjekt;
}
}
package de.ozgcloud.alfa.export;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import de.ozgcloud.alfa.common.file.OzgFile;
import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
import de.xoev.xdomea.VersionType;
@Component
class VersionTypeCreator {
static final String NUMMER = "1";
@Autowired
private FormatTypeCreator formatTypeCreator;
public VersionType create(VorgangWithEingang vorgang, OzgFile ozgFile) {
var version = new VersionType();
version.setNummer(NUMMER);
version.getFormat().add(formatTypeCreator.create(vorgang, ozgFile));
return version;
}
}
package de.ozgcloud.alfa.export;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Configuration
@ConfigurationProperties("ozgcloud.xdomea")
public class XDomeaProperties {
/**
* XDomea Behoerdenschluessel.
*/
private String behoerdenschluessel;
}
package de.ozgcloud.alfa.export;
package de.ozgcloud.alfa.file;
import de.xoev.xdomea.DateiformatCodeType;
......
package de.ozgcloud.alfa.export;
package de.ozgcloud.alfa.file;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import de.ozgcloud.alfa.common.ExportFilenameGenerator;
import de.ozgcloud.alfa.common.UUIDConverter;
import de.ozgcloud.alfa.common.file.OzgFile;
import de.ozgcloud.alfa.vorgang.Eingang;
import de.ozgcloud.alfa.vorgang.VorgangWithEingang;
import de.xoev.xdomea.AllgemeineMetadatenType;
import de.xoev.xdomea.DokumentType;
import de.xoev.xdomea.FormatType;
import de.xoev.xdomea.IdentifikationObjektType;
import de.xoev.xdomea.MediumCodeType;
import de.xoev.xdomea.PrimaerdokumentType;
import de.xoev.xdomea.VersionType;
@Component
class DokumentTypeCreator {
static final String VERSION_NUMMER = "1";
static final String ALLGEMEINE_METADATEN_MEDIUM_CODE = "001";
@Autowired
private ExportFileService exportFileService;
@Autowired
private UUIDConverter uuidConverter;
@Autowired
private VersionTypeCreator versionTypeCreator;
public Stream<DokumentType> create(VorgangWithEingang vorgang) {
var ozgFiles = Optional.ofNullable(vorgang.getEingang()).map(Eingang::getId).map(exportFileService::getAllPdfs).map(Stream::toList)
.orElse(Collections.emptyList());
List<DokumentType> dokumenten = new ArrayList<>(ozgFiles.size());
for (var i = 0; i < ozgFiles.size(); i++) {
dokumenten.add(createDokumentType(i + 1, vorgang, ozgFiles.get(i)));
}
return dokumenten.stream();
private ExportFilenameGenerator exportFilenameGenerator;
public Stream<DokumentType> create(Stream<OzgFile> ozgFiles, String formEngineName) {
final var ordinalNumberGenerator = new AtomicInteger(1);
return ozgFiles.map(ozgFile -> createDokumentType(new MappingData(ozgFile, ordinalNumberGenerator.getAndIncrement(), formEngineName)));
}
DokumentType createDokumentType(int dokumentOrdinalNumber, VorgangWithEingang vorgang, OzgFile ozgFile) {
var dokument = new DokumentType();
dokument.setIdentifikation(createIdentifikation(dokumentOrdinalNumber, ozgFile.getId().toString()));
dokument.setAllgemeineMetadaten(createAllgemeineMetadaten(ozgFile));
dokument.getVersion().add(versionTypeCreator.create(vorgang, ozgFile));
return dokument;
DokumentType createDokumentType(MappingData mappingData) {
var dokumentType = new DokumentType();
dokumentType.setIdentifikation(createIdentifikation(mappingData));
dokumentType.setAllgemeineMetadaten(createAllgemeineMetadaten(mappingData));
var versionType = createVersionType();
dokumentType.getVersion().add(versionType);
var formatType = createFormatType();
versionType.getFormat().add(formatType);
var primaerdokumentType = createPrimaerdokumentType(mappingData);
formatType.setPrimaerdokument(primaerdokumentType);
return dokumentType;
}
IdentifikationObjektType createIdentifikation(int dokumentOrdinalNumber, String fileId) {
IdentifikationObjektType createIdentifikation(MappingData mappingData) {
var identifikation = new IdentifikationObjektType();
identifikation.setID(uuidConverter.fromObjectId(fileId));
identifikation.setNummerImUebergeordnetenContainer(Long.valueOf(dokumentOrdinalNumber));
identifikation.setID(uuidConverter.fromObjectId(mappingData.ozgFile().getId().toString()));
identifikation.setNummerImUebergeordnetenContainer(Long.valueOf(mappingData.dokumentOrdinalNumber()));
return identifikation;
}
AllgemeineMetadatenType createAllgemeineMetadaten(OzgFile ozgFile) {
AllgemeineMetadatenType createAllgemeineMetadaten(MappingData mappingData) {
var allgemeineMetadaten = new AllgemeineMetadatenType();
allgemeineMetadaten.setBetreff(ozgFile.getName());
allgemeineMetadaten.setBetreff(mappingData.ozgFile().getName());
allgemeineMetadaten.setKennzeichen(StringUtils.EMPTY);
allgemeineMetadaten.setBemerkung(StringUtils.EMPTY);
allgemeineMetadaten.setMedium(createMediumCode());
......@@ -71,4 +73,28 @@ class DokumentTypeCreator {
mediumCode.setCode(ALLGEMEINE_METADATEN_MEDIUM_CODE);
return mediumCode;
}
private VersionType createVersionType() {
var versionType = new VersionType();
versionType.setNummer(VERSION_NUMMER);
return versionType;
}
private FormatType createFormatType() {
var formatType = new FormatType();
formatType.setName(DateiformatCode.PDF.createDateiformatCodeType());
formatType.setSonstigerName(StringUtils.EMPTY);
formatType.setVersion(StringUtils.EMPTY);
return formatType;
}
private PrimaerdokumentType createPrimaerdokumentType(MappingData mappingData) {
var primaerdokumentType = new PrimaerdokumentType();
primaerdokumentType.setDateiname(exportFilenameGenerator.generateExportFilename(mappingData.ozgFile()));
primaerdokumentType.setDateinameOriginal(mappingData.ozgFile().getName());
primaerdokumentType.setErsteller(mappingData.formEngineName());
return primaerdokumentType;
}
record MappingData (OzgFile ozgFile, int dokumentOrdinalNumber, String formEngineName) {}
}
package de.ozgcloud.alfa.export;
package de.ozgcloud.alfa.file;
import java.io.OutputStream;
import java.util.function.Predicate;
......@@ -12,9 +12,10 @@ import de.ozgcloud.alfa.common.binaryfile.BinaryFileService;
import de.ozgcloud.alfa.common.binaryfile.FileId;
import de.ozgcloud.alfa.common.file.OzgFile;
import de.ozgcloud.alfa.common.file.OzgFileService;
import de.xoev.xdomea.DokumentType;
@Service
class ExportFileService {
public class ExportFileService {
private static final Predicate<OzgFile> IS_PDF_FILE = file -> file.getContentType().equals(MediaType.APPLICATION_PDF_VALUE);
......@@ -24,12 +25,19 @@ class ExportFileService {
@Autowired
private BinaryFileService binaryFileService;
@Autowired
private DokumentTypeCreator dokumentTypeCreator;
public Stream<OzgFile> getAllPdfs(String eingangId) {
var representations = ozgFileService.getRepresentationsByEingang(eingangId);
var attachments = ozgFileService.getAttachmentsByEingang(eingangId);
return Stream.concat(representations, attachments).filter(IS_PDF_FILE);
}
public Stream<DokumentType> createDokumentTypes(Stream<OzgFile> ozgFileStream, String formEngineName) {
return dokumentTypeCreator.create(ozgFileStream, formEngineName);
}
public void writeOzgFile(FileId fileId, OutputStream outputStream) {
binaryFileService.writeFileContent(fileId, outputStream);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment