added example for developpers module
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
name: Events Example
|
||||
type: module
|
||||
description: Provides an example of subscribing to and dispatching events.
|
||||
package: Example modules
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- examples:examples
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-12-17
|
||||
version: '8.x-1.x-dev'
|
||||
core: '8.x'
|
||||
project: 'examples'
|
||||
datestamp: 1513537386
|
||||
@@ -0,0 +1,4 @@
|
||||
events_example.description:
|
||||
title: 'Events Example'
|
||||
description: 'Example of dispatching and subscribing to events.'
|
||||
route_name: events_example.description
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Demonstrates how to subscribe to and dispatch events.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup events_example Example: Events
|
||||
* @ingroup examples
|
||||
* @{
|
||||
* Demonstrates subscribing to, and dispatching, events.
|
||||
*
|
||||
* Events allow for different components of the system to interact and
|
||||
* communicate with each other. Modules can either dispatch events, subscribe to
|
||||
* events, or both.
|
||||
*
|
||||
* Subscribing to an event allows a module to declare that it would like to be
|
||||
* notified anytime a specific event happens. A module can subscribe to any
|
||||
* number of events, and can even subscribe to the same event more than once.
|
||||
*
|
||||
* Dispatching an event allows a module, or Drupal core subsystem, to notify any
|
||||
* registered subscribers that a specific event has just taken place. When an
|
||||
* event is dispatched the registered code for each subscriber is executed. For
|
||||
* example, whenever a configuration entity is updated the Configuration API
|
||||
* dispatches a new event allowing all subscribers to react to the change.
|
||||
*
|
||||
* This allows modules to extend other systems without the need to modify the
|
||||
* original code.
|
||||
*
|
||||
* Each event has a unique string name. This string is often referred to as "the
|
||||
* event", or "the event name". This string is how you identify which event(s)
|
||||
* you are interested in. A complete list of events dispatched by core is
|
||||
* available at
|
||||
* https://api.drupal.org/api/drupal/core%21core.api.php/group/events/
|
||||
*
|
||||
* Drupal's event system is an extension of the
|
||||
* @link http://symfony.com/doc/current/components/event_dispatcher.html Symfony
|
||||
* EventDispatcher component @endlink, and implements the Mediator pattern.
|
||||
*
|
||||
* Subscribing to an event requires:
|
||||
* - Defining a service in your module, tagged with 'event_subscriber'.
|
||||
* - Defining a class for your subscriber service that implements
|
||||
* \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
* - Using the getSubscribedEvents method to return a list of the events you
|
||||
* want to subscribe to, and which methods on the class should be called for
|
||||
* each one.
|
||||
*
|
||||
* For an example of subscribing to an event see the events_example.services.yml
|
||||
* file. And the \Drupal\events_example\EventSubscriber\EventsExampleSubscriber
|
||||
* class.
|
||||
*
|
||||
* Dispatching an event requires:
|
||||
* - Defining a new static class with constants for unique event names and
|
||||
* documentation. Example: \Drupal\events_exampl\Event\IncidentEvents
|
||||
* - Defining an event class that extends
|
||||
* \Symfony\Component\EventDispatcher\Event
|
||||
* Example: \Drupal\events_example\Event\IncidentReportEvent
|
||||
* - Use the 'event_dispatcher' service in your code to dispatch an event and
|
||||
* provide an event object as an argument. Example:
|
||||
* \Drupal\events_example\Form\EventsExampleForm::submitForm()
|
||||
*
|
||||
* This example code is based off of the article @link
|
||||
* https://drupalize.me/blog/201502/responding-events-drupal-8 Responding to
|
||||
* Events in Drupal 8 @endlink.
|
||||
*
|
||||
* @see events
|
||||
* @see \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
* @see \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher
|
||||
* @see service_tag
|
||||
*/
|
||||
|
||||
/**
|
||||
* @} End of "defgroup events_example".
|
||||
*/
|
||||
@@ -0,0 +1,8 @@
|
||||
# Define a route for our event dispatching example.
|
||||
events_example.description:
|
||||
path: 'examples/events-example'
|
||||
defaults:
|
||||
_form: '\Drupal\events_example\Form\EventsExampleForm'
|
||||
_title: 'Events Example'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
@@ -0,0 +1,16 @@
|
||||
# Subscribing to an event requires you to create a new service tagged with the
|
||||
# 'event_subscriber' tag. This tells the service container, and by proxy the
|
||||
# event dispatcher service, that the class registered here can be queried to get
|
||||
# a list of events that it would like to be notified about.
|
||||
#
|
||||
# For more on defining and tagging services see
|
||||
# https://api.drupal.org/api/drupal/core%21core.api.php/group/container/8.2.x
|
||||
services:
|
||||
# Give your service a unique name, convention is to prefix service names with
|
||||
# the name of the module that implements them.
|
||||
events_example_subscriber:
|
||||
# Point to the class that will contain your implementation of
|
||||
# \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
class: Drupal\events_example\EventSubscriber\EventsExampleSubscriber
|
||||
tags:
|
||||
- {name: event_subscriber}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\events_example\Event;
|
||||
|
||||
/**
|
||||
* Defines events for the events_example module.
|
||||
*
|
||||
* It is best practice define the unique names for events as constants on a
|
||||
* class. This provides a place for documentation of the events. As well as
|
||||
* allowing the event dispatcher to use the constants instead of hard coding a
|
||||
* string.
|
||||
*
|
||||
* In this example we're defining one new event:
|
||||
* 'events_example.new_incident_report'. This event will be dispatched by the
|
||||
* form controller \Drupal\events_example\Form\EventsExampleForm whenever a new
|
||||
* incident is reported. If your application dispatches more than one event
|
||||
* you can use a single class to document multiple events. Just add a new
|
||||
* constant for each. Group related events together with a single class, define
|
||||
* another class for unrelated events.
|
||||
*
|
||||
* The docblock for each event constant should contain an "@Event" tag. This is
|
||||
* used to ensure documentation parsing tools can gather and list all events.
|
||||
* For example,
|
||||
* https://api.drupal.org/api/drupal/core%21core.api.php/group/events/
|
||||
*
|
||||
* The docblock should also contain a description of when, and
|
||||
* under what conditions, the event is triggered. A module developer should be
|
||||
* able to read this description in order to determine whether or not this is
|
||||
* the event that they want to subscribe to.
|
||||
*
|
||||
* This class is declared as final so that it can not be extended. It should
|
||||
* only ever be used to provide unique event names, and documentation.
|
||||
*
|
||||
* In core \Drupal\Core\Config\ConfigCrudEvent is a good example of defining and
|
||||
* documenting new events.
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigCrudEvent
|
||||
*
|
||||
* @ingroup events_example
|
||||
*/
|
||||
final class IncidentEvents {
|
||||
|
||||
/**
|
||||
* Name of the event fired when a new incident is reported.
|
||||
*
|
||||
* This event allows modules to perform an action whenever a new incident is
|
||||
* reported via the incident report form. The event listener method receives a
|
||||
* \Drupal\events_example\Event\IncidentReportEvent instance.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\events_example\Event\IncidentReportEvent
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const NEW_REPORT = 'events_example.new_incident_report';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\events_example\Event;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Wraps a incident report event for event subscribers.
|
||||
*
|
||||
* Whenever there is additional contextual data that you want to provide to the
|
||||
* event subscribers when dispatching an event you should create a new class
|
||||
* that extends \Symfony\Component\EventDispatcher\Event.
|
||||
*
|
||||
* See \Drupal\Core\Config\ConfigCrudEvent for an example of this in core.
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigCrudEvent
|
||||
*
|
||||
* @ingroup events_example
|
||||
*/
|
||||
class IncidentReportEvent extends Event {
|
||||
|
||||
/**
|
||||
* Incident type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Detailed incident report.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $report;
|
||||
|
||||
/**
|
||||
* Constructs an incident report event object.
|
||||
*
|
||||
* @param string $type
|
||||
* The incident report type.
|
||||
* @param string $report
|
||||
* A detailed description of the incident provided by the reporter.
|
||||
*/
|
||||
public function __construct($type, $report) {
|
||||
$this->type = $type;
|
||||
$this->report = $report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the incident type.
|
||||
*
|
||||
* @return string
|
||||
* The type of report.
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the detailed incident report.
|
||||
*
|
||||
* @return string
|
||||
* The text of the report.
|
||||
*/
|
||||
public function getReport() {
|
||||
return $this->report;
|
||||
}
|
||||
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\events_example\EventSubscriber;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\events_example\Event\IncidentEvents;
|
||||
use Drupal\events_example\Event\IncidentReportEvent;
|
||||
|
||||
/**
|
||||
* Subscribe to IncidentEvents::NEW_REPORT events and react to new reports.
|
||||
*
|
||||
* In this example we subscribe to all IncidentEvents::NEW_REPORT events and
|
||||
* point to two different methods to execute when the event is triggered. In
|
||||
* each method we have some custom logic that determines if we want to react to
|
||||
* the event by examining the event object, and the displaying a message to the
|
||||
* user indicating whether or not that method reacted to the event.
|
||||
*
|
||||
* By convention, classes subscribing to an event live in the
|
||||
* Drupal/{module_name}/EventSubscriber namespace.
|
||||
*
|
||||
* @ingroup events_example
|
||||
*/
|
||||
class EventsExampleSubscriber implements EventSubscriberInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Return an array of events that you want to subscribe to mapped to the
|
||||
// method on this class that you would like called whenever the event is
|
||||
// triggered. A single class can subscribe to any number of events. For
|
||||
// organization purposes it's a good idea to create a new class for each
|
||||
// unique task/concept rather than just creating a catch-all class for all
|
||||
// event subscriptions.
|
||||
//
|
||||
// See EventSubscriberInterface::getSubscribedEvents() for an explanation
|
||||
// of the array's format.
|
||||
//
|
||||
// The array key is the name of the event your want to subscribe to. Best
|
||||
// practice is to use the constant that represents the event as defined by
|
||||
// the code responsible for dispatching the event. This way, if, for
|
||||
// example, the string name of an event changes your code will continue to
|
||||
// work. You can get a list of event constants for all events triggered by
|
||||
// core here:
|
||||
// https://api.drupal.org/api/drupal/core%21core.api.php/group/events/8.2.x.
|
||||
//
|
||||
// Since any module can define and trigger new events there may be
|
||||
// additional events available in your application. Look for classes with
|
||||
// the special @Event docblock indicator to discover other events.
|
||||
//
|
||||
// For each event key define an array of arrays composed of the method names
|
||||
// to call and optional priorities. The method name here refers to a method
|
||||
// on this class to call whenever the event is triggered.
|
||||
$events[IncidentEvents::NEW_REPORT][] = ['notifyMario'];
|
||||
|
||||
// Subscribers can optionally set a priority. If more than one subscriber is
|
||||
// listening to an event when it is triggered they will be executed in order
|
||||
// of priority. If no priority is set the default is 0.
|
||||
$events[IncidentEvents::NEW_REPORT][] = ['notifyBatman', -100];
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this incident is about a missing princess notify Mario.
|
||||
*
|
||||
* Per our configuration above, this method is called whenever the
|
||||
* IncidentEvents::NEW_REPORT event is dispatched. This method is where you
|
||||
* place any custom logic that you want to perform when the specific event is
|
||||
* triggered.
|
||||
*
|
||||
* These responder methods receive an event object as their argument. The
|
||||
* event object is usually, but not always, specific to the event being
|
||||
* triggered and contains data about application state and configuration
|
||||
* relative to what was happening when the event was triggered.
|
||||
*
|
||||
* For example, when responding to an event triggered by saving a
|
||||
* configuration change you'll get an event object that contains the relevant
|
||||
* configuration object.
|
||||
*
|
||||
* @param \Drupal\events_example\Event\IncidentReportEvent $event
|
||||
* The event object containing the incident report.
|
||||
*/
|
||||
public function notifyMario(IncidentReportEvent $event) {
|
||||
// You can use the event object to access information about the event passed
|
||||
// along by the event dispatcher.
|
||||
if ($event->getType() == 'stolen_princess') {
|
||||
drupal_set_message($this->t('Mario has been alerted. Thank you. This message was set by an event subscriber. See \Drupal\events_example\EventSubscriber\EventsExampleSubscriber::notifyMario()'), 'status');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Let Batman know about any events involving the Joker.
|
||||
*
|
||||
* @param \Drupal\events_example\Event\IncidentReportEvent $event
|
||||
* The event object containing the incident report.
|
||||
*/
|
||||
public function notifyBatman(IncidentReportEvent $event) {
|
||||
if ($event->getType() == 'joker') {
|
||||
drupal_set_message($this->t('Batman has been alerted. Thank you. This message was set by an event subscriber. See \Drupal\events_example\EventSubscriber\EventsExampleSubscriber::notifyBatman()'), 'status');
|
||||
// Optionally use the event object to stop propagation.
|
||||
// If there are other subscribers that have not been called yet this will
|
||||
// cause them to be skipped.
|
||||
$event->stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\events_example\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Drupal\events_example\Event\IncidentEvents;
|
||||
use Drupal\events_example\Event\IncidentReportEvent;
|
||||
|
||||
/**
|
||||
* Implements the SimpleForm form controller.
|
||||
*
|
||||
* The submitForm() method of this class demonstrates using the event dispatcher
|
||||
* service to dispatch an event.
|
||||
*
|
||||
* @see \Drupal\events_exampl\Event\IncidentEvents
|
||||
* @see \Drupal\events_example\Event\IncidentReportEvent
|
||||
* @see \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
* @see \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher
|
||||
*
|
||||
* @ingroup events_example
|
||||
*/
|
||||
class EventsExampleForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The event dispatcher service.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* Constructs a new UserLoginForm.
|
||||
*
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||
* The event dispatcher service.
|
||||
*/
|
||||
public function __construct(EventDispatcherInterface $event_dispatcher) {
|
||||
// The event dispatcher service is an implementation of
|
||||
// \Symfony\Component\EventDispatcher\EventDispatcherInterface. In Drupal
|
||||
// this is generally and instance of the
|
||||
// \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher service.
|
||||
// This dispatcher improves performance when dispatching events by compiling
|
||||
// a list of subscribers into the service container so that they do not need
|
||||
// to be looked up every time.
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('event_dispatcher')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['intro'] = [
|
||||
'#markup' => '<p>' . $this->t('This form demonstrates subscribing to, and dispatching, events. When the form is submitted an event is dispatched indicating a new report has been submitted. Event subscribers respond to this event with various messages depending on the incident type. Review the code for the events_example module to see how it works.') . '</p>',
|
||||
];
|
||||
|
||||
$form['incident_type'] = [
|
||||
'#type' => 'radios',
|
||||
'#required' => TRUE,
|
||||
'#title' => t('What type of incident do you want to report?'),
|
||||
'#options' => [
|
||||
'stolen_princess' => $this->t('Missing princess'),
|
||||
'cat' => $this->t('Cat stuck in tree'),
|
||||
'joker' => $this->t('Something involving the Joker'),
|
||||
],
|
||||
];
|
||||
|
||||
$form['incident'] = [
|
||||
'#type' => 'textarea',
|
||||
'#required' => FALSE,
|
||||
'#title' => t('Incident report'),
|
||||
'#description' => t('Describe the incident in detail. This information will be passed along to all crime fighters.'),
|
||||
'#cols' => 60,
|
||||
'#rows' => 5,
|
||||
];
|
||||
|
||||
$form['actions'] = [
|
||||
'#type' => 'actions',
|
||||
];
|
||||
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Submit'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'events_example_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$type = $form_state->getValue('incident_type');
|
||||
$report = $form_state->getValue('incident');
|
||||
|
||||
// When dispatching, or triggering, an event start by constructing a new
|
||||
// event object. Then use the event dispatcher service to notify any event
|
||||
// subscribers. Event objects are used to transport relevant data to any
|
||||
// subscribers, as well as keep track of the current state of an event. It
|
||||
// is best practice to create a unique class wrapping
|
||||
// \Symfony\Component\EventDispatcher\Event.
|
||||
$event = new IncidentReportEvent($type, $report);
|
||||
|
||||
// Dispatch an event by specifying which event, and providing an event
|
||||
// object. Rather than hard code the event name you should use a constant
|
||||
// to represent the event being dispatched. The constant serves as a
|
||||
// location for documentation of the event, and ensures your code is future
|
||||
// proofed against event name changes.
|
||||
$this->eventDispatcher->dispatch(IncidentEvents::NEW_REPORT, $event);
|
||||
}
|
||||
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\events_example\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Test the functionality of the Events Example module.
|
||||
*
|
||||
* For another example of testing whether or not events are dispatched see
|
||||
* \Drupal\Tests\migrate\Kernel\MigrateEventsTest.
|
||||
*
|
||||
* @ingroup events_example
|
||||
* @group examples
|
||||
*/
|
||||
class EventsExampleTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['events_example'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $profile = 'minimal';
|
||||
|
||||
/**
|
||||
* Test the output of the example page.
|
||||
*/
|
||||
public function testEventsExample() {
|
||||
// Test that the main page for the example is accessible.
|
||||
$this->drupalGet('examples/events-example');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Verify the page contains the required form fields.
|
||||
$this->assertSession()->fieldExists('incident_type');
|
||||
$this->assertSession()->fieldExists('incident');
|
||||
|
||||
// Submit the form with an incident type of 'stolen_princess'. This does a
|
||||
// couple of things. Fist of all, it ensures that our code in
|
||||
// EventsExampleForm::submitForm() that dispatches events works. If it did
|
||||
// not work, no event would be dispatched, and the message below would never
|
||||
// get displayed. Secondly, it tests that our
|
||||
// EventsExampleSubscriber::notifyMario() event subscriber is triggered for
|
||||
// incidents of the type 'stolen_princess'.
|
||||
$values = [
|
||||
'incident_type' => 'stolen_princess',
|
||||
'incident' => $this->randomString(),
|
||||
];
|
||||
$this->drupalPostForm('examples/events-example', $values, 'Submit');
|
||||
$this->assertSession()->pageTextContains('Mario has been alerted. Thank you.');
|
||||
|
||||
// Fill out the form again, this time testing that the
|
||||
// EventsExampleSubscriber::notifyBatman() subscriber is working.
|
||||
$values = [
|
||||
'incident_type' => 'joker',
|
||||
'incident' => $this->randomString(),
|
||||
];
|
||||
$this->drupalPostForm('examples/events-example', $values, 'Submit');
|
||||
$this->assertSession()->pageTextContains('Batman has been alerted. Thank you.');
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\events_example\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\events_example\EventSubscriber\EventsExampleSubscriber;
|
||||
|
||||
/**
|
||||
* Test to ensure 'events_example_subscriber' service is reachable.
|
||||
*
|
||||
* @ingroup events_example
|
||||
* @group examples
|
||||
*/
|
||||
class EventsExampleServiceTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['events_example'];
|
||||
|
||||
/**
|
||||
* Test for existence of 'events_example_subscriber' service.
|
||||
*/
|
||||
public function testEventsExampleService() {
|
||||
$subscriber = $this->container->get('events_example_subscriber');
|
||||
$this->assertInstanceOf(EventsExampleSubscriber::class, $subscriber);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user