✨ feat(website_view): Ajoute le contrôle et gestion des noms de domaine.
Ajoute l'onglet de contrôle et gestion des noms de domaine, incluant l'ajout manuel, la suppression et l'envoi d'instructions DNS.
This commit is contained in:
32
migrations/Version20251112141800.php
Normal file
32
migrations/Version20251112141800.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20251112141800 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE website_dns ADD type VARCHAR(255) DEFAULT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE website_dns DROP type');
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Controller\Artemis\EsyWeb;
|
||||
|
||||
use App\Entity\CustomerDns;
|
||||
use App\Entity\EsyWeb\Website;
|
||||
use App\Entity\EsyWeb\WebsiteDates;
|
||||
use App\Entity\EsyWeb\WebsiteDns;
|
||||
@@ -13,6 +14,7 @@ use App\Repository\EsyWebTutoRepository;
|
||||
use App\Service\Cloudflare\Client;
|
||||
use App\Service\License\LicenseManager;
|
||||
use App\Service\Logger\LoggerService;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\Vault\VaultClient;
|
||||
use App\Service\Website\EventCancelWebsite;
|
||||
use App\Service\Website\EventCreatedWebsite;
|
||||
@@ -113,16 +115,105 @@ class EsyWebController extends AbstractController
|
||||
]);
|
||||
}
|
||||
#[Route(path: '/artemis/esyweb/website/{id}', name: 'artemis_esyweb_view', methods: ['GET', 'POST'])]
|
||||
public function websiteView(?Website $website,VaultClient $vaultClient,LicenseManager $licenseManager,LoggerService $loggerService,Request $request,EntityManagerInterface $entityManager,WebsiteRepository $websiteRepository)
|
||||
public function websiteView(?Website $website,Mailer $mailer,VaultClient $vaultClient,LicenseManager $licenseManager,LoggerService $loggerService,Request $request,EntityManagerInterface $entityManager,WebsiteRepository $websiteRepository)
|
||||
{
|
||||
if(is_null($website)) {
|
||||
return $this->redirectToRoute('artemis_esyweb');
|
||||
}
|
||||
|
||||
if($request->query->has('idSendDns')) {
|
||||
$ndd = $entityManager->getRepository(WebsiteDns::class)->find($request->query->get('idSendDns'));
|
||||
$customer = $website->getCustomer();
|
||||
|
||||
$mailer->send($customer->mainContact()->getEmail(),$customer->getRaisonSocial(),"[SARL SITECONSEIL] - Configuration de nom de domaine ".$ndd->getDns(),"mails/esyWeb/dns_configuration.twig",[
|
||||
'ipServer' => $website->getServer()->getExternalIp(),
|
||||
'ndd' => $ndd,
|
||||
'customer' => $customer,
|
||||
]);
|
||||
return $this->redirectToRoute('artemis_esyweb_view',['id'=>$website->getId(),'current'=>'ndd']);
|
||||
|
||||
|
||||
}
|
||||
if($request->query->has('idNddDelete')) {
|
||||
$ndd = $entityManager->getRepository(WebsiteDns::class)->find($request->query->get('idNddDelete'));
|
||||
$entityManager->remove($ndd);
|
||||
$entityManager->flush();
|
||||
return $this->redirectToRoute('artemis_esyweb_view',['id'=>$website->getId(),'current'=>'ndd']);
|
||||
|
||||
}
|
||||
if($request->isMethod('POST')) {
|
||||
if($request->request->has('dns_manual')) {
|
||||
$dnsManual = $request->request->get('dns_manual');
|
||||
$isValidDomain = filter_var(
|
||||
$dnsManual,
|
||||
FILTER_VALIDATE_DOMAIN,
|
||||
FILTER_FLAG_HOSTNAME
|
||||
);
|
||||
if ($isValidDomain === false) {
|
||||
$this->addFlash("error","ERREUR : '$dnsManual' n'est pas un nom de domaine valide.");
|
||||
return $this->redirectToRoute('artemis_esyweb_view',['id'=>$website->getId(),'current'=>'ndd']);
|
||||
}
|
||||
$parts = explode('.', trim($dnsManual, '.')); // explode pour compter les segments
|
||||
$numParts = count($parts);
|
||||
$valid = false;
|
||||
$type = "sub";
|
||||
if ($numParts >= 3) {
|
||||
$valid = true;
|
||||
} elseif ($numParts === 2) {
|
||||
$valid = true;
|
||||
$type = "fqdn";
|
||||
} else {
|
||||
$this->addFlash("error","ERREUR : '$dnsManual' n'est pas un nom de domaine valide.");
|
||||
return $this->redirectToRoute('artemis_esyweb_view',['id'=>$website->getId(),'current'=>'ndd']);
|
||||
}
|
||||
if($valid) {
|
||||
$search = $entityManager->getRepository(WebsiteDns::class)->findOneBy(['dns'=>$dnsManual]);
|
||||
if($search instanceof WebsiteDns) {
|
||||
$this->addFlash("error","ERREUR : '$dnsManual' est déja utilisée !.");
|
||||
return $this->redirectToRoute('artemis_esyweb_view',['id'=>$website->getId(),'current'=>'ndd']);
|
||||
}
|
||||
$web = new WebsiteDns();
|
||||
$web->setDns($dnsManual);
|
||||
$web->setWebsite($website);
|
||||
$web->setIsMain(false);
|
||||
$web->setType($type);
|
||||
$entityManager->persist($web);
|
||||
$entityManager->flush();
|
||||
return $this->redirectToRoute('artemis_esyweb_view',['id'=>$website->getId(),'current'=>'ndd']);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$dnsList =[];
|
||||
foreach ($website->getWebsiteDns() as $websiteDn) {
|
||||
$websiteDn->isPointingCorrectly = false;
|
||||
$websiteDn->pointingIp = "";
|
||||
if($websiteDn->getCustomerDns() instanceof CustomerDns) {
|
||||
$websiteDn->isPointingCorrectly = true;
|
||||
$websiteDn->pointingIp = "EsyWeb";
|
||||
} else if(str_contains($websiteDn->getDns(),".esy-web.dev")) {
|
||||
$websiteDn->isPointingCorrectly = true;
|
||||
$websiteDn->pointingIp = "EsyWeb";
|
||||
} else {
|
||||
$currentIp = gethostbyname($websiteDn->getDns());
|
||||
if($currentIp == $website->getServer()->getExternalIp()) {
|
||||
$websiteDn->isPointingCorrectly = true;
|
||||
$websiteDn->pointingIp = "EsyWeb";
|
||||
} else {
|
||||
$websiteDn->isPointingCorrectly = false;
|
||||
$websiteDn->pointingIp = $currentIp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$dnsList[] = $websiteDn;
|
||||
}
|
||||
$loggerService->log("VIEW","Affiche la page de site internet - ".$website->getTitle(),$this->getUser());
|
||||
return $this->render('artemis/esyweb/website_view.twig', [
|
||||
'current' => $request->get('current','main'),
|
||||
'website' => $website
|
||||
'website' => $website,
|
||||
'dnsList' => $dnsList,
|
||||
]);
|
||||
}
|
||||
#[Route(path: '/artemis/esyweb/website/add', name: 'artemis_esyweb_add', methods: ['GET', 'POST'],priority: 5)]
|
||||
@@ -149,6 +240,7 @@ class EsyWebController extends AbstractController
|
||||
|
||||
$websiteDns = new WebsiteDns();
|
||||
$websiteDns->setWebsite($website);
|
||||
$websiteDns->setType("sub");
|
||||
$websiteDns->setIsMain(true);
|
||||
$websiteDns->setDns($slug->slugify($website->getTitle()).".esy-web.dev");
|
||||
$entityManager->persist($websiteDns);
|
||||
|
||||
@@ -26,6 +26,9 @@ class WebsiteDns
|
||||
#[ORM\OneToOne(inversedBy: 'websiteDns', cascade: ['persist', 'remove'])]
|
||||
private ?CustomerDns $customerDns = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $type = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -78,4 +81,16 @@ class WebsiteDns
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setType(?string $type): static
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,14 @@
|
||||
{{ website.uuid | default('N/A') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{# UUID #}
|
||||
<div class="border-l-4 border-teal-500 pl-4">
|
||||
<p class="text-sm font-medium text-gray-400">Nom de domaine principale</p>
|
||||
<p class="text-lg font-mono text-gray-200 truncate" title="{{ website.mainDns | default('UUID non disponible') }}">
|
||||
{{ website.mainDns | default('N/A') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class="mb-8 bg-gray-800 p-6 rounded-lg shadow-xl">
|
||||
|
||||
135
templates/artemis/esyweb/website/ndd.twig
Normal file
135
templates/artemis/esyweb/website/ndd.twig
Normal file
@@ -0,0 +1,135 @@
|
||||
<section class="mt-8 bg-gray-800 p-6 rounded-lg shadow-xl">
|
||||
<h2 class="text-2xl font-bold text-indigo-400 mb-4 border-b border-gray-700 pb-2">🔗 Noms de Domaine Rattachés</h2>
|
||||
|
||||
{% if dnsList is empty %}
|
||||
<p class="text-gray-400 italic">Aucun nom de domaine n'est rattaché à ce site web.</p>
|
||||
{% else %}
|
||||
<ul class="space-y-4">
|
||||
{% for dns in dnsList %}
|
||||
{% set pointingOk = dns.isPointingCorrectly | default(false) %}
|
||||
{% set pointingStatusClass = pointingOk ? 'bg-green-600/50 text-green-300' : 'bg-red-600/50 text-red-300' %}
|
||||
|
||||
<li class="p-4 border border-gray-700 rounded-lg flex flex-col sm:flex-row justify-between items-start sm:items-center hover:bg-gray-700/50 transition duration-200">
|
||||
|
||||
<div class="flex items-center space-x-4 mb-3 sm:mb-0">
|
||||
|
||||
{# Statut Principal #}
|
||||
{% if dns.isMain %}
|
||||
<span class="px-3 py-1 text-xs font-bold bg-indigo-600 rounded-full text-white shadow-md" title="Nom de domaine principal du site">
|
||||
Principal
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{# STATUT DE POINTAGE (NOUVEAU) #}
|
||||
<span class="px-3 py-1 text-xs font-bold rounded-full {{ pointingStatusClass }}" title="Vérification du pointage DNS">
|
||||
Pointage: {{ pointingOk ? 'OK' : 'KO' }} (Ip : {{ dns.pointingIp }})
|
||||
</span>
|
||||
|
||||
{# Affichage du Nom de Domaine #}
|
||||
<div class="text-lg font-semibold text-gray-100">
|
||||
{{ dns.dns | default('Nom de domaine inconnu') }}
|
||||
|
||||
{# LOGIQUE DE DÉTERMINATION AUTOMATIQUE DU FQDN #}
|
||||
{% if dns.type == 'fqdn' %}
|
||||
<span class="ml-2 text-sm text-gray-400 italic">(gère www et sans www)</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Actions et Liens #}
|
||||
<div class="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-4 text-sm">
|
||||
|
||||
{% if dns.customerDns is not null %}
|
||||
{# Lien vers la fiche client du NDD #}
|
||||
<a target="_blank" href="{{ path('artemis_intranet_customer_view',{id:dns.customerDns.customer.id,current:'dns'}) }}" class="text-cyan-400 hover:text-cyan-300 inline-flex items-center">
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path></svg>
|
||||
Voir le nom de domaine
|
||||
</a>
|
||||
{% elseif pointingOk == false %}
|
||||
{# ACTION 2: Bouton "Envoyer Instructions" si le NDD est externe #}
|
||||
<a href="{{ path('artemis_esyweb_view', {id: website.id,'current':'ndd',idSendDns:dns.id}) }}" class="px-3 py-1 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition duration-300 font-semibold inline-flex items-center">
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26c.49.33 1.11.33 1.6 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg>
|
||||
Envoyer Instructions DNS
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{# LOGIQUE DE SUPPRESSION : Masquer si c'est le domaine principal #}
|
||||
{% if not dns.isMain %}
|
||||
{# Lien de Suppression #}
|
||||
<a href="{{ path('artemis_esyweb_view',{id:website.id,current:'ndd',idNddDelete:dns.id}) }}" class="text-red-400 hover:text-red-300 inline-flex items-center">
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
|
||||
Supprimer
|
||||
</a>
|
||||
{% else %}
|
||||
{# Badge si suppression impossible #}
|
||||
<span class="px-3 py-1 text-xs font-semibold bg-gray-700 rounded text-gray-400 cursor-not-allowed" title="Le nom de domaine principal ne peut pas être supprimé.">
|
||||
Non Supprimable
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% if website.state != "validate" %}
|
||||
<section class="mt-8 mb-8 p-6 bg-gray-800 rounded-lg shadow-xl">
|
||||
<h2 class="text-2xl font-bold text-teal-400 mb-4 border-b border-gray-700 pb-2">➕ Ajouter un Nom de Domaine</h2>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
|
||||
{# Colonne 1 : Ajout Manuel #}
|
||||
<div class="p-4 border border-gray-700 rounded-lg bg-gray-700/50">
|
||||
<h3 class="text-xl font-semibold text-gray-200 mb-3 flex items-center">
|
||||
<span class="text-2xl mr-2">✍️</span> Ajout Manuel
|
||||
</h3>
|
||||
<p class="text-sm text-gray-400 mb-4">
|
||||
Liez un nom de domaine existant à ce site web.
|
||||
</p>
|
||||
|
||||
<form method="POST" class="space-y-4">
|
||||
<div>
|
||||
<label for="dns_manual" class="block text-sm font-medium text-gray-400 mb-1">Nom de domaine</label>
|
||||
<input
|
||||
type="text"
|
||||
name="dns_manual"
|
||||
id="dns_manual"
|
||||
required
|
||||
placeholder="Ex: mon-nouveaudomaine.com"
|
||||
class="w-full px-4 py-2 rounded-lg bg-gray-600 text-gray-100 border border-gray-500 focus:border-teal-500 focus:ring-teal-500"
|
||||
>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full px-4 py-2 bg-teal-600 text-white font-semibold rounded-lg hover:bg-teal-700 transition duration-300 shadow-md"
|
||||
>
|
||||
Lier le Domaine
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{# Colonne 2 : Ajout Automatique #}
|
||||
<div class="p-4 border border-gray-700 rounded-lg bg-gray-700/50">
|
||||
<h3 class="text-xl font-semibold text-gray-200 mb-3 flex items-center">
|
||||
<span class="text-2xl mr-2">🤖</span> Ajout Automatique
|
||||
</h3>
|
||||
<p class="text-sm text-gray-400 mb-4">
|
||||
Cette fonction gère l'enregistrement et la configuration DNS (vide pour le moment).
|
||||
</p>
|
||||
|
||||
<div class="text-center p-6 border-2 border-dashed border-gray-600 rounded-lg">
|
||||
<p class="text-gray-500 italic">
|
||||
Fonctionnalité d'automatisation en cours de développement.
|
||||
</p>
|
||||
{# Un bouton ou un champ de recherche pourrait être ajouté ici lors de l'implémentation #}
|
||||
</div>
|
||||
|
||||
<form method="POST" action="VOTRE_ROUTE_AJOUT_AUTOMATIQUE" class="mt-4 hidden">
|
||||
{# Champ(s) caché(s) ou futur(s) pour l'ajout automatique #}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
@@ -14,9 +14,6 @@
|
||||
<a target="_blank" href="https://{{ website.mainDns }}" class="px-4 py-2 bg-indigo-600 text-white font-medium rounded-md shadow-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
Voir le site internet
|
||||
</a>
|
||||
<a href="" class="px-4 py-2 bg-indigo-600 text-white font-medium rounded-md shadow-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
Contrôle site internet
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex space-x-4 mb-6 border-b border-gray-700">
|
||||
@@ -38,6 +35,10 @@
|
||||
<i class="fad fa-home"></i>
|
||||
Clé du site internet
|
||||
</a>
|
||||
<a href="{{ path('artemis_esyweb_view',{id:website.id,current:'key'}) }}" class="px-4 py-2 font-semibold {% if current == "ndd" %}{{ active }}{% else %}{{ desactive }}{% endif %}">
|
||||
<i class="fad fa-cog"></i>
|
||||
Contrôle du site internet
|
||||
</a>
|
||||
</div>
|
||||
{% include 'artemis/esyweb/website/'~current~".twig" %}
|
||||
|
||||
|
||||
66
templates/mails/esyWeb/dns_configuration.twig
Normal file
66
templates/mails/esyWeb/dns_configuration.twig
Normal file
@@ -0,0 +1,66 @@
|
||||
{% extends 'mails/base.twig' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<mj-section background-color="#4F46E5" padding="20px 0">
|
||||
<mj-column>
|
||||
<mj-text align="center" font-size="24px" color="#ffffff" font-weight="bold" padding="10px 25px">
|
||||
Instructions de Configuration DNS
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
<mj-section background-color="#ffffff" padding="30px 20px 20px 20px">
|
||||
<mj-column>
|
||||
|
||||
<mj-text font-size="18px" font-weight="bold" padding-bottom="10px">
|
||||
Bonjour,
|
||||
</mj-text>
|
||||
|
||||
<mj-text padding-bottom="20px">
|
||||
Veuillez trouver ci-dessous les instructions pour configurer le nom de domaine : <strong style="color: #4F46E5;">{{ datas.ndd.dns }}</strong>.
|
||||
</mj-text>
|
||||
|
||||
<mj-text padding-bottom="20px">
|
||||
Pour que votre site fonctionne correctement, vous devez faire pointer ce domaine vers l'adresse IP de notre serveur.
|
||||
</mj-text>
|
||||
|
||||
<mj-text font-size="16px" font-weight="bold" padding="10px 0 5px 0" color="#1F2937">
|
||||
Adresse IP cible :
|
||||
</mj-text>
|
||||
<mj-text background-color="#E5E7EB" color="#1F2937" padding="10px 20px" border-radius="4px" font-weight="bold" font-family="Courier New, monospace" font-size="18px" align="center">
|
||||
{{ datas.ipServer }}
|
||||
</mj-text>
|
||||
|
||||
<mj-text font-size="16px" font-weight="bold" padding="25px 0 10px 0" color="#1F2937">
|
||||
Action à effectuer chez votre registrar :
|
||||
</mj-text>
|
||||
<mj-text>
|
||||
Créez (ou modifiez) un enregistrement de type <strong style="color: #EF4444;">A</strong> qui pointe vers l'adresse ci-dessus.
|
||||
</mj-text>
|
||||
|
||||
{% if datas.ndd.type == "fqdn" %}
|
||||
<mj-text padding="20px 0 10px 0" font-weight="bold" color="#F59E0B">
|
||||
⚠️ Attention (FQDN)
|
||||
</mj-text>
|
||||
<mj-text>
|
||||
Puisque ce domaine est de type FQDN, pensez à configurer l'enregistrement <strong style="color: #EF4444;">A</strong> pour les deux versions :
|
||||
<ul>
|
||||
<li><strong style="color: #F59E0B;">@</strong> (domaine sans www)</li>
|
||||
<li><strong style="color: #F59E0B;">www</strong> (sous-domaine www)</li>
|
||||
</ul>
|
||||
Les deux doivent pointer vers l'adresse <strong style="color: #4F46E5;">{{ datas.ipServer }}</strong>.
|
||||
</mj-text>
|
||||
{% endif %}
|
||||
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
<mj-section background-color="#f4f4f4" padding="20px 0">
|
||||
<mj-column>
|
||||
<mj-text align="center" font-size="12px" color="#777777">
|
||||
Ceci est un message automatique. Merci de ne pas y répondre.
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user