Part: Use TopoShape instead of TopoDS_Shape
This changes Attacher to use more our own TopoShape class which is more information rich.
This commit is contained in:
@@ -388,8 +388,8 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const
|
||||
result.message = SuggestResult::srLinkBroken;
|
||||
result.bestFitMode = mmDeactivated;
|
||||
|
||||
std::vector<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> shapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> shapeStorage;
|
||||
std::vector<eRefType> typeStr;
|
||||
try{
|
||||
readLinks(getRefObjects(),subnames, shapes, shapeStorage, typeStr);
|
||||
@@ -578,8 +578,8 @@ eRefType AttachEngine::getShapeType(const App::DocumentObject *obj, const std::s
|
||||
//const_cast is worth here, to keep obj argument const. We are not going to write anything to obj through this temporary link.
|
||||
tmpLink.setValue(const_cast<App::DocumentObject*>(obj), subshape.c_str());
|
||||
|
||||
std::vector<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> copiedShapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> copiedShapeStorage;
|
||||
std::vector<eRefType> types;
|
||||
readLinks(tmpLink.getValues(), tmpLink.getSubValues(), shapes, copiedShapeStorage, types);
|
||||
|
||||
@@ -740,13 +740,14 @@ eRefType AttachEngine::getRefTypeByName(const std::string& typeName)
|
||||
throw AttachEngineException(errmsg.str());
|
||||
}
|
||||
|
||||
GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector<const TopoDS_Shape*> &shapes)
|
||||
GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector<const TopoShape*> &shapes)
|
||||
{
|
||||
//explode compounds
|
||||
TopTools_HSequenceOfShape totalSeq;
|
||||
for (const TopoDS_Shape* pSh : shapes) {
|
||||
for (auto tSh : shapes) {
|
||||
auto pSh = tSh->getShape();
|
||||
ShapeExtend_Explorer xp;
|
||||
totalSeq.Append( xp.SeqFromCompound(*pSh, /*recursive=*/true));
|
||||
totalSeq.Append( xp.SeqFromCompound(pSh, /*recursive=*/true));
|
||||
}
|
||||
if (totalSeq.Length() == 0)
|
||||
throw AttachEngineException("AttachEngine::getInertialPropsOfShape: no geometry provided");
|
||||
@@ -820,8 +821,8 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector<const TopoD
|
||||
*/
|
||||
void AttachEngine::readLinks(const std::vector<App::DocumentObject*>& objs,
|
||||
const std::vector<std::string> &subs,
|
||||
std::vector<const TopoDS_Shape*> &shapes,
|
||||
std::vector<TopoDS_Shape> &storage,
|
||||
std::vector<const TopoShape*> &shapes,
|
||||
std::vector<TopoShape> &storage,
|
||||
std::vector<eRefType> &types)
|
||||
{
|
||||
storage.reserve(objs.size());
|
||||
@@ -837,12 +838,11 @@ void AttachEngine::readLinks(const std::vector<App::DocumentObject*>& objs,
|
||||
"AttachEngine3D: attached to a non App::GeoFeature '" << objs[i]->getNameInDocument() << "'");
|
||||
}
|
||||
}
|
||||
TopoDS_Shape myShape;
|
||||
|
||||
Part::TopoShape shape;
|
||||
try {
|
||||
// getTopoShape support fully qualified subnames and should return shape with correct
|
||||
// global placement.
|
||||
Part::TopoShape shape = Part::Feature::getTopoShape(objs[i], subs[i].c_str(), true);
|
||||
shape = Part::Feature::getTopoShape(objs[i], subs[i].c_str(), true);
|
||||
for (;;) {
|
||||
if (shape.isNull()) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
@@ -856,8 +856,6 @@ void AttachEngine::readLinks(const std::vector<App::DocumentObject*>& objs,
|
||||
// auto extract the single sub-shape from a compound
|
||||
shape = shape.getSubTopoShape(TopAbs_SHAPE, 1);
|
||||
}
|
||||
|
||||
myShape = shape.getShape();
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
@@ -871,18 +869,19 @@ void AttachEngine::readLinks(const std::vector<App::DocumentObject*>& objs,
|
||||
<< '.' << subs[i] << std::endl
|
||||
<< e.what());
|
||||
}
|
||||
if (myShape.IsNull()) {
|
||||
|
||||
if (shape.isNull()) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.'
|
||||
<< subs[i]);
|
||||
}
|
||||
|
||||
storage.emplace_back(myShape);
|
||||
storage.emplace_back(shape);
|
||||
shapes[i] = &(storage.back());
|
||||
|
||||
// FIXME: unpack single-child compounds here? Compounds are not used so far, so it should be
|
||||
// considered later, when the need arises.
|
||||
types[i] = getShapeType(*(shapes[i]));
|
||||
types[i] = getShapeType(shapes[i]->getShape());
|
||||
if (subs[i].length() == 0) {
|
||||
types[i] = eRefType(types[i] | rtFlagHasPlacement);
|
||||
}
|
||||
@@ -1140,8 +1139,8 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
throw ExceptionCancel(); // to be handled in positionBySupport, to not do anything if
|
||||
// disabled
|
||||
}
|
||||
std::vector<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> copiedShapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> copiedShapeStorage;
|
||||
std::vector<eRefType> types;
|
||||
readLinks(objs, subs, shapes, copiedShapeStorage, types);
|
||||
|
||||
@@ -1172,7 +1171,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subobjects "
|
||||
"specified (need one vertex).");
|
||||
}
|
||||
const TopoDS_Shape& sh = *shapes[0];
|
||||
const TopoDS_Shape& sh = shapes[0]->getShape();
|
||||
if (sh.IsNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEngine3D::calculateAttachedPlacement()!");
|
||||
@@ -1207,7 +1206,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z);
|
||||
}
|
||||
else if (isShapeOfType(types[0], rtConic) > 0) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(*shapes[0]);
|
||||
const TopoDS_Edge& e = TopoDS::Edge(shapes[0]->getShape());
|
||||
BRepAdaptor_Curve adapt(e);
|
||||
gp_Ax3 pos;
|
||||
switch (adapt.GetType()) {
|
||||
@@ -1259,7 +1258,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
|
||||
TopoDS_Vertex vertex;
|
||||
try {
|
||||
vertex = TopoDS::Vertex(*(shapes[1]));
|
||||
vertex = TopoDS::Vertex(shapes[1]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1330,7 +1329,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
gp_Pln plane;
|
||||
bool Reverse = false;
|
||||
try {
|
||||
face = TopoDS::Face(*(shapes[0]));
|
||||
face = TopoDS::Face(shapes[0]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1387,14 +1386,14 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
}
|
||||
|
||||
bool bThruVertex = false;
|
||||
if (shapes[0]->ShapeType() == TopAbs_VERTEX) {
|
||||
if (shapes[0]->shapeType() == TopAbs_VERTEX) {
|
||||
std::swap(shapes[0], shapes[1]);
|
||||
bThruVertex = true;
|
||||
}
|
||||
|
||||
TopoDS_Face face;
|
||||
try {
|
||||
face = TopoDS::Face(*(shapes[0]));
|
||||
face = TopoDS::Face(shapes[0]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1405,7 +1404,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
|
||||
TopoDS_Vertex vertex;
|
||||
try {
|
||||
vertex = TopoDS::Vertex(*(shapes[1]));
|
||||
vertex = TopoDS::Vertex(shapes[1]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1472,14 +1471,14 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
}
|
||||
|
||||
bool bThruVertex = false;
|
||||
if (shapes[0]->ShapeType() == TopAbs_VERTEX && shapes.size() >= 2) {
|
||||
if (shapes[0]->shapeType() == TopAbs_VERTEX && shapes.size() >= 2) {
|
||||
std::swap(shapes[0], shapes[1]);
|
||||
bThruVertex = true;
|
||||
}
|
||||
|
||||
TopoDS_Edge path;
|
||||
try {
|
||||
path = TopoDS::Edge(*(shapes[0]));
|
||||
path = TopoDS::Edge(shapes[0]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1506,7 +1505,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
if (shapes.size() >= 2) {
|
||||
TopoDS_Vertex vertex;
|
||||
try {
|
||||
vertex = TopoDS::Vertex(*(shapes[1]));
|
||||
vertex = TopoDS::Vertex(shapes[1]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1633,7 +1632,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
std::vector<gp_Pnt> points;
|
||||
|
||||
for (const auto & shape : shapes) {
|
||||
const TopoDS_Shape &sh = *shape;
|
||||
const TopoDS_Shape &sh = shape->getShape();
|
||||
if (sh.IsNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEngine3D::calculateAttachedPlacement()!");
|
||||
@@ -1725,7 +1724,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
gp_Lin lines[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
edges[i] = &TopoDS::Edge(*(shapes[i]));
|
||||
edges[i] = &TopoDS::Edge(shapes[i]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1845,28 +1844,28 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
gp_Vec dirs[3];
|
||||
|
||||
// read out origin
|
||||
if (shapes[0]->IsNull()) {
|
||||
if (shapes[0]->isNull()) {
|
||||
THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: null shape!")
|
||||
}
|
||||
if (shapes[0]->ShapeType() != TopAbs_VERTEX) {
|
||||
if (shapes[0]->shapeType() != TopAbs_VERTEX) {
|
||||
THROWM(Base::TypeError,
|
||||
"AttachEngine3D::calculateAttachedPlacement: first reference must be a "
|
||||
"vertex, it's not")
|
||||
}
|
||||
SketchBasePoint = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[0])));
|
||||
SketchBasePoint = BRep_Tool::Pnt(TopoDS::Vertex(shapes[0]->getShape()));
|
||||
|
||||
// read out axes directions
|
||||
for (size_t i = 1; i < 3 && i < shapes.size(); ++i) {
|
||||
if (shapes[i]->IsNull()) {
|
||||
if (shapes[i]->isNull()) {
|
||||
THROWM(Base::TypeError,
|
||||
"AttachEngine3D::calculateAttachedPlacement: null shape!")
|
||||
}
|
||||
if (shapes[i]->ShapeType() == TopAbs_VERTEX) {
|
||||
gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[i])));
|
||||
if (shapes[i]->shapeType() == TopAbs_VERTEX) {
|
||||
gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(shapes[i]->getShape()));
|
||||
dirs[order[i - 1]] = gp_Vec(SketchBasePoint, p);
|
||||
}
|
||||
else if (shapes[i]->ShapeType() == TopAbs_EDGE) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(*(shapes[i]));
|
||||
else if (shapes[i]->shapeType() == TopAbs_EDGE) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(shapes[i]->getShape());
|
||||
BRepAdaptor_Curve crv(e);
|
||||
double u1 = crv.FirstParameter();
|
||||
double u2 = crv.LastParameter();
|
||||
@@ -2083,8 +2082,8 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector<App::DocumentObj
|
||||
|
||||
Base::Placement plm;
|
||||
if (!bReUsed) {
|
||||
std::vector<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> copiedShapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> copiedShapeStorage;
|
||||
std::vector<eRefType> types;
|
||||
readLinks(objs, subs, shapes, copiedShapeStorage, types);
|
||||
|
||||
@@ -2157,7 +2156,7 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector<App::DocumentObj
|
||||
std::vector<gp_Pnt> points;
|
||||
|
||||
for (const auto & shape : shapes) {
|
||||
const TopoDS_Shape &sh = *shape;
|
||||
const TopoDS_Shape &sh = shape->getShape();
|
||||
if (sh.IsNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
|
||||
@@ -2197,13 +2196,13 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector<App::DocumentObj
|
||||
} break;
|
||||
case mm1Asymptote1:
|
||||
case mm1Asymptote2: {
|
||||
if (shapes[0]->IsNull()) {
|
||||
if (shapes[0]->isNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
|
||||
}
|
||||
TopoDS_Edge e;
|
||||
try {
|
||||
e = TopoDS::Edge(*(shapes[0]));
|
||||
e = TopoDS::Edge(shapes[0]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -2228,13 +2227,13 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector<App::DocumentObj
|
||||
} break;
|
||||
case mm1Directrix1:
|
||||
case mm1Directrix2: {
|
||||
if (shapes[0]->IsNull()) {
|
||||
if (shapes[0]->isNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
|
||||
}
|
||||
TopoDS_Edge e;
|
||||
try {
|
||||
e = TopoDS::Edge(*(shapes[0]));
|
||||
e = TopoDS::Edge(shapes[0]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -2283,13 +2282,13 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector<App::DocumentObj
|
||||
"AttachEngineLine::calculateAttachedPlacement: Intersection mode requires "
|
||||
"two shapes; only one is supplied");
|
||||
}
|
||||
if (shapes[0]->IsNull() || shapes[1]->IsNull()) {
|
||||
if (shapes[0]->isNull() || shapes[1]->isNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
|
||||
}
|
||||
|
||||
const TopoDS_Face& face1 = TopoDS::Face(*(shapes[0]));
|
||||
const TopoDS_Face& face2 = TopoDS::Face(*(shapes[1]));
|
||||
const TopoDS_Face& face1 = TopoDS::Face(shapes[0]->getShape());
|
||||
const TopoDS_Face& face2 = TopoDS::Face(shapes[1]->getShape());
|
||||
|
||||
Handle(Geom_Surface) hSurf1 = BRep_Tool::Surface(face1);
|
||||
Handle(Geom_Surface) hSurf2 = BRep_Tool::Surface(face2);
|
||||
@@ -2324,15 +2323,15 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector<App::DocumentObj
|
||||
"AttachEngineLine::calculateAttachedPlacement: Proximity mode requires two "
|
||||
"shapes; only one is supplied");
|
||||
}
|
||||
if (shapes[0]->IsNull()) {
|
||||
if (shapes[0]->isNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
|
||||
}
|
||||
if (shapes[1]->IsNull()) {
|
||||
if (shapes[1]->isNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
|
||||
}
|
||||
BRepExtrema_DistShapeShape distancer(*(shapes[0]), *(shapes[1]));
|
||||
BRepExtrema_DistShapeShape distancer(shapes[0]->getShape(), shapes[1]->getShape());
|
||||
if (!distancer.IsDone()) {
|
||||
throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: "
|
||||
"proximity calculation failed.");
|
||||
@@ -2450,8 +2449,8 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector<App::DocumentOb
|
||||
|
||||
Base::Placement plm;
|
||||
if (!bReUsed) {
|
||||
std::vector<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> copiedShapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> copiedShapeStorage;
|
||||
std::vector<eRefType> types;
|
||||
readLinks(objs, subs, shapes, copiedShapeStorage, types);
|
||||
|
||||
@@ -2470,7 +2469,7 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector<App::DocumentOb
|
||||
std::vector<gp_Pnt> points;
|
||||
assert(!shapes.empty());
|
||||
|
||||
const TopoDS_Shape& sh = *shapes[0];
|
||||
const TopoDS_Shape& sh = shapes[0]->getShape();
|
||||
if (sh.IsNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEnginePoint::calculateAttachedPlacement()!");
|
||||
@@ -2492,13 +2491,13 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector<App::DocumentOb
|
||||
} break;
|
||||
case mm0Focus1:
|
||||
case mm0Focus2: {
|
||||
if (shapes[0]->IsNull()) {
|
||||
if (shapes[0]->isNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEnginePoint::calculateAttachedPlacement()!");
|
||||
}
|
||||
TopoDS_Edge e;
|
||||
try {
|
||||
e = TopoDS::Edge(*(shapes[0]));
|
||||
e = TopoDS::Edge(shapes[0]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -2546,16 +2545,16 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector<App::DocumentOb
|
||||
"AttachEnginePoint::calculateAttachedPlacement: Proximity mode requires "
|
||||
"two shapes; only one is supplied");
|
||||
}
|
||||
if (shapes[0]->IsNull()) {
|
||||
if (shapes[0]->isNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEnginePoint::calculateAttachedPlacement()!");
|
||||
}
|
||||
if (shapes[1]->IsNull()) {
|
||||
if (shapes[1]->isNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEnginePoint::calculateAttachedPlacement()!");
|
||||
}
|
||||
|
||||
BasePoint = getProximityPoint(mmode, *(shapes[0]), *(shapes[1]));
|
||||
BasePoint = getProximityPoint(mmode, shapes[0]->getShape(), shapes[1]->getShape());
|
||||
} break;
|
||||
case mm0CenterOfMass: {
|
||||
GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes);
|
||||
|
||||
@@ -363,7 +363,7 @@ public://helper functions that may be useful outside of the class
|
||||
|
||||
static eRefType getRefTypeByName(const std::string &typeName);
|
||||
|
||||
static GProp_GProps getInertialPropsOfShape(const std::vector<const TopoDS_Shape*> &shapes);
|
||||
static GProp_GProps getInertialPropsOfShape(const std::vector<const Part::TopoShape*> &shapes);
|
||||
|
||||
std::vector<App::DocumentObject*> getRefObjects() const;
|
||||
const std::vector<std::string> &getSubValues() const {return subnames;}
|
||||
@@ -430,7 +430,9 @@ protected:
|
||||
}
|
||||
static void readLinks(const std::vector<App::DocumentObject*> &objs,
|
||||
const std::vector<std::string> &subs,
|
||||
std::vector<const TopoDS_Shape*>& shapes, std::vector<TopoDS_Shape> &storage,
|
||||
|
||||
std::vector<const Part::TopoShape*>& shapes,
|
||||
std::vector<Part::TopoShape> &storage,
|
||||
std::vector<eRefType> &types);
|
||||
|
||||
static void throwWrongMode(eMapMode mmode);
|
||||
|
||||
@@ -79,8 +79,8 @@ TEST_F(AttacherTest, TestGetShapeType)
|
||||
TEST_F(AttacherTest, TestGetInertialPropsOfShape)
|
||||
{
|
||||
auto& attacher = _boxes[1]->attacher();
|
||||
std::vector<const TopoDS_Shape*> result;
|
||||
auto faces = _boxes[1]->Shape.getShape().getSubShapes(TopAbs_FACE);
|
||||
std::vector<const TopoShape*> result;
|
||||
auto faces = _boxes[1]->Shape.getShape().getSubTopoShapes(TopAbs_FACE);
|
||||
result.emplace_back(&faces[0]);
|
||||
auto shapeType = attacher.getInertialPropsOfShape(result);
|
||||
EXPECT_EQ(result.size(), 1);
|
||||
|
||||
Reference in New Issue
Block a user