Modificata struttura del dao per fare query sui model e non sulle
collection
This commit is contained in:
-272
@@ -1,272 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
## Funzioni utilizzate da build
|
||||
|
||||
#Stampa i comandi possibili
|
||||
function help() {
|
||||
printf "\n"
|
||||
printf "build [-h] -[ptl] -[DCcfs] [config_file]\n"
|
||||
printf "\n"
|
||||
printf "Specificare il config_file solo se non si desidera utilizzare quello di default.\n"
|
||||
printf "E' obbligatorio specificare l'environment. In caso di environment multipli sarà utilizzato solo quello a priorità maggiore.\n"
|
||||
printf "L'opzione default di database è -f (fix).\n"
|
||||
printf "\n"
|
||||
printf "Possibili valori di option\n"
|
||||
printf "\t -h questa schermata\n"
|
||||
printf "\n"
|
||||
printf "Lista environment (per deployare la configurazione) in ordine di priorità\n"
|
||||
printf "\t -p Produzione\n"
|
||||
printf "\t -t Test\n"
|
||||
printf "\t -l Locale\n"
|
||||
printf "\n"
|
||||
printf "Opzioni database\n"
|
||||
printf "\t -D Droppa tutto il database e lo rebuilda\n"
|
||||
printf "\t -C Droppa le tabelle di contenuti e le rebuilda\n"
|
||||
printf "\t -c Droppa le tabelle di configurazione e le rebuilda\n"
|
||||
printf "\t -f Aggiunge le tabelle mancanti\n"
|
||||
printf "\t -s Skip db fix\n"
|
||||
}
|
||||
|
||||
#Stampa il nome del comando in esecuzione
|
||||
function printcmd () {
|
||||
printf "*-----------------------------------------*\n"
|
||||
printf " PROJECT: $PROJECT_NAME\n"
|
||||
printf " EXECUTING: $1\n"
|
||||
printf "\n"
|
||||
printf "*-----------------------------------------*\n"
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Imposta come proprietario della cartella l'utente apache
|
||||
function chown_folder() {
|
||||
if [[ -z "$1" ]] || [[ "$1" == "/" ]] || [[ "$1" == "./" ]]; then
|
||||
exit 1
|
||||
else
|
||||
chown -R $APACHE2_USER:$APACHE2_GROUP "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
function svncleanup() {
|
||||
if [[ -z "$1" ]] || [[ "$1" == "/" ]] || [[ "$1" == "./" ]]; then
|
||||
exit 1
|
||||
else
|
||||
for i in `find "$1" -name .svn`;
|
||||
do
|
||||
printf "Deleting .. $i\n"
|
||||
rm -rf "$i"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Crea le cartelle di sistema
|
||||
function create_project_fs(){
|
||||
# CREATES APPLICATION PRIVATE FOLDER
|
||||
mkdir -p $APPLICATION_PRIVATE
|
||||
chown_folder $APPLICATION_PRIVATE
|
||||
|
||||
# CREATES APPLICATION PUBLIC FOLDER
|
||||
mkdir -p $APPLICATION_PUBLIC
|
||||
chown_folder $APPLICATION_PUBLIC
|
||||
|
||||
# CREATES APPLICATION STORAGE FOLDER
|
||||
mkdir -p $APPLICATION_STORAGE
|
||||
chown_folder $APPLICATION_STORAGE
|
||||
|
||||
# CREATES APPLICATION LOG FOLDER
|
||||
mkdir -p $APPLICATION_LOG_PATH
|
||||
chown_folder $APPLICATION_LOG_PATH
|
||||
|
||||
# CREATES XSENDFILE FOLDER
|
||||
mkdir -p $APPLICATION_XSENDFILE_PATH
|
||||
chown_folder $APPLICATION_XSENDFILE_PATH
|
||||
|
||||
# CREATES APPLICATION MEDIA FOLDER
|
||||
mkdir -p $APPLICATION_MEDIA_PATH
|
||||
chown_folder $APPLICATION_MEDIA_PATH
|
||||
|
||||
# CREATES APPLICATION BACKUP FOLDER
|
||||
mkdir -p $APPLICATION_BACKUPS_PATH
|
||||
chown_folder $APPLICATION_BACKUPS_PATH
|
||||
|
||||
# CREATES APPLICATION TEMPORARY FOLDER
|
||||
mkdir -p $APPLICATION_TMP_PATH
|
||||
chown_folder $APPLICATION_TMP_PATH
|
||||
}
|
||||
|
||||
|
||||
# Deploya i sorgenti
|
||||
function deploy(){
|
||||
echo "Deleting old build (Private)"
|
||||
rm -rf "$APPLICATION_PRIVATE/*"
|
||||
|
||||
echo "Deleting old build (Public)"
|
||||
rm -rf "$APPLICATION_PUBLIC/*"
|
||||
|
||||
|
||||
echo "Deploying new build (Private)"
|
||||
cp -a "./private/"* "$APPLICATION_PRIVATE"
|
||||
svncleanup "$APPLICATION_PRIVATE"
|
||||
chown_folder "$APPLICATION_PRIVATE"
|
||||
|
||||
echo "Deploying new build (Public)"
|
||||
cp -a "./public/"* "$APPLICATION_PUBLIC"
|
||||
svncleanup "$APPLICATION_PUBLIC"
|
||||
chown_folder "$APPLICATION_PUBLIC"
|
||||
|
||||
|
||||
chmod -R a+x "$APPLICATION_BINARY_PATH"
|
||||
chmod -R a+x "$APPLICATION_CRON_PATH"
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Deploya il progetto e mantiene solo la configurazione di produzione
|
||||
function deploy_production_env(){
|
||||
create_project_fs
|
||||
deploy
|
||||
if [[ -f "$APPLICATION_CONFIG_PATH/config.local.php" ]];then
|
||||
rm "$APPLICATION_CONFIG_PATH/config.local.php"
|
||||
fi
|
||||
if [[ -f "$APPLICATION_CONFIG_PATH/config.test.php" ]];then
|
||||
rm "$APPLICATION_CONFIG_PATH/config.test.php"
|
||||
fi
|
||||
}
|
||||
|
||||
# Deploya il progetto e mantiene solo la configurazione del server di test
|
||||
function deploy_test_env(){
|
||||
create_project_fs
|
||||
deploy
|
||||
if [[ -f "$APPLICATION_CONFIG_PATH/config.local.php" ]];then
|
||||
rm "$APPLICATION_CONFIG_PATH/config.local.php"
|
||||
fi
|
||||
if [[ -f "$APPLICATION_CONFIG_PATH/config.prod.php" ]];then
|
||||
rm "$APPLICATION_CONFIG_PATH/config.prod.php"
|
||||
fi
|
||||
}
|
||||
|
||||
# Deploya il progetto e mantiene solo la configurazione locale
|
||||
function deploy_local_env(){
|
||||
create_project_fs
|
||||
deploy
|
||||
if [[ -f "$APPLICATION_CONFIG_PATH/config.prod.php" ]];then
|
||||
rm "$APPLICATION_CONFIG_PATH/config.prod.php"
|
||||
fi
|
||||
if [[ -f "$APPLICATION_CONFIG_PATH/config.test.php" ]];then
|
||||
rm "$APPLICATION_CONFIG_PATH/config.test.php"
|
||||
fi
|
||||
}
|
||||
|
||||
function database_rebuild_content(){
|
||||
echo "Function not implemented... faking execution"
|
||||
for collection_name in "${!MONGO_DB_CONTENT_COLLECTIONS[@]}"; do
|
||||
echo "Rebuilding collection $collection_name - ${MONGO_DB_CONTENT_COLLECTIONS["$collection_name"]}";
|
||||
done
|
||||
|
||||
for table_name in "${!MYSQL_DB_CONTENT_TABLES[@]}"; do
|
||||
echo "Rebuilding table '$table_name' using query '${MYSQL_DB_CONTENT_TABLES["$table_name"]}'";
|
||||
done
|
||||
}
|
||||
|
||||
function database_rebuild_config(){
|
||||
echo "Function not implemented... faking execution"
|
||||
for collection_name in "${!MONGO_DB_CONFIG_COLLECTIONS[@]}"; do
|
||||
echo "Rebuilding collection $collection_name - ${MONGO_DB_CONFIG_COLLECTIONS["$collection_name"]}";
|
||||
done
|
||||
|
||||
for table_name in "${!MYSQL_DB_CONFIG_TABLES[@]}"; do
|
||||
echo "Rebuilding table '$table_name' using query '${MYSQL_DB_CONFIG_TABLES["$table_name"]}'";
|
||||
done
|
||||
}
|
||||
|
||||
function database_add_missing(){
|
||||
echo "Function not implemented... faking execution"
|
||||
for collection_name in "${!MONGO_DB_CONFIG_COLLECTIONS[@]}"; do
|
||||
echo "Checking collection $collection_name - ${MONGO_DB_CONFIG_COLLECTIONS["$collection_name"]}";
|
||||
done
|
||||
|
||||
for table_name in "${!MYSQL_DB_CONFIG_TABLES[@]}"; do
|
||||
echo "Checking table '$table_name' using query '${MYSQL_DB_CONFIG_TABLES["$table_name"]}'";
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Droppa e ricrea solo le tabelle dei dati
|
||||
function regenerate_db(){
|
||||
EXDIR=`pwd`
|
||||
cd "$APPLICATION_BINARY_PATH"
|
||||
|
||||
./createDatabase
|
||||
DBCONN=`./getDbConnectionString`
|
||||
|
||||
QUERY="CREATE TABLE campagna (id bigint auto_increment PRIMARY KEY, nome VARCHAR(64), owner bigint not null, creation_date TIMESTAMP default CURRENT_TIMESTAMP, current boolean default false, deleted boolean default false)"
|
||||
TABNAME="campagna"
|
||||
EXISTS=`echo "show tables" | $DBCONN | grep "$TABNAME" | wc -l`
|
||||
if [[ "$EXISTS" -gt 0 ]];then
|
||||
echo "Dropping table $TABNAME"
|
||||
echo "DROP TABLE $TABNAME" | $DBCONN
|
||||
fi
|
||||
echo "Executing query '$QUERY'"
|
||||
echo $QUERY | $DBCONN
|
||||
|
||||
QUERY="CREATE TABLE personaggio ( id bigint auto_increment PRIMARY KEY, deleted boolean default false, campagna bigint, active boolean default true, nome varchar(128), giocatore varchar(128), vita_cur smallint, vita_max smallint, forza tinyint unsigned, destrezza tinyint unsigned, costituzione tinyint unsigned, intelligenza tinyint unsigned, saggezza tinyint unsigned, carisma tinyint unsigned, ts_tempra smallint, ts_tempra_note varchar(128), ts_riflessi smallint, ts_riflessi_note varchar(128), ts_volonta smallint, ts_volonta_note varchar(128), ca_base tinyint, ca_contatto tinyint, ca_sprovvista tinyint, iniziativa int not null default 0, ascoltare int, ascoltare_note varchar(128), cercare int, cercare_note varchar(128), diplomazia int, diplomazia_note varchar(128), muov_sil int, muov_sil_note varchar(128), nascondersi int, nascondersi_note varchar(128), osservare int, osservare_note varchar(128), perc_intenz int, perc_intenz_note varchar(128), raggirare int, raggirare_note varchar(128), valutare int, valutare_note varchar(128), conoscenze varchar(256), lingue varchar(256), melee_atk_short varchar(512), melee_atk_complete varchar(512), ranged_atk_short varchar(512), ranged_atk_complete varchar(512), unarmed_atk_short varchar(512), unarmed_atk_complete varchar(512), resist_incant smallint default 0, combat_note varchar(512), details TEXT, INDEX(campagna) )"
|
||||
TABNAME="personaggio"
|
||||
EXISTS=`echo "show tables" | $DBCONN | grep "$TABNAME" | wc -l`
|
||||
if [[ "$EXISTS" -gt 0 ]];then
|
||||
echo "Dropping table $TABNAME"
|
||||
echo "DROP TABLE $TABNAME" | $DBCONN
|
||||
fi
|
||||
echo "Executing query '$QUERY'"
|
||||
echo $QUERY | $DBCONN
|
||||
|
||||
|
||||
QUERY="CREATE TABLE npc( id bigint auto_increment PRIMARY KEY, deleted boolean default false, nome varchar(128), tipo varchar(128), dadi_vita smallint, vita_max smallint, allineamento varchar(32), grado_sfida smallint, forza tinyint unsigned, destrezza tinyint unsigned, costituzione tinyint unsigned, intelligenza tinyint unsigned, saggezza tinyint unsigned, carisma tinyint unsigned, ts_tempra smallint, ts_tempra_note varchar(128), ts_riflessi smallint, ts_riflessi_note varchar(128), ts_volonta smallint, ts_volonta_note varchar(128), ca_base tinyint, ca_contatto tinyint, ca_sprovvista tinyint, iniziativa int not null default 0, ascoltare int, ascoltare_note varchar(128), cercare int, cercare_note varchar(128), muov_sil int, muov_sil_note varchar(128), nascondersi int, nascondersi_note varchar(128), osservare int, osservare_note varchar(128), perc_intenz int, perc_intenz_note varchar(128), raggirare int, raggirare_note varchar(128), valutare int, valutare_note varchar(128), conoscenze varchar(256), lingue varchar(256), melee_atk_short varchar(512), melee_atk_complete varchar(512), ranged_atk_short varchar(512), ranged_atk_complete varchar(512), unarmed_atk_short varchar(512), unarmed_atk_complete varchar(512), resist_incant smallint default 0, combat_note varchar(512), details TEXT )"
|
||||
TABNAME="npc"
|
||||
EXISTS=`echo "show tables" | $DBCONN | grep "$TABNAME" | wc -l`
|
||||
if [[ "$EXISTS" -gt 0 ]];then
|
||||
echo "Dropping table $TABNAME"
|
||||
echo "DROP TABLE $TABNAME" | $DBCONN
|
||||
fi
|
||||
echo "Executing query '$QUERY'"
|
||||
echo $QUERY | $DBCONN
|
||||
|
||||
cd "$EXDIR"
|
||||
}
|
||||
|
||||
# Droppa e ricrea le tabelle base (tipo admin e config)
|
||||
function regenerate_db_hard(){
|
||||
EXDIR=`pwd`
|
||||
cd "$APPLICATION_BINARY_PATH"
|
||||
|
||||
./createDatabase
|
||||
DBCONN=`./getDbConnectionString`
|
||||
|
||||
|
||||
QUERY="CREATE TABLE general_configuration (config_key varchar(128) primary key, config_value varchar(128), INDEX(config_key))"
|
||||
TABNAME="general_configuration"
|
||||
EXISTS=`echo "show tables" | $DBCONN | grep "$TABNAME" | wc -l`
|
||||
if [[ "$EXISTS" -gt 0 ]];then
|
||||
echo "Dropping table $TABNAME"
|
||||
echo "DROP TABLE $TABNAME" | $DBCONN
|
||||
fi
|
||||
echo "Executing query '$QUERY'"
|
||||
echo $QUERY | $DBCONN
|
||||
|
||||
|
||||
QUERY="CREATE TABLE user (id bigint auto_increment PRIMARY KEY, login varchar(64) not null, password varchar(64) not null, last_session_id varchar(64) default null, is_admin boolean not null default false, INDEX(login), INDEX(last_session_id))"
|
||||
TABNAME="user"
|
||||
EXISTS=`echo "show tables" | $DBCONN | grep "$TABNAME" | wc -l`
|
||||
if [[ "$EXISTS" -gt 0 ]];then
|
||||
echo "Dropping table $TABNAME"
|
||||
echo "DROP TABLE $TABNAME" | $DBCONN
|
||||
fi
|
||||
echo "Executing query '$QUERY'"
|
||||
echo $QUERY | $DBCONN
|
||||
|
||||
|
||||
cd "$EXDIR"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,16 +4,14 @@ Author: Riccardo Di Dato
|
||||
Creation Date: 23/dic/2015
|
||||
*/
|
||||
|
||||
class DaoSaveableDefaultImplementation implements DaoSaveable {
|
||||
private $collectionName;
|
||||
abstract class DaoSaveableDefaultImplementation implements DaoSaveable {
|
||||
private $saveableObject;
|
||||
|
||||
|
||||
public function __construct($collectionName, $saveableObject = null){
|
||||
public function __construct($saveableObject = null){
|
||||
if (is_null($saveableObject)){
|
||||
$saveableObject = new stdClass();
|
||||
}
|
||||
$this->collectionName = $collectionName;
|
||||
$this->updateFromSaveableObj($saveableObject);
|
||||
}
|
||||
|
||||
@@ -57,21 +55,12 @@ class DaoSaveableDefaultImplementation implements DaoSaveable {
|
||||
return $rval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see DaoSaveable::getCollectionName()
|
||||
*/
|
||||
public function getCollectionName(){
|
||||
return $this->collectionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see DaoSaveable::buildFromSaveableObj()
|
||||
*/
|
||||
public static function buildFromSaveableObj($collectionName, $saveableObject){
|
||||
return new static($collectionName, $saveableObject);
|
||||
public static function buildFromSaveableObj($saveableObject){
|
||||
return new static($saveableObject);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,14 +14,14 @@ interface DaoSaveable {
|
||||
/**
|
||||
* @return string La collection name dove salvare il model
|
||||
*/
|
||||
public function getCollectionName();
|
||||
public static function getCollectionName();
|
||||
|
||||
/**
|
||||
* Costruisce l'oggetto a partire dai dati raw di mongo
|
||||
* @param string $collectionName Nome della collezione da cui proviene l'oggetto
|
||||
* @param stdClass $obj dati raw di mongo
|
||||
*/
|
||||
public static function buildFromSaveableObj($collectionName, $obj);
|
||||
public static function buildFromSaveableObj($obj);
|
||||
|
||||
/**
|
||||
* Aggiorna i dati dell'oggetto partendo da un saveable object
|
||||
|
||||
@@ -5,11 +5,8 @@ Creation Date: 22/dic/2015
|
||||
*/
|
||||
|
||||
class GenericDao{
|
||||
private static $MODEL_REGISTRY = array();
|
||||
private $mongoObj;
|
||||
private $database;
|
||||
|
||||
private static $defaultReturnClass = "DaoSaveableDefaultImplementation";
|
||||
|
||||
/**
|
||||
* La connection string viene passata interamente a MongoClient, richiede formato 'mongo://server:port'.
|
||||
@@ -33,22 +30,21 @@ class GenericDao{
|
||||
|
||||
/**
|
||||
* Ritorna un array di model
|
||||
* @param string $collectionName nome della collezione
|
||||
* @param string $modelClass classe da ritornare (DaoSaveable)
|
||||
* @param array $filter array associativo della select formato mongo
|
||||
* @param array $options (al momento supporta limit e sort)
|
||||
* @param string $customReturnClass
|
||||
* @throws CoreException se la CustomReturnClass non è valida
|
||||
* @return DaoSaveable[]
|
||||
*/
|
||||
public function query($collectionName, array $filter = array(), array $options = array(), $customReturnClass = null){
|
||||
$retClass = self::$defaultReturnClass;
|
||||
if (!is_null($customReturnClass)){
|
||||
if (!is_subclass_of($customReturnClass, "DaoSaveable", true)){
|
||||
throw new CoreException("Invalid return class passed to GenericDao::query ($customReturnClass)");
|
||||
public function query($modelClass, array $filter = array(), array $options = array()){
|
||||
if (!is_null($modelClass)){
|
||||
if (!is_subclass_of($modelClass, "DaoSaveable", true)){
|
||||
throw new CoreException("Invalid return class passed to GenericDao::query ($modelClass)");
|
||||
}
|
||||
$retClass = $customReturnClass;
|
||||
}
|
||||
|
||||
$collectionName = $modelClass::getCollectionName();
|
||||
$collection = $this->database->$collectionName;
|
||||
|
||||
if (!array_key_exists("deleted",$filter)){
|
||||
@@ -66,7 +62,7 @@ class GenericDao{
|
||||
$rval = array();
|
||||
foreach ($cursor as $element){
|
||||
var_dump($element);
|
||||
$rval[] = $retClass::buildFromSaveableObj($collectionName, $element);
|
||||
$rval[] = $modelClass::buildFromSaveableObj($element);
|
||||
}
|
||||
|
||||
return $rval;
|
||||
@@ -101,30 +97,18 @@ class GenericDao{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imposta il tipo di oggetto restituito dalle query; la classe deve implementare l'interfaccia DaoSaveable.
|
||||
* Default DaoSaveableDefaultImplementation
|
||||
* @param string $retClass nome della classe
|
||||
* @throws CoreException se la classe non implementa l'interfaccia DaoSaveable
|
||||
*/
|
||||
public static function setDefaultReturnClass($retClass){
|
||||
if (!is_subclass_of($retClass, "DaoSaveable", true)){
|
||||
throw new CoreException("Invalid return class passed to GenericDao::setDefaultReturnClass ($retClass)");
|
||||
}
|
||||
self::$defaultReturnClass = $retClass;
|
||||
}
|
||||
|
||||
|
||||
public static function registerModel($model){
|
||||
if(is_subclass_of($model, "Model",true)){
|
||||
$collectionName = $model::getCollectionName();
|
||||
self::$MODEL_REGISTRY[$collectionName] = $model;
|
||||
// public static function registerModel($model){
|
||||
// if(is_subclass_of($model, "DaoSaveable",true)){
|
||||
// $collectionName = $model::getCollectionName();
|
||||
// self::$MODEL_REGISTRY[$collectionName] = $model;
|
||||
|
||||
}
|
||||
else{
|
||||
throw new CoreException("Invalid model passed to GenericDao::registerModel ($model)");
|
||||
}
|
||||
}
|
||||
// }
|
||||
// else{
|
||||
// throw new CoreException("Invalid model passed to GenericDao::registerModel ($model)");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -6,13 +6,6 @@ Creation Date: 22/dic/2015
|
||||
|
||||
abstract class Model extends DaoSaveableDefaultImplementation{
|
||||
|
||||
public function __construct($saveableObject){
|
||||
parent::__construct(static::getCollectionName(),$saveableObject);
|
||||
|
||||
}
|
||||
|
||||
abstract public static function getCollectionName();
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -14,7 +14,6 @@ require_once(dirname(__FILE__)."/GenericDao.php");
|
||||
require_once(dirname(__FILE__)."/Model.php");
|
||||
|
||||
require_once(dirname(__FILE__)."/models/NotiziaModel.php");
|
||||
GenericDao::registerModel("NotiziaModel");
|
||||
|
||||
$dao = new GenericDao($database->dbName,$database->connectionString);
|
||||
GlobalVariables::set("dao", $dao);
|
||||
|
||||
@@ -5,9 +5,11 @@ Creation Date: 07/gen/2016
|
||||
*/
|
||||
|
||||
class NotiziaModel extends Model{
|
||||
public function __construct($saveableObject = null){
|
||||
parent::__construct("notizia", $saveableObject);
|
||||
|
||||
public static function getCollectionName(){
|
||||
return "notizia";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -15,6 +15,4 @@ require_once(dirname(__FILE__)."/MediaRendererInterface.php");
|
||||
require_once(dirname(__FILE__)."/mediaRenderers/MediaHtml5.php");
|
||||
require_once(dirname(__FILE__)."/MediaHandler.php");
|
||||
|
||||
GenericDao::registerModel("MediaModel");
|
||||
|
||||
?>
|
||||
+1
-1
@@ -9,7 +9,7 @@ require_once(dirname(__FILE__)."/load.php");
|
||||
try {
|
||||
// var_dump($dao->query("notizia"));
|
||||
|
||||
$models = $dao->query("notizia");
|
||||
$models = $dao->query("NotiziaModel");
|
||||
$model = reset($models);
|
||||
|
||||
var_dump($model);
|
||||
|
||||
Reference in New Issue
Block a user