mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-27 11:52:25 +00:00
Strike out (in the output) deleted display math with track-changes
Showing deleted display math by enabling "Show Changes in Output" was only possible with dvi (through dvipost). Although LyX strikes out such formulas on screen, it was impossible obtaining an output directly using pdflatex (or other engines producing pdf) because ulem cannot cope with display math material and gives errors. The solution is to strike out by ourselves such deleted formulas. I took into account several options. One of them would produce an output similar to dvipost (which strikes out each element), but would have required much more changes in the output routines. Eventually, I opted for using tikz, which gives a more clean output (as it requires to simply adding a preamble and a postamble to the latex code of any displayed math, instead of a mark up tailored to each particular math construct). The look of the pdf output is similar to the way LyX strikes out the equations on screen. Fixes #9678
This commit is contained in:
parent
3191178555
commit
8a1f936ff1
@ -1932,6 +1932,10 @@ void Buffer::writeLaTeXSource(otexstream & os,
|
||||
// make the body.
|
||||
os << "\\begin{document}\n";
|
||||
|
||||
// mark the start of a new paragraph by simulating a newline,
|
||||
// so that os.afterParbreak() returns true at document start
|
||||
os.lastChar('\n');
|
||||
|
||||
// output the parent macros
|
||||
MacroSet::iterator it = parentMacros.begin();
|
||||
MacroSet::iterator end = parentMacros.end();
|
||||
|
@ -421,7 +421,7 @@ int Changes::latexMarkChange(otexstream & os, BufferParams const & bparams,
|
||||
// close \lyxadded or \lyxdeleted
|
||||
os << '}';
|
||||
column++;
|
||||
if (oldChange.type == Change::DELETED)
|
||||
if (oldChange.type == Change::DELETED && !runparams.wasDisplayMath)
|
||||
--runparams.inulemcmd;
|
||||
}
|
||||
|
||||
@ -433,6 +433,7 @@ int Changes::latexMarkChange(otexstream & os, BufferParams const & bparams,
|
||||
docstring macro_beg;
|
||||
if (change.type == Change::DELETED) {
|
||||
macro_beg = from_ascii("\\lyxdeleted{");
|
||||
if (!runparams.inDisplayMath)
|
||||
++runparams.inulemcmd;
|
||||
}
|
||||
else if (change.type == Change::INSERTED)
|
||||
@ -442,6 +443,17 @@ int Changes::latexMarkChange(otexstream & os, BufferParams const & bparams,
|
||||
bparams.authors().get(change.author).name(),
|
||||
chgTime, runparams);
|
||||
|
||||
// signature needed by \lyxsout to correctly strike out display math
|
||||
if (change.type == Change::DELETED && runparams.inDisplayMath
|
||||
&& (!LaTeXFeatures::isAvailable("dvipost")
|
||||
|| (runparams.flavor != OutputParams::LATEX
|
||||
&& runparams.flavor != OutputParams::DVILUATEX))) {
|
||||
if (os.afterParbreak())
|
||||
str += from_ascii("\\\\\\noindent\n");
|
||||
else
|
||||
str += from_ascii("\\\\\\\\\n");
|
||||
}
|
||||
|
||||
os << str;
|
||||
column += str.size();
|
||||
|
||||
|
@ -191,12 +191,25 @@ static docstring const changetracking_dvipost_def = from_ascii(
|
||||
static docstring const changetracking_xcolor_ulem_def = from_ascii(
|
||||
"%% Change tracking with ulem\n"
|
||||
"\\DeclareRobustCommand{\\lyxadded}[3]{{\\color{lyxadded}{}#3}}\n"
|
||||
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\color{lyxdeleted}\\sout{#3}}}\n");
|
||||
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\color{lyxdeleted}\\lyxsout{#3}}}\n"
|
||||
"\\DeclareRobustCommand{\\lyxsout}[1]{\\ifx\\\\#1\\else\\sout{#1}\\fi}\n");
|
||||
|
||||
static docstring const changetracking_xcolor_ulem_hyperref_def = from_ascii(
|
||||
"%% Change tracking with ulem\n"
|
||||
"\\DeclareRobustCommand{\\lyxadded}[3]{{\\texorpdfstring{\\color{lyxadded}{}}{}#3}}\n"
|
||||
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\texorpdfstring{\\color{lyxdeleted}\\sout{#3}}{}}}\n");
|
||||
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\texorpdfstring{\\color{lyxdeleted}\\lyxsout{#3}}{}}}\n"
|
||||
"\\DeclareRobustCommand{\\lyxsout}[1]{\\ifx\\\\#1\\else\\sout{#1}\\fi}\n");
|
||||
|
||||
static docstring const changetracking_tikz_math_sout_def = from_ascii(
|
||||
"%% Strike out display math with tikz\n"
|
||||
"\\usepackage{tikz}\n"
|
||||
"\\usetikzlibrary{calc}\n"
|
||||
"\\newcommand{\\lyxmathsout}[1]{%\n"
|
||||
" \\tikz[baseline=(math.base)]{\n"
|
||||
" \\node[inner sep=0pt,outer sep=0pt](math){#1};\n"
|
||||
" \\draw($(math.south west)+(2em,.5em)$)--($(math.north east)-(2em,.5em)$);\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
static docstring const changetracking_none_def = from_ascii(
|
||||
"\\newcommand{\\lyxadded}[3]{#3}\n"
|
||||
@ -388,7 +401,8 @@ static docstring const lyxstrikeout_style = from_ascii(
|
||||
|
||||
LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p,
|
||||
OutputParams const & r)
|
||||
: buffer_(&b), params_(p), runparams_(r), in_float_(false)
|
||||
: buffer_(&b), params_(p), runparams_(r), in_float_(false),
|
||||
in_deleted_inset_(false)
|
||||
{}
|
||||
|
||||
|
||||
@ -1367,6 +1381,9 @@ docstring const LaTeXFeatures::getMacros() const
|
||||
macros << changetracking_xcolor_ulem_def;
|
||||
}
|
||||
|
||||
if (mustProvide("ct-tikz-math-sout"))
|
||||
macros << changetracking_tikz_math_sout_def;
|
||||
|
||||
if (mustProvide("ct-none"))
|
||||
macros << changetracking_none_def;
|
||||
|
||||
|
@ -156,6 +156,10 @@ public:
|
||||
bool inFloat() const { return in_float_; }
|
||||
/// are we in a float?
|
||||
void inFloat(bool const b) { in_float_ = b; }
|
||||
/// are we in a deleted inset?
|
||||
bool inDeletedInset() const { return in_deleted_inset_; }
|
||||
/// are we in a deleted inset?
|
||||
void inDeletedInset(bool const b) { in_deleted_inset_ = b; }
|
||||
/// Runparams that will be used for exporting this file.
|
||||
OutputParams const & runparams() const { return runparams_; }
|
||||
/// Resolve alternatives like "esint|amsmath|wasysym"
|
||||
@ -209,6 +213,8 @@ private:
|
||||
///
|
||||
bool in_float_;
|
||||
///
|
||||
bool in_deleted_inset_;
|
||||
///
|
||||
docstring htmltitle_;
|
||||
};
|
||||
|
||||
|
@ -23,7 +23,7 @@ OutputParams::OutputParams(Encoding const * enc)
|
||||
moving_arg(false), intitle(false), inulemcmd(0), local_font(0), master_language(0),
|
||||
encoding(enc), free_spacing(false), use_babel(false), use_polyglossia(false),
|
||||
use_indices(false), use_japanese(false), linelen(0), depth(0),
|
||||
exportdata(new ExportData),
|
||||
exportdata(new ExportData), inDisplayMath(false), wasDisplayMath(false),
|
||||
inComment(false), inTableCell(NO), inFloat(NONFLOAT),
|
||||
inIndexEntry(false), inIPA(false), inDeletedInset(0),
|
||||
changeOfDeletedInset(Change::UNCHANGED),
|
||||
|
@ -184,6 +184,16 @@ public:
|
||||
*/
|
||||
shared_ptr<ExportData> exportdata;
|
||||
|
||||
/** Whether we are entering a display math inset.
|
||||
* Needed to correctly strike out deleted math in change tracking.
|
||||
*/
|
||||
mutable bool inDisplayMath;
|
||||
|
||||
/** Whether we are leaving a display math inset.
|
||||
* Needed to correctly track nested ulem commands in change tracking.
|
||||
*/
|
||||
mutable bool wasDisplayMath;
|
||||
|
||||
/** Whether we are inside a comment inset. Insets that are including
|
||||
* external files like InsetGraphics, InsetInclude and InsetExternal
|
||||
* may only write the usual output and must not attempt to do
|
||||
|
@ -52,6 +52,8 @@
|
||||
#include "insets/InsetLabel.h"
|
||||
#include "insets/InsetSpecialChar.h"
|
||||
|
||||
#include "mathed/InsetMathHull.h"
|
||||
|
||||
#include "support/debug.h"
|
||||
#include "support/docstring_list.h"
|
||||
#include "support/ExceptionMessage.h"
|
||||
@ -1437,7 +1439,9 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const
|
||||
InsetList::const_iterator iend = insetlist_.end();
|
||||
for (; icit != iend; ++icit) {
|
||||
if (icit->inset) {
|
||||
features.inDeletedInset(owner_->isDeleted(icit->pos));
|
||||
icit->inset->validate(features);
|
||||
features.inDeletedInset(false);
|
||||
if (layout_->needprotect &&
|
||||
icit->inset->lyxCode() == FOOT_CODE)
|
||||
features.require("NeedLyXFootnoteCode");
|
||||
@ -2391,6 +2395,25 @@ void Paragraph::latex(BufferParams const & bparams,
|
||||
runparams);
|
||||
}
|
||||
|
||||
runparams.wasDisplayMath = runparams.inDisplayMath;
|
||||
runparams.inDisplayMath = false;
|
||||
bool deleted_display_math = false;
|
||||
|
||||
// Check whether a display math inset follows
|
||||
if (d->text_[i] == META_INSET
|
||||
&& i >= start_pos && (end_pos == -1 || i < end_pos)) {
|
||||
InsetMath const * im = getInset(i)->asInsetMath();
|
||||
if (im && im->asHullInset()
|
||||
&& im->asHullInset()->outerDisplay()) {
|
||||
runparams.inDisplayMath = true;
|
||||
// runparams.inDeletedInset will be set by
|
||||
// latexInset later, but we need this info
|
||||
// before it is called. On the other hand, we
|
||||
// cannot set it here because it is a counter.
|
||||
deleted_display_math = isDeleted(i);
|
||||
}
|
||||
}
|
||||
|
||||
Change const & change = runparams.inDeletedInset
|
||||
? runparams.changeOfDeletedInset : lookupChange(i);
|
||||
|
||||
@ -2402,7 +2425,6 @@ void Paragraph::latex(BufferParams const & bparams,
|
||||
}
|
||||
basefont = getLayoutFont(bparams, outerfont);
|
||||
running_font = basefont;
|
||||
|
||||
column += Changes::latexMarkChange(os, bparams, runningChange,
|
||||
change, runparams);
|
||||
runningChange = change;
|
||||
@ -2464,6 +2486,19 @@ void Paragraph::latex(BufferParams const & bparams,
|
||||
|
||||
char_type const c = d->text_[i];
|
||||
|
||||
// A display math inset inside an ulem command will be output
|
||||
// as a box of width \columnwidth, so we have to either disable
|
||||
// indentation if the inset starts a paragraph, or start a new
|
||||
// line to accommodate such box. This has to be done before
|
||||
// writing any font changing commands.
|
||||
if (runparams.inDisplayMath && !deleted_display_math
|
||||
&& runparams.inulemcmd) {
|
||||
if (os.afterParbreak())
|
||||
os << "\\noindent";
|
||||
else
|
||||
os << "\\\\\n";
|
||||
}
|
||||
|
||||
// Do we need to change font?
|
||||
if ((font != running_font ||
|
||||
font.language() != running_font.language()) &&
|
||||
@ -2473,6 +2508,15 @@ void Paragraph::latex(BufferParams const & bparams,
|
||||
column += font.latexWriteStartChanges(ods, bparams,
|
||||
runparams, basefont,
|
||||
last_font);
|
||||
// Check again for display math in ulem commands as a
|
||||
// font change may also occur just before a math inset.
|
||||
if (runparams.inDisplayMath && !deleted_display_math
|
||||
&& runparams.inulemcmd) {
|
||||
if (os.afterParbreak())
|
||||
os << "\\noindent";
|
||||
else
|
||||
os << "\\\\\n";
|
||||
}
|
||||
running_font = font;
|
||||
open_font = true;
|
||||
docstring fontchange = ods.str();
|
||||
@ -2524,10 +2568,10 @@ void Paragraph::latex(BufferParams const & bparams,
|
||||
basefont, outerfont, open_font,
|
||||
runningChange, style, i, column);
|
||||
}
|
||||
} else {
|
||||
if (i >= start_pos && (end_pos == -1 || i < end_pos)) {
|
||||
} else if (i >= start_pos && (end_pos == -1 || i < end_pos)) {
|
||||
try {
|
||||
d->latexSpecialChar(os, bparams, rp, running_font, runningChange,
|
||||
d->latexSpecialChar(os, bparams, rp,
|
||||
running_font, runningChange,
|
||||
style, i, end_pos, column);
|
||||
} catch (EncodingException & e) {
|
||||
if (runparams.dryrun) {
|
||||
@ -2543,11 +2587,15 @@ void Paragraph::latex(BufferParams const & bparams,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the encoding to that returned from latexSpecialChar (see
|
||||
// comment for encoding member in OutputParams.h)
|
||||
runparams.encoding = rp.encoding;
|
||||
|
||||
// Also carry on the info on a closed ulem command for insets
|
||||
// such as Note that do not produce any output, so that no
|
||||
// command is ever executed but its opening was recorded.
|
||||
runparams.inulemcmd = rp.inulemcmd;
|
||||
}
|
||||
|
||||
// If we have an open font definition, we have to close it
|
||||
|
@ -118,6 +118,37 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
// writes a preamble for underlined or struck out math display
|
||||
void writeMathdisplayPreamble(WriteStream & os)
|
||||
{
|
||||
if (os.strikeoutMath()) {
|
||||
if (os.ulemCmd() == WriteStream::UNDERLINE)
|
||||
os << "\\raisebox{-\\belowdisplayshortskip}{"
|
||||
"\\lyxmathsout{\\parbox[b]{\\columnwidth}{";
|
||||
else
|
||||
os << "\\lyxmathsout{\\parbox{\\columnwidth}{";
|
||||
} else if (os.ulemCmd() == WriteStream::UNDERLINE)
|
||||
os << "\\raisebox{-\\belowdisplayshortskip}{"
|
||||
"\\parbox[b]{\\columnwidth}{";
|
||||
else if (os.ulemCmd() == WriteStream::STRIKEOUT)
|
||||
os << "\\parbox{\\columnwidth}{";
|
||||
}
|
||||
|
||||
|
||||
// writes a postamble for underlined or struck out math display
|
||||
void writeMathdisplayPostamble(WriteStream & os)
|
||||
{
|
||||
if (os.strikeoutMath()) {
|
||||
if (os.ulemCmd() == WriteStream::UNDERLINE)
|
||||
os << "}";
|
||||
os << "}}\\\\\n";
|
||||
} else if (os.ulemCmd() == WriteStream::UNDERLINE)
|
||||
os << "}}\\\\\n";
|
||||
else if (os.ulemCmd() == WriteStream::STRIKEOUT)
|
||||
os << "}\\\\\n";
|
||||
}
|
||||
|
||||
|
||||
} // end anon namespace
|
||||
|
||||
|
||||
@ -892,6 +923,29 @@ bool InsetMathHull::ams() const
|
||||
}
|
||||
|
||||
|
||||
bool InsetMathHull::outerDisplay() const
|
||||
{
|
||||
switch (type_) {
|
||||
case hullEquation:
|
||||
case hullEqnArray:
|
||||
case hullAlign:
|
||||
case hullFlAlign:
|
||||
case hullGather:
|
||||
case hullMultline:
|
||||
return true;
|
||||
case hullNone:
|
||||
case hullSimple:
|
||||
case hullAlignAt:
|
||||
case hullXAlignAt:
|
||||
case hullXXAlignAt:
|
||||
case hullUnknown:
|
||||
case hullRegexp:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Inset::DisplayType InsetMathHull::display() const
|
||||
{
|
||||
switch (type_) {
|
||||
@ -957,6 +1011,9 @@ void InsetMathHull::validate(LaTeXFeatures & features) const
|
||||
+ bgcol + string("}{\\ensuremath{\\mathtt{#1}}}}"));
|
||||
features.addPreambleSnippet(
|
||||
string("\\newcommand{\\endregexp}{}"));
|
||||
} else if (outerDisplay() && features.inDeletedInset()
|
||||
&& !features.mustProvide("ct-dvipost")) {
|
||||
features.require("ct-tikz-math-sout");
|
||||
}
|
||||
|
||||
// Validation is necessary only if not using AMS math.
|
||||
@ -988,6 +1045,8 @@ void InsetMathHull::header_write(WriteStream & os) const
|
||||
break;
|
||||
|
||||
case hullSimple:
|
||||
if (os.ulemCmd())
|
||||
os << "\\mbox{";
|
||||
os << '$';
|
||||
os.startOuterRow();
|
||||
if (cell(0).empty())
|
||||
@ -995,6 +1054,7 @@ void InsetMathHull::header_write(WriteStream & os) const
|
||||
break;
|
||||
|
||||
case hullEquation:
|
||||
writeMathdisplayPreamble(os);
|
||||
os << "\n";
|
||||
os.startOuterRow();
|
||||
if (n)
|
||||
@ -1008,6 +1068,7 @@ void InsetMathHull::header_write(WriteStream & os) const
|
||||
case hullFlAlign:
|
||||
case hullGather:
|
||||
case hullMultline:
|
||||
writeMathdisplayPreamble(os);
|
||||
os << "\n";
|
||||
os.startOuterRow();
|
||||
os << "\\begin{" << hullName(type_) << star(n) << "}\n";
|
||||
@ -1052,6 +1113,8 @@ void InsetMathHull::footer_write(WriteStream & os) const
|
||||
|
||||
case hullSimple:
|
||||
os << '$';
|
||||
if (os.ulemCmd())
|
||||
os << "}";
|
||||
break;
|
||||
|
||||
case hullEquation:
|
||||
@ -1061,15 +1124,22 @@ void InsetMathHull::footer_write(WriteStream & os) const
|
||||
os << "\\end{equation" << star(n) << "}\n";
|
||||
else
|
||||
os << "\\]\n";
|
||||
writeMathdisplayPostamble(os);
|
||||
break;
|
||||
|
||||
case hullEqnArray:
|
||||
case hullAlign:
|
||||
case hullFlAlign:
|
||||
case hullAlignAt:
|
||||
case hullXAlignAt:
|
||||
case hullGather:
|
||||
case hullMultline:
|
||||
os << "\n";
|
||||
os.startOuterRow();
|
||||
os << "\\end{" << hullName(type_) << star(n) << "}\n";
|
||||
writeMathdisplayPostamble(os);
|
||||
break;
|
||||
|
||||
case hullAlignAt:
|
||||
case hullXAlignAt:
|
||||
os << "\n";
|
||||
os.startOuterRow();
|
||||
os << "\\end{" << hullName(type_) << star(n) << "}\n";
|
||||
|
@ -86,6 +86,8 @@ public:
|
||||
///
|
||||
bool ams() const;
|
||||
///
|
||||
bool outerDisplay() const;
|
||||
///
|
||||
void validate(LaTeXFeatures & features) const;
|
||||
/// identifies HullInset
|
||||
InsetMathHull const * asHullInset() const { return this; }
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "Encoding.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "FuncStatus.h"
|
||||
#include "LaTeXFeatures.h"
|
||||
#include "LyX.h"
|
||||
#include "LyXRC.h"
|
||||
#include "OutputParams.h"
|
||||
@ -406,6 +407,18 @@ void InsetMathNest::latex(otexstream & os, OutputParams const & runparams) const
|
||||
WriteStream wi(os, runparams.moving_arg, true,
|
||||
runparams.dryrun ? WriteStream::wsDryrun : WriteStream::wsDefault,
|
||||
runparams.encoding);
|
||||
wi.strikeoutMath(runparams.inDeletedInset
|
||||
&& (!LaTeXFeatures::isAvailable("dvipost")
|
||||
|| (runparams.flavor != OutputParams::LATEX
|
||||
&& runparams.flavor != OutputParams::DVILUATEX)));
|
||||
if (runparams.inulemcmd) {
|
||||
wi.ulemCmd(WriteStream::UNDERLINE);
|
||||
if (runparams.local_font) {
|
||||
FontInfo f = runparams.local_font->fontInfo();
|
||||
if (f.strikeout() == FONT_ON)
|
||||
wi.ulemCmd(WriteStream::STRIKEOUT);
|
||||
}
|
||||
}
|
||||
wi.canBreakLine(os.canBreakLine());
|
||||
if (runparams.lastid != -1) {
|
||||
wi.pushRowEntry(os.texrow().textEntry(runparams.lastid,
|
||||
|
@ -127,7 +127,7 @@ WriteStream::WriteStream(otexrowstream & os, bool fragile, bool latex,
|
||||
: os_(os), fragile_(fragile), firstitem_(false), latex_(latex),
|
||||
output_(output), pendingspace_(false), pendingbrace_(false),
|
||||
textmode_(false), locked_(0), ascii_(0), canbreakline_(true),
|
||||
line_(0), encoding_(encoding)
|
||||
mathsout_(false), ulemcmd_(NONE), line_(0), encoding_(encoding)
|
||||
{}
|
||||
|
||||
|
||||
|
@ -40,6 +40,12 @@ public:
|
||||
wsPreview
|
||||
};
|
||||
///
|
||||
enum UlemCmdType {
|
||||
NONE,
|
||||
UNDERLINE,
|
||||
STRIKEOUT
|
||||
};
|
||||
///
|
||||
WriteStream(otexrowstream & os, bool fragile, bool latex, OutputType output,
|
||||
Encoding const * encoding = 0);
|
||||
///
|
||||
@ -66,6 +72,14 @@ public:
|
||||
void canBreakLine(bool breakline) { canbreakline_ = breakline; }
|
||||
/// tell whether we can write an immediately following newline char
|
||||
bool canBreakLine() const { return canbreakline_; }
|
||||
/// record whether we have to take care for striking out display math
|
||||
void strikeoutMath(bool mathsout) { mathsout_ = mathsout; }
|
||||
/// tell whether we have to take care for striking out display math
|
||||
bool strikeoutMath() const { return mathsout_; }
|
||||
/// record which ulem command type we are inside
|
||||
void ulemCmd(UlemCmdType ulemcmd) { ulemcmd_ = ulemcmd; }
|
||||
/// tell which ulem command type we are inside
|
||||
UlemCmdType ulemCmd() const { return ulemcmd_; }
|
||||
/// writes space if next thing is isalpha()
|
||||
void pendingSpace(bool how);
|
||||
/// writes space if next thing is isalpha()
|
||||
@ -120,6 +134,10 @@ private:
|
||||
bool ascii_;
|
||||
/// are we allowed to output an immediately following newline?
|
||||
bool canbreakline_;
|
||||
/// should we take care for striking out display math?
|
||||
bool mathsout_;
|
||||
/// what ulem command are we inside (none, underline, strikeout)?
|
||||
UlemCmdType ulemcmd_;
|
||||
///
|
||||
int line_;
|
||||
///
|
||||
|
@ -73,6 +73,9 @@ What's new
|
||||
|
||||
- Fix LaTeX output of fixed-width cells with decimal separator (bug 9568).
|
||||
|
||||
- Fix strike out of deleted display math with track-changes and pdf
|
||||
output (bug 9678).
|
||||
|
||||
- Do not hardcode required packages for Note inset.
|
||||
|
||||
- Make *-lyxformat-* backup files use .lyx~ extension.
|
||||
|
Loading…
Reference in New Issue
Block a user