Make tab and page numbers declarative
This commit is contained in:
@ -16,9 +16,9 @@ doc.addSection({
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "Github is the best",
|
text: "\tGithub is the best",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -238,9 +238,9 @@ class DocumentCreator {
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: dateText,
|
text: `\t${dateText}`,
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,21 @@
|
|||||||
// Footnotes
|
// Footnotes
|
||||||
// Import from 'docx' rather than '../build' if you install from npm
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { Document, Packer, Paragraph, TextRun } from "../build";
|
import { Document, Packer, Paragraph, TextRun, FootnoteReferenceRun } from "../build";
|
||||||
|
|
||||||
const doc = new Document();
|
const doc = new Document();
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [new TextRun("Hello").referenceFootnote(1), new TextRun(" World!").referenceFootnote(2)],
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
children: ["Hello", new FootnoteReferenceRun(1)],
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
children: [" World!", new FootnoteReferenceRun(2)],
|
||||||
|
}),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
new Paragraph("Hello World").referenceFootnote(3),
|
new Paragraph("Hello World").referenceFootnote(3),
|
||||||
],
|
],
|
||||||
|
@ -15,9 +15,9 @@ doc.addSection({
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "Bar",
|
text: "\tBar",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Example how to display page numbers
|
// Example how to display page numbers
|
||||||
// Import from 'docx' rather than '../build' if you install from npm
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { AlignmentType, Document, Footer, Header, Packer, PageNumberFormat, Paragraph, TextRun } from "../build";
|
import { AlignmentType, Document, Footer, Header, Packer, PageBreak, PageNumber, PageNumberFormat, Paragraph, TextRun } from "../build";
|
||||||
|
|
||||||
const doc = new Document({});
|
const doc = new Document({});
|
||||||
|
|
||||||
@ -9,9 +9,17 @@ doc.addSection({
|
|||||||
headers: {
|
headers: {
|
||||||
default: new Header({
|
default: new Header({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph("Foo Bar corp. ")
|
new Paragraph({
|
||||||
.addRun(new TextRun("Page Number ").pageNumber())
|
children: [
|
||||||
.addRun(new TextRun(" to ").numberOfTotalPages()),
|
new TextRun("Foo Bar corp. "),
|
||||||
|
new TextRun({
|
||||||
|
children: ["Page Number ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
children: [" to ", PageNumber.TOTAL_PAGES],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -19,11 +27,17 @@ doc.addSection({
|
|||||||
default: new Footer({
|
default: new Footer({
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
text: "Foo Bar corp. ",
|
|
||||||
alignment: AlignmentType.CENTER,
|
alignment: AlignmentType.CENTER,
|
||||||
})
|
children: [
|
||||||
.addRun(new TextRun("Page Number: ").pageNumber())
|
new TextRun("Foo Bar corp. "),
|
||||||
.addRun(new TextRun(" to ").numberOfTotalPages()),
|
new TextRun({
|
||||||
|
children: ["Page Number: ", PageNumber.CURRENT],
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
children: [" to ", PageNumber.TOTAL_PAGES],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -32,11 +46,21 @@ doc.addSection({
|
|||||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
new Paragraph("Hello World 1").pageBreak(),
|
new Paragraph({
|
||||||
new Paragraph("Hello World 2").pageBreak(),
|
children: [new TextRun("Hello World 1"), new PageBreak()],
|
||||||
new Paragraph("Hello World 3").pageBreak(),
|
}),
|
||||||
new Paragraph("Hello World 4").pageBreak(),
|
new Paragraph({
|
||||||
new Paragraph("Hello World 5").pageBreak(),
|
children: [new TextRun("Hello World 2"), new PageBreak()],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hello World 3"), new PageBreak()],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hello World 4"), new PageBreak()],
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hello World 5"), new PageBreak()],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ doc.addSection({
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "Github is the best",
|
text: "\tGithub is the best",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -21,9 +21,9 @@ doc.addSection({
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "Github is the best",
|
text: "\tGithub is the best",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
|
@ -24,9 +24,9 @@
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new docx.TextRun({
|
new docx.TextRun({
|
||||||
text: "Github is the best",
|
text: "\tGithub is the best",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -50,9 +50,9 @@ doc.addSection({
|
|||||||
bold: true,
|
bold: true,
|
||||||
}),
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "Github is the best",
|
text: "\tGithub is the best",
|
||||||
bold: true,
|
bold: true,
|
||||||
}).tab(),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -12,7 +12,7 @@ Simply call the relevant methods on the paragraph listed below. Then just add a
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
children: [new TextRun("Hey everyone").bold(), new TextRun("11th November 1999").tab()],
|
children: [new TextRun("Hey everyone").bold(), new TextRun(\t"11th November 1999")],
|
||||||
tabStops: [
|
tabStops: [
|
||||||
{
|
{
|
||||||
type: TabStopType.RIGHT,
|
type: TabStopType.RIGHT,
|
||||||
@ -26,7 +26,7 @@ The example above will create a left aligned text, and a right aligned text on t
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
children: [new TextRun("Second tab stop here I come!").tab().tab()],
|
children: [new TextRun("Second tab stop here I come!")],
|
||||||
tabStops: [
|
tabStops: [
|
||||||
{
|
{
|
||||||
type: TabStopType.RIGHT,
|
type: TabStopType.RIGHT,
|
||||||
@ -46,7 +46,7 @@ You can add multiple tab stops of the same `type` too.
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const paragraph = new Paragraph({
|
const paragraph = new Paragraph({
|
||||||
children: [new TextRun("Multiple tab stops!").tab().tab()],
|
children: [new TextRun("Multiple tab stops!")],
|
||||||
tabStops: [
|
tabStops: [
|
||||||
{
|
{
|
||||||
type: TabStopType.RIGHT,
|
type: TabStopType.RIGHT,
|
||||||
|
@ -5,3 +5,4 @@ export * from "./picture-run";
|
|||||||
export * from "./run-fonts";
|
export * from "./run-fonts";
|
||||||
export * from "./sequential-identifier";
|
export * from "./sequential-identifier";
|
||||||
export * from "./underline";
|
export * from "./underline";
|
||||||
|
export * from "./tab";
|
||||||
|
@ -6,12 +6,6 @@ import { Text } from "./text";
|
|||||||
|
|
||||||
describe("Text", () => {
|
describe("Text", () => {
|
||||||
describe("#constructor", () => {
|
describe("#constructor", () => {
|
||||||
it("creates an empty text run if no text is given", () => {
|
|
||||||
const t = new Text("");
|
|
||||||
const f = new Formatter().format(t);
|
|
||||||
expect(f).to.deep.equal({ "w:t": { _attr: { "xml:space": "preserve" } } });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds the passed in text to the component", () => {
|
it("adds the passed in text to the component", () => {
|
||||||
const t = new Text(" this is\n text");
|
const t = new Text(" this is\n text");
|
||||||
const f = new Formatter().format(t);
|
const f = new Formatter().format(t);
|
||||||
|
@ -9,8 +9,7 @@ export class Text extends XmlComponent {
|
|||||||
constructor(text: string) {
|
constructor(text: string) {
|
||||||
super("w:t");
|
super("w:t");
|
||||||
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
|
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
|
||||||
if (text) {
|
|
||||||
this.root.push(text);
|
this.root.push(text);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
import { Formatter } from "export/formatter";
|
import { Formatter } from "export/formatter";
|
||||||
|
// import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||||
import { ShadingType } from "file/table";
|
import { ShadingType } from "file/table";
|
||||||
|
|
||||||
import { Run } from "./";
|
import { Run } from "./";
|
||||||
|
import { PageNumber } from "./run";
|
||||||
import { UnderlineType } from "./underline";
|
import { UnderlineType } from "./underline";
|
||||||
|
|
||||||
describe("Run", () => {
|
describe("Run", () => {
|
||||||
@ -197,17 +199,6 @@ describe("Run", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#tab()", () => {
|
|
||||||
it("it should add break to the run", () => {
|
|
||||||
const run = new Run({});
|
|
||||||
run.tab();
|
|
||||||
const tree = new Formatter().format(run);
|
|
||||||
expect(tree).to.deep.equal({
|
|
||||||
"w:r": [{ "w:tab": {} }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#font()", () => {
|
describe("#font()", () => {
|
||||||
it("should set the font as named", () => {
|
it("should set the font as named", () => {
|
||||||
const run = new Run({
|
const run = new Run({
|
||||||
@ -270,8 +261,10 @@ describe("Run", () => {
|
|||||||
|
|
||||||
describe("#numberOfTotalPages", () => {
|
describe("#numberOfTotalPages", () => {
|
||||||
it("should set the run to the RTL mode", () => {
|
it("should set the run to the RTL mode", () => {
|
||||||
const run = new Run({});
|
const run = new Run({
|
||||||
run.numberOfTotalPages();
|
children: [PageNumber.TOTAL_PAGES],
|
||||||
|
});
|
||||||
|
|
||||||
const tree = new Formatter().format(run);
|
const tree = new Formatter().format(run);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
@ -286,8 +279,10 @@ describe("Run", () => {
|
|||||||
|
|
||||||
describe("#numberOfTotalPagesSection", () => {
|
describe("#numberOfTotalPagesSection", () => {
|
||||||
it("should set the run to the RTL mode", () => {
|
it("should set the run to the RTL mode", () => {
|
||||||
const run = new Run({});
|
const run = new Run({
|
||||||
run.numberOfTotalPagesSection();
|
children: [PageNumber.TOTAL_PAGES_IN_SECTION],
|
||||||
|
});
|
||||||
|
|
||||||
const tree = new Formatter().format(run);
|
const tree = new Formatter().format(run);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
@ -302,8 +297,9 @@ describe("Run", () => {
|
|||||||
|
|
||||||
describe("#pageNumber", () => {
|
describe("#pageNumber", () => {
|
||||||
it("should set the run to the RTL mode", () => {
|
it("should set the run to the RTL mode", () => {
|
||||||
const run = new Run({});
|
const run = new Run({
|
||||||
run.pageNumber();
|
children: [PageNumber.CURRENT],
|
||||||
|
});
|
||||||
const tree = new Formatter().format(run);
|
const tree = new Formatter().format(run);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { ShadingType } from "file/table";
|
import { ShadingType } from "file/table";
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||||
import { FieldInstruction } from "file/table-of-contents/field-instruction";
|
import { FieldInstruction } from "file/table-of-contents/field-instruction";
|
||||||
import { Break } from "./break";
|
import { Break } from "./break";
|
||||||
import { Caps, SmallCaps } from "./caps";
|
import { Caps, SmallCaps } from "./caps";
|
||||||
@ -24,10 +25,10 @@ import {
|
|||||||
} from "./formatting";
|
} from "./formatting";
|
||||||
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
|
import { NumberOfPages, NumberOfPagesSection, Page } from "./page-number";
|
||||||
import { RunProperties } from "./properties";
|
import { RunProperties } from "./properties";
|
||||||
|
import { Text } from "./run-components/text";
|
||||||
import { RunFonts } from "./run-fonts";
|
import { RunFonts } from "./run-fonts";
|
||||||
import { SubScript, SuperScript } from "./script";
|
import { SubScript, SuperScript } from "./script";
|
||||||
import { Style } from "./style";
|
import { Style } from "./style";
|
||||||
import { Tab } from "./tab";
|
|
||||||
import { Underline, UnderlineType } from "./underline";
|
import { Underline, UnderlineType } from "./underline";
|
||||||
|
|
||||||
export interface IRunOptions {
|
export interface IRunOptions {
|
||||||
@ -57,7 +58,14 @@ export interface IRunOptions {
|
|||||||
readonly fill: string;
|
readonly fill: string;
|
||||||
readonly color: string;
|
readonly color: string;
|
||||||
};
|
};
|
||||||
readonly children?: Array<Begin | FieldInstruction | Separate | End>;
|
readonly children?: Array<Begin | FieldInstruction | Separate | End | PageNumber | FootnoteReferenceRun | string>;
|
||||||
|
readonly text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PageNumber {
|
||||||
|
CURRENT = "CURRENT",
|
||||||
|
TOTAL_PAGES = "TOTAL_PAGES",
|
||||||
|
TOTAL_PAGES_IN_SECTION = "TOTAL_PAGES_IN_SECTION",
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Run extends XmlComponent {
|
export class Run extends XmlComponent {
|
||||||
@ -139,8 +147,37 @@ export class Run extends XmlComponent {
|
|||||||
|
|
||||||
if (options.children) {
|
if (options.children) {
|
||||||
for (const child of options.children) {
|
for (const child of options.children) {
|
||||||
|
if (typeof child === "string") {
|
||||||
|
switch (child) {
|
||||||
|
case PageNumber.CURRENT:
|
||||||
|
this.root.push(new Begin());
|
||||||
|
this.root.push(new Page());
|
||||||
|
this.root.push(new Separate());
|
||||||
|
this.root.push(new End());
|
||||||
|
break;
|
||||||
|
case PageNumber.TOTAL_PAGES:
|
||||||
|
this.root.push(new Begin());
|
||||||
|
this.root.push(new NumberOfPages());
|
||||||
|
this.root.push(new Separate());
|
||||||
|
this.root.push(new End());
|
||||||
|
break;
|
||||||
|
case PageNumber.TOTAL_PAGES_IN_SECTION:
|
||||||
|
this.root.push(new Begin());
|
||||||
|
this.root.push(new NumberOfPagesSection());
|
||||||
|
this.root.push(new Separate());
|
||||||
|
this.root.push(new End());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.root.push(new Text(child));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this.root.push(child);
|
this.root.push(child);
|
||||||
}
|
}
|
||||||
|
} else if (options.text) {
|
||||||
|
this.root.push(new Text(options.text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,33 +185,4 @@ export class Run extends XmlComponent {
|
|||||||
this.root.splice(1, 0, new Break());
|
this.root.splice(1, 0, new Break());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public tab(): Run {
|
|
||||||
this.root.splice(1, 0, new Tab());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public pageNumber(): Run {
|
|
||||||
this.root.push(new Begin());
|
|
||||||
this.root.push(new Page());
|
|
||||||
this.root.push(new Separate());
|
|
||||||
this.root.push(new End());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public numberOfTotalPages(): Run {
|
|
||||||
this.root.push(new Begin());
|
|
||||||
this.root.push(new NumberOfPages());
|
|
||||||
this.root.push(new Separate());
|
|
||||||
this.root.push(new End());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public numberOfTotalPagesSection(): Run {
|
|
||||||
this.root.push(new Begin());
|
|
||||||
this.root.push(new NumberOfPagesSection());
|
|
||||||
this.root.push(new Separate());
|
|
||||||
this.root.push(new End());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
import { Formatter } from "export/formatter";
|
import { Formatter } from "export/formatter";
|
||||||
|
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||||
|
|
||||||
import { TextRun } from "./text-run";
|
import { TextRun } from "./text-run";
|
||||||
|
|
||||||
@ -19,9 +20,11 @@ describe("TextRun", () => {
|
|||||||
|
|
||||||
describe("#referenceFootnote()", () => {
|
describe("#referenceFootnote()", () => {
|
||||||
it("should add a valid footnote reference", () => {
|
it("should add a valid footnote reference", () => {
|
||||||
run = new TextRun("test");
|
run = new TextRun({
|
||||||
run.referenceFootnote(1);
|
children: ["test", new FootnoteReferenceRun(1)],
|
||||||
|
});
|
||||||
const tree = new Formatter().format(run);
|
const tree = new Formatter().format(run);
|
||||||
|
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test"] },
|
{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test"] },
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
|
||||||
import { IRunOptions, Run } from "./run";
|
import { IRunOptions, Run } from "./run";
|
||||||
import { Text } from "./run-components/text";
|
import { Text } from "./run-components/text";
|
||||||
|
|
||||||
export interface ITextRunOptions extends IRunOptions {
|
|
||||||
readonly text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TextRun extends Run {
|
export class TextRun extends Run {
|
||||||
constructor(options: ITextRunOptions | string) {
|
constructor(options: IRunOptions | string) {
|
||||||
if (typeof options === "string") {
|
if (typeof options === "string") {
|
||||||
super({});
|
super({});
|
||||||
this.root.push(new Text(options));
|
this.root.push(new Text(options));
|
||||||
@ -15,11 +10,5 @@ export class TextRun extends Run {
|
|||||||
}
|
}
|
||||||
|
|
||||||
super(options);
|
super(options);
|
||||||
this.root.push(new Text(options.text));
|
|
||||||
}
|
|
||||||
|
|
||||||
public referenceFootnote(id: number): TextRun {
|
|
||||||
this.root.push(new FootnoteReferenceRun(id));
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user