mirror of
https://gitea.msk.dinamika-avia.ru/Constanta-Design/RRJServer.git
synced 2026-03-27 19:45:43 +03:00
CFIController 0
This commit is contained in:
@@ -58,6 +58,10 @@ add_library(Server SHARED
|
||||
Systems/docsupdater.h
|
||||
providerdblms/providerdblms.cpp
|
||||
providerdblms/providerdblms.h
|
||||
cficontroller/cficontroller.cpp
|
||||
cficontroller/cficontroller.h
|
||||
cficontroller/cfiobject.cpp
|
||||
cficontroller/cfiobject.h
|
||||
Server.qrc
|
||||
)
|
||||
|
||||
@@ -74,6 +78,7 @@ target_include_directories(Server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/settings)
|
||||
target_include_directories(Server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/Data)
|
||||
target_include_directories(Server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/Systems)
|
||||
target_include_directories(Server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/Systems/Parsers)
|
||||
target_include_directories(Server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/cficontroller)
|
||||
|
||||
target_include_directories(Server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../LibDataBaseInterface)
|
||||
if(PROJECT_TYPE_DEBUG)
|
||||
|
||||
@@ -41,8 +41,7 @@ bool DocsUpdater::updateDocsXML()
|
||||
{
|
||||
QMutexLocker locker(&mtxAccess);
|
||||
|
||||
QString nameDocsFile = tasksAMMfileName; //кручу верчу запутать хочу!
|
||||
QString pathDocsFile = updateController->getPathAdditionalFile(nameDocsFile);
|
||||
QString pathDocsFile = updateController->getPathAdditionalFile(tasksAMMfileName);
|
||||
|
||||
QDomDocument docTasksDOM;
|
||||
if(! Tools::loadXMLtoDOM(pathDocsFile, &docTasksDOM))
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
#include <clienthandler.h>
|
||||
|
||||
ProcessingSystem::ProcessingSystem(ProviderDBLMS* providerDBLMS, UpdateController* updateController, DocsUpdater* docsUpdater, QObject *parent):
|
||||
ProcessingSystem::ProcessingSystem(ProviderDBLMS* providerDBLMS, UpdateController* updateController, DocsUpdater* docsUpdater, CfiController* cfiController, QObject *parent):
|
||||
QObject(parent),
|
||||
updateController(nullptr),
|
||||
docsUpdater(nullptr),
|
||||
cfiController(cfiController),
|
||||
providerDBLMS(nullptr)
|
||||
{
|
||||
this->providerDBLMS = providerDBLMS;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "chatsystem.h"
|
||||
#include "providerdblms.h"
|
||||
#include "docsupdater.h"
|
||||
#include "cficontroller.h"
|
||||
|
||||
class SendSystem;
|
||||
class ServerLMSWidget;
|
||||
@@ -20,12 +21,13 @@ class DataParser;
|
||||
class ClientHandler;
|
||||
class CommonClientHandler;
|
||||
class MultiThreadServer;
|
||||
class CfiController;
|
||||
|
||||
class ProcessingSystem : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProcessingSystem(ProviderDBLMS* providerDBLMS, UpdateController* updateController, DocsUpdater* docsUpdater, QObject *parent = nullptr);
|
||||
explicit ProcessingSystem(ProviderDBLMS* providerDBLMS, UpdateController* updateController, DocsUpdater* docsUpdater, CfiController* cfiController, QObject *parent = nullptr);
|
||||
|
||||
void initialize(MultiThreadServer *server,
|
||||
DataParser* dataParser,
|
||||
@@ -73,6 +75,7 @@ private:
|
||||
DataParser *dataParser;
|
||||
UpdateController *updateController;
|
||||
DocsUpdater* docsUpdater;
|
||||
CfiController* cfiController;
|
||||
ProviderDBLMS* providerDBLMS;
|
||||
ChatSystem *chatSystem;
|
||||
void sendTaskListToUnity(ClientHandler *client);
|
||||
|
||||
@@ -29,6 +29,7 @@ static const QString buildHashName = staticDataFolderName + "/buildHash.xml";
|
||||
static const QString buildDataPath = "/Application/" + projectFolderName + "/RRJ_Data/";
|
||||
static const QString tasksAMMfileName = "/docs.xml"; //"/tasksAmm.xml";
|
||||
static const QString tasksFIMfileName = "/tasksFIM.xml";
|
||||
static const QString cfiListFileName = "/CfiList.xml";
|
||||
//static const QString clientHash = staticDataFolderName + "/clientHash.xml";
|
||||
static const QString logFolderPath = "log";
|
||||
|
||||
|
||||
148
LibServer/cficontroller/cficontroller.cpp
Normal file
148
LibServer/cficontroller/cficontroller.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "cficontroller.h"
|
||||
|
||||
CfiController::CfiController(UpdateController* updateController, QObject *parent) :
|
||||
QObject(parent),
|
||||
updateController(updateController)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CfiController::~CfiController()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CfiController::lockAccessToCfiXML()
|
||||
{
|
||||
mtxAccess.lock();
|
||||
}
|
||||
|
||||
void CfiController::unLockAccessToCfiXML()
|
||||
{
|
||||
mtxAccess.unlock();
|
||||
}
|
||||
|
||||
bool CfiController::parsingCfiXML()
|
||||
{
|
||||
QMutexLocker locker(&mtxAccess);
|
||||
|
||||
if(!updateController)
|
||||
return false;
|
||||
|
||||
QString pathCfiListFile = updateController->getPathAdditionalFile(cfiListFileName);
|
||||
|
||||
QDomDocument docCfiListDOM;
|
||||
if(! Tools::loadXMLtoDOM(pathCfiListFile, &docCfiListDOM))
|
||||
return false;
|
||||
|
||||
QDomElement CFIObjectsElement = docCfiListDOM.firstChildElement("CFIObjects");
|
||||
if(CFIObjectsElement.isNull())
|
||||
return false;
|
||||
|
||||
listCfiObjects.clear();
|
||||
|
||||
//Parsing
|
||||
QDomElement oneCFIObjectElement = CFIObjectsElement.firstChildElement();
|
||||
if(oneCFIObjectElement.isNull())
|
||||
return true;
|
||||
|
||||
QString name;
|
||||
|
||||
do
|
||||
{
|
||||
name = oneCFIObjectElement.nodeName();
|
||||
|
||||
if(name == "CFIObject")
|
||||
{
|
||||
CfiObject cfiObj;
|
||||
|
||||
//атрибуты CFIObject
|
||||
QDomNamedNodeMap nodeMapOneCFIObject = oneCFIObjectElement.attributes();
|
||||
cfiObj.setId(nodeMapOneCFIObject.namedItem("id").nodeValue().toInt());
|
||||
cfiObj.setIsChanged(nodeMapOneCFIObject.namedItem("isChanged").nodeValue() == "True" ? true : false);
|
||||
|
||||
//childs CFIObject
|
||||
QDomElement cfiName_Element = oneCFIObjectElement.elementsByTagName("cfiName").at(0).toElement();
|
||||
cfiObj.setCfiName(cfiName_Element.text());
|
||||
QDomElement cfi_Element = oneCFIObjectElement.elementsByTagName("cfi").at(0).toElement();
|
||||
cfiObj.setCfi(cfi_Element.text());
|
||||
QDomElement zoneName_Element = oneCFIObjectElement.elementsByTagName("zoneName").at(0).toElement();
|
||||
cfiObj.setZoneName(zoneName_Element.text());
|
||||
QDomElement goName_Element = oneCFIObjectElement.elementsByTagName("goName").at(0).toElement();
|
||||
cfiObj.setGoName(goName_Element.text());
|
||||
|
||||
//child CFIObject : SetCameraPos
|
||||
SetCameraPos setCamPos;
|
||||
QDomElement setcamerapos_Element = oneCFIObjectElement.elementsByTagName("setcamerapos").at(0).toElement();
|
||||
//атрибуты setcamerapos
|
||||
QDomNamedNodeMap nodeMapSetcamerapos = setcamerapos_Element.attributes();
|
||||
setCamPos.set = nodeMapSetcamerapos.namedItem("set").nodeValue() == "True" ? true : false;
|
||||
setCamPos.fast = nodeMapSetcamerapos.namedItem("fast").nodeValue() == "True" ? true : false;
|
||||
setCamPos.specialPanel = nodeMapSetcamerapos.namedItem("specialPanel").nodeValue() == "True" ? true : false;
|
||||
|
||||
setCamPos.cameraMode = nodeMapSetcamerapos.namedItem("cameraMode").nodeValue();
|
||||
setCamPos.state = nodeMapSetcamerapos.namedItem("state").nodeValue();
|
||||
setCamPos.panelName = nodeMapSetcamerapos.namedItem("panelName").nodeValue();
|
||||
|
||||
setCamPos.prevCamOffsetMARposY = nodeMapSetcamerapos.namedItem("prevCamOffsetMARposY").nodeValue().toFloat();
|
||||
setCamPos.camOffsetMARposY = nodeMapSetcamerapos.namedItem("camOffsetMARposY").nodeValue().toFloat();
|
||||
setCamPos.prevCharControlerHeight = nodeMapSetcamerapos.namedItem("prevCharControlerHeight").nodeValue().toFloat();
|
||||
setCamPos.charControlerHeight = nodeMapSetcamerapos.namedItem("charControlerHeight").nodeValue().toFloat();
|
||||
setCamPos.FOV = nodeMapSetcamerapos.namedItem("FOV").nodeValue().toFloat();
|
||||
setCamPos.moveSpeed = nodeMapSetcamerapos.namedItem("moveSpeed").nodeValue().toFloat();
|
||||
setCamPos.projectionSize = nodeMapSetcamerapos.namedItem("projectionSize").nodeValue().toFloat();
|
||||
setCamPos.offset = nodeMapSetcamerapos.namedItem("offset").nodeValue().toFloat();
|
||||
setCamPos.prevPlayerRot = nodeMapSetcamerapos.namedItem("prevPlayerRot").nodeValue().toFloat();
|
||||
|
||||
//childs setcamerapos
|
||||
QDomElement loc_Element;
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("axesClamp").at(0).toElement();
|
||||
setCamPos.axesClamp = getXYfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("prevPlayerPos").at(0).toElement();
|
||||
setCamPos.prevPlayerPos = getXYZfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("playerPos").at(0).toElement();
|
||||
setCamPos.playerPos = getXYZfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("playerRot").at(0).toElement();
|
||||
setCamPos.playerRot = getXYZfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("prevCamRot").at(0).toElement();
|
||||
setCamPos.prevCamRot = getXYZfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("camRot").at(0).toElement();
|
||||
setCamPos.camRot = getXYZfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("goCenter").at(0).toElement();
|
||||
setCamPos.goCenter = getXYZfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("playerMARpos").at(0).toElement();
|
||||
setCamPos.playerMARpos = getXYZfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("playerMARrot").at(0).toElement();
|
||||
setCamPos.playerMARrot = getXYZfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("camMARrot").at(0).toElement();
|
||||
setCamPos.camMARrot = getXYZfromElement(loc_Element);
|
||||
loc_Element = setcamerapos_Element.elementsByTagName("playerPosRelativeToThePanel").at(0).toElement();
|
||||
setCamPos.playerPosRelativeToThePanel = getXYZfromElement(loc_Element);
|
||||
|
||||
cfiObj.setSetCameraPos(setCamPos);
|
||||
|
||||
listCfiObjects.append(cfiObj);
|
||||
}
|
||||
}while (! (oneCFIObjectElement = oneCFIObjectElement.nextSiblingElement()).isNull());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVector3D CfiController::getXYZfromElement(QDomElement element)
|
||||
{
|
||||
QVector3D vector3D;
|
||||
QDomNamedNodeMap nodeMap = element.attributes();
|
||||
vector3D.setX(nodeMap.namedItem("x").nodeValue().toFloat());
|
||||
vector3D.setY(nodeMap.namedItem("y").nodeValue().toFloat());
|
||||
vector3D.setZ(nodeMap.namedItem("z").nodeValue().toFloat());
|
||||
return vector3D;
|
||||
}
|
||||
|
||||
QVector2D CfiController::getXYfromElement(QDomElement element)
|
||||
{
|
||||
QVector2D vector2D;
|
||||
QDomNamedNodeMap nodeMap = element.attributes();
|
||||
vector2D.setX(nodeMap.namedItem("x").nodeValue().toFloat());
|
||||
vector2D.setY(nodeMap.namedItem("y").nodeValue().toFloat());
|
||||
return vector2D;
|
||||
}
|
||||
35
LibServer/cficontroller/cficontroller.h
Normal file
35
LibServer/cficontroller/cficontroller.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef CFICONTROLLER_H
|
||||
#define CFICONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include "updatecontroller.h"
|
||||
#include "cfiobject.h"
|
||||
|
||||
class CfiController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CfiController(UpdateController* updateController, QObject *parent = nullptr);
|
||||
~CfiController();
|
||||
public:
|
||||
void lockAccessToCfiXML();
|
||||
void unLockAccessToCfiXML();
|
||||
|
||||
bool parsingCfiXML();
|
||||
|
||||
signals:
|
||||
|
||||
private:
|
||||
QVector3D getXYZfromElement(QDomElement element);
|
||||
QVector2D getXYfromElement(QDomElement element);
|
||||
|
||||
private:
|
||||
UpdateController* updateController;
|
||||
|
||||
QMutex mtxAccess;
|
||||
|
||||
QList<CfiObject> listCfiObjects;
|
||||
};
|
||||
|
||||
#endif // CFICONTROLLER_H
|
||||
76
LibServer/cficontroller/cfiobject.cpp
Normal file
76
LibServer/cficontroller/cfiobject.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "cfiobject.h"
|
||||
|
||||
CfiObject::CfiObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int CfiObject::getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
void CfiObject::setId(int value)
|
||||
{
|
||||
id = value;
|
||||
}
|
||||
|
||||
QString CfiObject::getCfiName() const
|
||||
{
|
||||
return cfiName;
|
||||
}
|
||||
|
||||
void CfiObject::setCfiName(const QString &value)
|
||||
{
|
||||
cfiName = value;
|
||||
}
|
||||
|
||||
QString CfiObject::getCfi() const
|
||||
{
|
||||
return cfi;
|
||||
}
|
||||
|
||||
void CfiObject::setCfi(const QString &value)
|
||||
{
|
||||
cfi = value;
|
||||
}
|
||||
|
||||
QString CfiObject::getZoneName() const
|
||||
{
|
||||
return zoneName;
|
||||
}
|
||||
|
||||
void CfiObject::setZoneName(const QString &value)
|
||||
{
|
||||
zoneName = value;
|
||||
}
|
||||
|
||||
QString CfiObject::getGoName() const
|
||||
{
|
||||
return goName;
|
||||
}
|
||||
|
||||
void CfiObject::setGoName(const QString &value)
|
||||
{
|
||||
goName = value;
|
||||
}
|
||||
|
||||
SetCameraPos CfiObject::getSetCameraPos() const
|
||||
{
|
||||
return setCameraPos;
|
||||
}
|
||||
|
||||
void CfiObject::setSetCameraPos(const SetCameraPos &value)
|
||||
{
|
||||
setCameraPos = value;
|
||||
}
|
||||
|
||||
bool CfiObject::getIsChanged() const
|
||||
{
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
void CfiObject::setIsChanged(bool value)
|
||||
{
|
||||
isChanged = value;
|
||||
}
|
||||
78
LibServer/cficontroller/cfiobject.h
Normal file
78
LibServer/cficontroller/cfiobject.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#ifndef CFIOBJECT_H
|
||||
#define CFIOBJECT_H
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QVector2D>
|
||||
#include <QVector3D>
|
||||
|
||||
struct SetCameraPos
|
||||
{
|
||||
bool set;
|
||||
bool fast;
|
||||
bool specialPanel;
|
||||
QString cameraMode;
|
||||
QString state;
|
||||
QString panelName;
|
||||
float prevCamOffsetMARposY;
|
||||
float camOffsetMARposY;
|
||||
float prevCharControlerHeight;
|
||||
float charControlerHeight;
|
||||
float FOV;
|
||||
float moveSpeed;
|
||||
float projectionSize;
|
||||
float offset;
|
||||
float prevPlayerRot;
|
||||
|
||||
QVector2D axesClamp;
|
||||
|
||||
QVector3D prevPlayerPos;
|
||||
QVector3D playerPos;
|
||||
QVector3D playerRot;
|
||||
QVector3D prevCamRot;
|
||||
QVector3D camRot;
|
||||
QVector3D goCenter;
|
||||
QVector3D playerMARpos;
|
||||
QVector3D playerMARrot;
|
||||
QVector3D camMARrot;
|
||||
QVector3D playerPosRelativeToThePanel;
|
||||
};
|
||||
|
||||
class CfiObject
|
||||
{
|
||||
public:
|
||||
CfiObject();
|
||||
|
||||
int getId() const;
|
||||
void setId(int value);
|
||||
|
||||
QString getCfiName() const;
|
||||
void setCfiName(const QString &value);
|
||||
|
||||
QString getCfi() const;
|
||||
void setCfi(const QString &value);
|
||||
|
||||
QString getZoneName() const;
|
||||
void setZoneName(const QString &value);
|
||||
|
||||
QString getGoName() const;
|
||||
void setGoName(const QString &value);
|
||||
|
||||
SetCameraPos getSetCameraPos() const;
|
||||
void setSetCameraPos(const SetCameraPos &value);
|
||||
|
||||
bool getIsChanged() const;
|
||||
void setIsChanged(bool value);
|
||||
|
||||
private:
|
||||
int id;
|
||||
bool isChanged;
|
||||
QString cfiName;
|
||||
QString cfi;
|
||||
QString zoneName;
|
||||
QString goName;
|
||||
|
||||
SetCameraPos setCameraPos;
|
||||
};
|
||||
|
||||
#endif // CFIOBJECT_H
|
||||
@@ -28,6 +28,7 @@ ServerLMSWidget::ServerLMSWidget(QWidget *parent) :
|
||||
commonClientHandler(nullptr),
|
||||
chatSystem(nullptr),
|
||||
docsUpdater(nullptr),
|
||||
cfiController(nullptr),
|
||||
providerDBLMS(nullptr),
|
||||
first (true),
|
||||
language(languageENG),
|
||||
@@ -75,6 +76,7 @@ ServerLMSWidget::~ServerLMSWidget()
|
||||
delete dataParser;
|
||||
delete processingSystem;
|
||||
delete updateController;
|
||||
delete cfiController;
|
||||
delete docsUpdater;
|
||||
delete assetsManager;
|
||||
delete chatSystem;
|
||||
@@ -151,6 +153,21 @@ void ServerLMSWidget::slot_UpdateDocs()
|
||||
emit signal_DocsChanged();
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
//TODO для теста
|
||||
slot_ParsingCfiXML();
|
||||
}
|
||||
|
||||
void ServerLMSWidget::slot_ParsingCfiXML()
|
||||
{
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
if(cfiController->parsingCfiXML())
|
||||
{
|
||||
//TODO...что-то, наверное, должно происходить?
|
||||
}
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
void ServerLMSWidget::slot_startInitialization_step1()
|
||||
@@ -374,7 +391,10 @@ void ServerLMSWidget::startInitialization_step0()
|
||||
docsUpdater = new DocsUpdater(updateController/*, this*/);
|
||||
docsUpdater->moveToThread(updateThread);
|
||||
|
||||
processingSystem = new ProcessingSystem(providerDBLMS, updateController, docsUpdater);
|
||||
cfiController = new CfiController(updateController/*, this*/);
|
||||
cfiController->moveToThread(updateThread);
|
||||
|
||||
processingSystem = new ProcessingSystem(providerDBLMS, updateController, docsUpdater, cfiController);
|
||||
|
||||
dataParser = new DataParser(assetsManager, processingSystem);
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include "waitanimationwidget.h"
|
||||
#include "specialmessagebox.h"
|
||||
|
||||
#include "cficontroller.h"
|
||||
|
||||
|
||||
namespace Ui {
|
||||
class ServerLMSWidget;
|
||||
@@ -47,6 +49,7 @@ class MultiThreadServer;
|
||||
class AssetsManager;
|
||||
class ChatSystem;
|
||||
class DocsUpdater;
|
||||
class CfiController;
|
||||
|
||||
class SERVERLMS_EXPORT ServerLMSWidget : public QWidget
|
||||
{
|
||||
@@ -98,6 +101,8 @@ public slots:
|
||||
|
||||
void slot_UpdateDocs();
|
||||
|
||||
void slot_ParsingCfiXML();
|
||||
|
||||
void slot_startInitialization_step1();
|
||||
|
||||
void slot_setVersion(QString versionStr);
|
||||
@@ -176,6 +181,7 @@ private:
|
||||
ChatSystem *chatSystem;
|
||||
|
||||
DocsUpdater* docsUpdater;
|
||||
CfiController* cfiController;
|
||||
ProviderDBLMS* providerDBLMS;
|
||||
|
||||
bool first = true; // для тестов Unity
|
||||
|
||||
@@ -39,6 +39,7 @@ target_include_directories(ServerMTD PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../LibSe
|
||||
target_include_directories(ServerMTD PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../LibServer/multithreadserver)
|
||||
target_include_directories(ServerMTD PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../LibServer/providerdblms)
|
||||
target_include_directories(ServerMTD PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../LibServer/Systems)
|
||||
target_include_directories(ServerMTD PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../LibServer/cficontroller)
|
||||
if(PROJECT_TYPE_DEBUG)
|
||||
target_link_directories(ServerMTD PUBLIC ${REPO_PATH}/BUILDS/Debug64/LibServer)
|
||||
else()
|
||||
|
||||
Reference in New Issue
Block a user