closes #249
This commit is contained in:
@@ -1,4 +1,11 @@
|
|||||||
- support XR 2.1 (UN/CEFACT)
|
2.3.0
|
||||||
|
=======
|
||||||
|
2021-10-04
|
||||||
|
|
||||||
|
- support validating XR 2.1 (UN/CEFACT)
|
||||||
|
- invalid output PDF for input with incomplete CIDsets #249
|
||||||
|
To prevent regressions it is recommended to re-validate your PDF output after upgrading to 2.3.0
|
||||||
|
- Upgrade to PDFBox 2.0.24
|
||||||
|
|
||||||
2.2.1
|
2.2.1
|
||||||
=======
|
=======
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.pdfbox</groupId>
|
<groupId>org.apache.pdfbox</groupId>
|
||||||
<artifactId>preflight</artifactId>
|
<artifactId>preflight</artifactId>
|
||||||
<version>2.0.23</version>
|
<version>2.0.24</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.pdfbox</groupId>
|
<groupId>org.apache.pdfbox</groupId>
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecifica
|
|||||||
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
|
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
|
||||||
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo;
|
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo;
|
||||||
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
|
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDCIDFontType2;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
|
import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
|
||||||
import org.apache.pdfbox.preflight.utils.ByteArrayDataSource;
|
import org.apache.pdfbox.preflight.utils.ByteArrayDataSource;
|
||||||
import org.apache.xmpbox.XMPMetadata;
|
import org.apache.xmpbox.XMPMetadata;
|
||||||
@@ -46,11 +50,7 @@ import javax.activation.DataSource;
|
|||||||
import javax.activation.FileDataSource;
|
import javax.activation.FileDataSource;
|
||||||
import javax.xml.transform.TransformerException;
|
import javax.xml.transform.TransformerException;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporter, IExporter, Closeable {
|
public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporter, IExporter, Closeable {
|
||||||
private boolean isFacturX = true;
|
private boolean isFacturX = true;
|
||||||
@@ -92,13 +92,19 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
|||||||
*/
|
*/
|
||||||
protected String creatorTool = "mustangproject";
|
protected String creatorTool = "mustangproject";
|
||||||
|
|
||||||
/** @deprecated author is never set yet */
|
/**
|
||||||
|
* @deprecated author is never set yet
|
||||||
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected String author;
|
protected String author;
|
||||||
/** @deprecated title is never set yet */
|
/**
|
||||||
|
* @deprecated title is never set yet
|
||||||
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected String title;
|
protected String title;
|
||||||
/** @deprecated subject is never set yet */
|
/**
|
||||||
|
* @deprecated subject is never set yet
|
||||||
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected String subject;
|
protected String subject;
|
||||||
|
|
||||||
@@ -129,17 +135,17 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ZUGFeRDExporterFromA3 setProfile(Profile p) {
|
public ZUGFeRDExporterFromA3 setProfile(Profile p) {
|
||||||
this.profile=p;
|
this.profile = p;
|
||||||
if (xmlProvider!=null) {
|
if (xmlProvider != null) {
|
||||||
xmlProvider.setProfile(p);
|
xmlProvider.setProfile(p);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZUGFeRDExporterFromA3 setProfile(String profilename) {
|
public ZUGFeRDExporterFromA3 setProfile(String profilename) {
|
||||||
this.profile=Profiles.getByName(profilename);
|
this.profile = Profiles.getByName(profilename);
|
||||||
|
|
||||||
if (xmlProvider!=null) {
|
if (xmlProvider != null) {
|
||||||
xmlProvider.setProfile(this.profile);
|
xmlProvider.setProfile(this.profile);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@@ -208,8 +214,8 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
|||||||
* Factur-X is now set by default since ZF 2.1, you have to disable it if you dont wont it
|
* Factur-X is now set by default since ZF 2.1, you have to disable it if you dont wont it
|
||||||
* Generate ZF2.1 files with filename factur-x.xml
|
* Generate ZF2.1 files with filename factur-x.xml
|
||||||
*
|
*
|
||||||
* @deprecated
|
|
||||||
* @return this (fluent setter)
|
* @return this (fluent setter)
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public ZUGFeRDExporterFromA3 setFacturX() {
|
public ZUGFeRDExporterFromA3 setFacturX() {
|
||||||
isFacturX = true;
|
isFacturX = true;
|
||||||
@@ -244,8 +250,9 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
|||||||
public void attachFile(FileAttachment file) {
|
public void attachFile(FileAttachment file) {
|
||||||
fileAttachments.add(file);
|
fileAttachments.add(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void attachFile(String filename, byte[] data, String mimetype, String relation) {
|
public void attachFile(String filename, byte[] data, String mimetype, String relation) {
|
||||||
FileAttachment fa=new FileAttachment(filename, mimetype, relation, data);
|
FileAttachment fa = new FileAttachment(filename, mimetype, relation, data);
|
||||||
fileAttachments.add(fa);
|
fileAttachments.add(fa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,12 +481,48 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
|||||||
metadata.addSchema(pdfaex);
|
metadata.addSchema(pdfaex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeCidSet(PDDocumentCatalog catalog, PDDocument doc) {
|
||||||
|
|
||||||
|
// https://github.com/ZUGFeRD/mustangproject/issues/249
|
||||||
|
|
||||||
|
COSName cidSet = COSName.getPDFName("CIDSet");
|
||||||
|
|
||||||
|
// iterate over all pdf pages
|
||||||
|
|
||||||
|
for (Object object : doc.getPages()) {
|
||||||
|
if (object instanceof PDPage) {
|
||||||
|
|
||||||
|
PDPage page = (PDPage) object;
|
||||||
|
PDResources res = page.getResources();
|
||||||
|
for (COSName fontName : res.getFontNames()) {
|
||||||
|
try {
|
||||||
|
PDFont pdFont = res.getFont(fontName);
|
||||||
|
if (pdFont instanceof PDType0Font) {
|
||||||
|
PDType0Font typedFont = (PDType0Font) pdFont;
|
||||||
|
|
||||||
|
if (typedFont.getDescendantFont() instanceof PDCIDFontType2) {
|
||||||
|
PDCIDFontType2 f = (PDCIDFontType2) typedFont.getDescendantFont();
|
||||||
|
PDFontDescriptor fontDescriptor = pdFont.getFontDescriptor();
|
||||||
|
|
||||||
|
fontDescriptor.getCOSObject().removeItem(cidSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// do stuff with the font
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void prepareDocument() throws IOException {
|
protected void prepareDocument() throws IOException {
|
||||||
|
|
||||||
PDDocumentCatalog cat = doc.getDocumentCatalog();
|
PDDocumentCatalog cat = doc.getDocumentCatalog();
|
||||||
metadata = new PDMetadata(doc);
|
metadata = new PDMetadata(doc);
|
||||||
cat.setMetadata(metadata);
|
cat.setMetadata(metadata);
|
||||||
|
|
||||||
|
removeCidSet(cat, doc);
|
||||||
xmp = getXmpMetadata();
|
xmp = getXmpMetadata();
|
||||||
writeAdobePDFSchema(xmp);
|
writeAdobePDFSchema(xmp);
|
||||||
writePDFAIdentificationSchema(xmp);
|
writePDFAIdentificationSchema(xmp);
|
||||||
@@ -521,7 +564,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
|||||||
return prepare();
|
return prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IExporter prepare() throws IOException{
|
public IExporter prepare() throws IOException {
|
||||||
prepareDocument();
|
prepareDocument();
|
||||||
xmlProvider.generateXML(trans);
|
xmlProvider.generateXML(trans);
|
||||||
String filename = getFilenameForVersion(ZFVersion, xmlProvider.getProfile());
|
String filename = getFilenameForVersion(ZFVersion, xmlProvider.getProfile());
|
||||||
@@ -529,7 +572,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
|||||||
"Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)",
|
"Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)",
|
||||||
"text/xml", xmlProvider.getXML());
|
"text/xml", xmlProvider.getXML());
|
||||||
|
|
||||||
for (FileAttachment attachment: fileAttachments) {
|
for (FileAttachment attachment : fileAttachments) {
|
||||||
PDFAttachGenericFile(doc, attachment.getFilename(), attachment.getRelation(), attachment.getDescription(), attachment.getMimetype(), attachment.getData());
|
PDFAttachGenericFile(doc, attachment.getFilename(), attachment.getRelation(), attachment.getDescription(), attachment.getMimetype(), attachment.getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,10 +782,11 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
|
|||||||
|
|
||||||
protected void setXMLProvider(IXMLProvider p) {
|
protected void setXMLProvider(IXMLProvider p) {
|
||||||
this.xmlProvider = p;
|
this.xmlProvider = p;
|
||||||
if (profile!=null) {
|
if (profile != null) {
|
||||||
xmlProvider.setProfile(profile);
|
xmlProvider.setProfile(profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ZUGFeRDExporterFromA3 setZUGFeRDVersion(int version) {
|
public ZUGFeRDExporterFromA3 setZUGFeRDVersion(int version) {
|
||||||
this.ZFVersion = version;
|
this.ZFVersion = version;
|
||||||
|
|||||||
@@ -272,7 +272,8 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//ApplicableHeaderTradeSettlement//InvoiceCurrencyCode");
|
return extractString("//ApplicableHeaderTradeSettlement//InvoiceCurrencyCode");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,7 +307,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//BuyerOrderReferencedDocument//IssuerAssignedID");
|
return extractString("//BuyerOrderReferencedDocument//IssuerAssignedID");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,7 +330,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//ExchangedDocument//IssueDateTime//DateTimeString");
|
return extractString("//ExchangedDocument//IssueDateTime//DateTimeString");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,7 +346,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//TaxBasisTotalAmount");
|
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//TaxBasisTotalAmount");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,7 +362,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//TaxTotalAmount");
|
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//TaxTotalAmount");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,7 +378,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//RoundingAmount");
|
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//RoundingAmount");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,7 +394,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//TotalPrepaidAmount");
|
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//TotalPrepaidAmount");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -431,7 +432,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//ExchangedDocument//IncludedNote");
|
return extractString("//ExchangedDocument//IncludedNote");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -456,7 +457,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//LineTotalAmount");
|
return extractString("//SpecifiedTradeSettlementHeaderMonetarySummation//LineTotalAmount");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -479,7 +480,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//ActualDeliverySupplyChainEvent//OccurrenceDateTime//DateTimeString");
|
return extractString("//ActualDeliverySupplyChainEvent//OccurrenceDateTime//DateTimeString");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -495,7 +496,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//ExchangedDocument//ID");
|
return extractString("//ExchangedDocument//ID");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,7 +514,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//ExchangedDocument/TypeCode");
|
return extractString("//ExchangedDocument/TypeCode");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -530,7 +531,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//ApplicableHeaderTradeAgreement/BuyerReference");
|
return extractString("//ApplicableHeaderTradeAgreement/BuyerReference");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -718,7 +719,7 @@ public class ZUGFeRDImporter {
|
|||||||
nl = getNodeListByPath("//CrossIndustryInvoice//SupplyChainTradeTransaction//ApplicableHeaderTradeAgreement//BuyerTradeParty//PostalTradeAddress");
|
nl = getNodeListByPath("//CrossIndustryInvoice//SupplyChainTradeTransaction//ApplicableHeaderTradeAgreement//BuyerTradeParty//PostalTradeAddress");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,7 +742,7 @@ public class ZUGFeRDImporter {
|
|||||||
nl = getNodeListByPath("//CrossIndustryInvoice//SupplyChainTradeTransaction//ApplicableHeaderTradeAgreement//SellerTradeParty//PostalTradeAddress");
|
nl = getNodeListByPath("//CrossIndustryInvoice//SupplyChainTradeTransaction//ApplicableHeaderTradeAgreement//SellerTradeParty//PostalTradeAddress");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -940,7 +941,7 @@ public class ZUGFeRDImporter {
|
|||||||
nl = getNodeListByPath("//CrossIndustryInvoice//SupplyChainTradeTransaction//IncludedSupplyChainTradeLineItem");
|
nl = getNodeListByPath("//CrossIndustryInvoice//SupplyChainTradeTransaction//IncludedSupplyChainTradeLineItem");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < nl.getLength(); i++) {
|
for (int i = 0; i < nl.getLength(); i++) {
|
||||||
@@ -985,7 +986,7 @@ public class ZUGFeRDImporter {
|
|||||||
final XPathExpression xpr = xPath.compile(s);
|
final XPathExpression xpr = xPath.compile(s);
|
||||||
return (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
return (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user