2015-08-17 17:00:26 -07:00
< ? php
namespace Drupal\comment ;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface ;
use Drupal\Component\Utility\Html ;
use Drupal\Component\Utility\Unicode ;
use Drupal\Core\Datetime\DrupalDateTime ;
use Drupal\Core\Entity\ContentEntityForm ;
use Drupal\Core\Entity\EntityConstraintViolationListInterface ;
use Drupal\Core\Entity\EntityManagerInterface ;
use Drupal\Core\Form\FormStateInterface ;
use Drupal\Core\Render\RendererInterface ;
use Drupal\Core\Session\AccountInterface ;
use Symfony\Component\DependencyInjection\ContainerInterface ;
/**
2016-04-07 11:19:57 -07:00
* Base handler for comment forms .
2015-08-17 17:00:26 -07:00
*/
class CommentForm extends ContentEntityForm {
/**
* The current user .
*
* @ var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser ;
/**
* The renderer .
*
* @ var \Drupal\Core\Render\RendererInterface
*/
protected $renderer ;
/**
* { @ inheritdoc }
*/
public static function create ( ContainerInterface $container ) {
return new static (
$container -> get ( 'entity.manager' ),
$container -> get ( 'current_user' ),
$container -> get ( 'renderer' )
);
}
/**
* Constructs a new CommentForm .
*
* @ param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service .
* @ param \Drupal\Core\Session\AccountInterface $current_user
* The current user .
* @ param \Drupal\Core\Render\RendererInterface $renderer
* The renderer .
*/
public function __construct ( EntityManagerInterface $entity_manager , AccountInterface $current_user , RendererInterface $renderer ) {
parent :: __construct ( $entity_manager );
$this -> currentUser = $current_user ;
$this -> renderer = $renderer ;
}
/**
2015-11-04 11:11:27 -08:00
* { @ inheritdoc }
2015-08-17 17:00:26 -07:00
*/
public function form ( array $form , FormStateInterface $form_state ) {
/** @var \Drupal\comment\CommentInterface $comment */
$comment = $this -> entity ;
$entity = $this -> entityManager -> getStorage ( $comment -> getCommentedEntityTypeId ()) -> load ( $comment -> getCommentedEntityId ());
$field_name = $comment -> getFieldName ();
$field_definition = $this -> entityManager -> getFieldDefinitions ( $entity -> getEntityTypeId (), $entity -> bundle ())[ $comment -> getFieldName ()];
$config = $this -> config ( 'user.settings' );
2015-09-04 13:20:09 -07:00
// In several places within this function, we vary $form on:
// - The current user's permissions.
// - Whether the current user is authenticated or anonymous.
// - The 'user.settings' configuration.
// - The comment field's definition.
$form [ '#cache' ][ 'contexts' ][] = 'user.permissions' ;
$form [ '#cache' ][ 'contexts' ][] = 'user.roles:authenticated' ;
$this -> renderer -> addCacheableDependency ( $form , $config );
$this -> renderer -> addCacheableDependency ( $form , $field_definition -> getConfig ( $entity -> bundle ()));
2015-08-17 17:00:26 -07:00
// Use #comment-form as unique jump target, regardless of entity type.
$form [ '#id' ] = Html :: getUniqueId ( 'comment_form' );
$form [ '#theme' ] = array ( 'comment_form__' . $entity -> getEntityTypeId () . '__' . $entity -> bundle () . '__' . $field_name , 'comment_form' );
$anonymous_contact = $field_definition -> getSetting ( 'anonymous' );
$is_admin = $comment -> id () && $this -> currentUser -> hasPermission ( 'administer comments' );
if ( ! $this -> currentUser -> isAuthenticated () && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT ) {
$form [ '#attached' ][ 'library' ][] = 'core/drupal.form' ;
$form [ '#attributes' ][ 'data-user-info-from-browser' ] = TRUE ;
}
// If not replying to a comment, use our dedicated page callback for new
// Comments on entities.
if ( ! $comment -> id () && ! $comment -> hasParentComment ()) {
$form [ '#action' ] = $this -> url ( 'comment.reply' , array ( 'entity_type' => $entity -> getEntityTypeId (), 'entity' => $entity -> id (), 'field_name' => $field_name ));
}
$comment_preview = $form_state -> get ( 'comment_preview' );
if ( isset ( $comment_preview )) {
$form += $comment_preview ;
}
$form [ 'author' ] = array ();
// Display author information in a details element for comment moderators.
if ( $is_admin ) {
$form [ 'author' ] += array (
'#type' => 'details' ,
'#title' => $this -> t ( 'Administration' ),
);
}
// Prepare default values for form elements.
2015-10-08 11:40:12 -07:00
$author = '' ;
2015-08-17 17:00:26 -07:00
if ( $is_admin ) {
2015-10-08 11:40:12 -07:00
if ( ! $comment -> getOwnerId ()) {
$author = $comment -> getAuthorName ();
}
2015-08-17 17:00:26 -07:00
$status = $comment -> getStatus ();
if ( empty ( $comment_preview )) {
$form [ '#title' ] = $this -> t ( 'Edit comment %title' , array (
'%title' => $comment -> getSubject (),
));
}
}
else {
$status = ( $this -> currentUser -> hasPermission ( 'skip comment approval' ) ? CommentInterface :: PUBLISHED : CommentInterface :: NOT_PUBLISHED );
}
$date = '' ;
if ( $comment -> id ()) {
$date = ! empty ( $comment -> date ) ? $comment -> date : DrupalDateTime :: createFromTimestamp ( $comment -> getCreatedTime ());
}
2015-10-08 11:40:12 -07:00
// The uid field is only displayed when a user with the permission
// 'administer comments' is editing an existing comment from an
// authenticated user.
$owner = $comment -> getOwner ();
$form [ 'author' ][ 'uid' ] = [
'#type' => 'entity_autocomplete' ,
'#target_type' => 'user' ,
'#default_value' => $owner -> isAnonymous () ? NULL : $owner ,
// A comment can be made anonymous by leaving this field empty therefore
// there is no need to list them in the autocomplete.
'#selection_settings' => [ 'include_anonymous' => FALSE ],
'#title' => $this -> t ( 'Authored by' ),
'#description' => $this -> t ( 'Leave blank for %anonymous.' , [ '%anonymous' => $config -> get ( 'anonymous' )]),
'#access' => $is_admin ,
];
// The name field is displayed when an anonymous user is adding a comment or
// when a user with the permission 'administer comments' is editing an
// existing comment from an anonymous user.
2015-08-17 17:00:26 -07:00
$form [ 'author' ][ 'name' ] = array (
'#type' => 'textfield' ,
2015-10-08 11:40:12 -07:00
'#title' => $is_admin ? $this -> t ( 'Name for @anonymous' , [ '@anonymous' => $config -> get ( 'anonymous' )]) : $this -> t ( 'Your name' ),
2015-08-17 17:00:26 -07:00
'#default_value' => $author ,
'#required' => ( $this -> currentUser -> isAnonymous () && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT ),
'#maxlength' => 60 ,
2015-10-08 11:40:12 -07:00
'#access' => $this -> currentUser -> isAnonymous () || $is_admin ,
2015-08-17 17:00:26 -07:00
'#size' => 30 ,
2016-05-04 14:35:41 -07:00
'#attributes' => [
2015-10-08 11:40:12 -07:00
'data-drupal-default-value' => $config -> get ( 'anonymous' ),
],
2015-08-17 17:00:26 -07:00
);
2015-10-08 11:40:12 -07:00
2015-08-17 17:00:26 -07:00
if ( $is_admin ) {
2015-10-08 11:40:12 -07:00
// When editing a comment only display the name textfield if the uid field
// is empty.
$form [ 'author' ][ 'name' ][ '#states' ] = [
'visible' => [
':input[name="uid"]' => array ( 'empty' => TRUE ),
],
];
2015-08-17 17:00:26 -07:00
}
// Add author email and homepage fields depending on the current user.
$form [ 'author' ][ 'mail' ] = array (
'#type' => 'email' ,
'#title' => $this -> t ( 'Email' ),
'#default_value' => $comment -> getAuthorEmail (),
'#required' => ( $this -> currentUser -> isAnonymous () && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT ),
'#maxlength' => 64 ,
'#size' => 30 ,
'#description' => $this -> t ( 'The content of this field is kept private and will not be shown publicly.' ),
'#access' => ( $comment -> getOwner () -> isAnonymous () && $is_admin ) || ( $this -> currentUser -> isAnonymous () && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT ),
);
$form [ 'author' ][ 'homepage' ] = array (
'#type' => 'url' ,
'#title' => $this -> t ( 'Homepage' ),
'#default_value' => $comment -> getHomepage (),
'#maxlength' => 255 ,
'#size' => 30 ,
'#access' => $is_admin || ( $this -> currentUser -> isAnonymous () && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT ),
);
// Add administrative comment publishing options.
$form [ 'author' ][ 'date' ] = array (
'#type' => 'datetime' ,
'#title' => $this -> t ( 'Authored on' ),
'#default_value' => $date ,
'#size' => 20 ,
'#access' => $is_admin ,
);
$form [ 'author' ][ 'status' ] = array (
'#type' => 'radios' ,
'#title' => $this -> t ( 'Status' ),
'#default_value' => $status ,
'#options' => array (
CommentInterface :: PUBLISHED => $this -> t ( 'Published' ),
CommentInterface :: NOT_PUBLISHED => $this -> t ( 'Not published' ),
),
'#access' => $is_admin ,
);
return parent :: form ( $form , $form_state , $comment );
}
/**
2015-11-04 11:11:27 -08:00
* { @ inheritdoc }
2015-08-17 17:00:26 -07:00
*/
protected function actions ( array $form , FormStateInterface $form_state ) {
$element = parent :: actions ( $form , $form_state );
/* @var \Drupal\comment\CommentInterface $comment */
$comment = $this -> entity ;
$entity = $comment -> getCommentedEntity ();
$field_definition = $this -> entityManager -> getFieldDefinitions ( $entity -> getEntityTypeId (), $entity -> bundle ())[ $comment -> getFieldName ()];
$preview_mode = $field_definition -> getSetting ( 'preview' );
// No delete action on the comment form.
unset ( $element [ 'delete' ]);
// Mark the submit action as the primary action, when it appears.
$element [ 'submit' ][ '#button_type' ] = 'primary' ;
// Only show the save button if comment previews are optional or if we are
// already previewing the submission.
$element [ 'submit' ][ '#access' ] = ( $comment -> id () && $this -> currentUser -> hasPermission ( 'administer comments' )) || $preview_mode != DRUPAL_REQUIRED || $form_state -> get ( 'comment_preview' );
$element [ 'preview' ] = array (
'#type' => 'submit' ,
'#value' => $this -> t ( 'Preview' ),
'#access' => $preview_mode != DRUPAL_DISABLED ,
'#submit' => array ( '::submitForm' , '::preview' ),
);
return $element ;
}
/**
* { @ inheritdoc }
*/
public function buildEntity ( array $form , FormStateInterface $form_state ) {
/** @var \Drupal\comment\CommentInterface $comment */
$comment = parent :: buildEntity ( $form , $form_state );
2015-10-21 21:44:50 -07:00
if ( ! $form_state -> isValueEmpty ( 'date' ) && $form_state -> getValue ( 'date' ) instanceof DrupalDateTime ) {
2015-08-17 17:00:26 -07:00
$comment -> setCreatedTime ( $form_state -> getValue ( 'date' ) -> getTimestamp ());
}
else {
$comment -> setCreatedTime ( REQUEST_TIME );
}
2015-10-08 11:40:12 -07:00
// Empty author ID should revert to anonymous.
$author_id = $form_state -> getValue ( 'uid' );
if ( $comment -> id () && $this -> currentUser -> hasPermission ( 'administer comments' )) {
// Admin can leave the author ID blank to revert to anonymous.
$author_id = $author_id ? : 0 ;
2015-08-17 17:00:26 -07:00
}
2015-10-08 11:40:12 -07:00
if ( ! is_null ( $author_id )) {
if ( $author_id === 0 && $form [ 'author' ][ 'name' ][ '#access' ]) {
// Use the author name value when the form has access to the element and
// the author ID is anonymous.
$comment -> setAuthorName ( $form_state -> getValue ( 'name' ));
}
else {
// Ensure the author name is not set.
$comment -> setAuthorName ( NULL );
}
}
else {
$author_id = $this -> currentUser -> id ();
2015-08-17 17:00:26 -07:00
}
2015-10-08 11:40:12 -07:00
$comment -> setOwnerId ( $author_id );
2015-08-17 17:00:26 -07:00
// Validate the comment's subject. If not specified, extract from comment
// body.
if ( trim ( $comment -> getSubject ()) == '' ) {
if ( $comment -> hasField ( 'comment_body' )) {
// The body may be in any format, so:
// 1) Filter it into HTML
// 2) Strip out all HTML tags
// 3) Convert entities back to plain-text.
$comment_text = $comment -> comment_body -> processed ;
$comment -> setSubject ( Unicode :: truncate ( trim ( Html :: decodeEntities ( strip_tags ( $comment_text ))), 29 , TRUE , TRUE ));
}
// Edge cases where the comment body is populated only by HTML tags will
// require a default subject.
if ( $comment -> getSubject () == '' ) {
$comment -> setSubject ( $this -> t ( '(No subject)' ));
}
}
return $comment ;
}
/**
* { @ inheritdoc }
*/
protected function getEditedFieldNames ( FormStateInterface $form_state ) {
return array_merge ([ 'created' , 'name' ], parent :: getEditedFieldNames ( $form_state ));
}
/**
* { @ inheritdoc }
*/
protected function flagViolations ( EntityConstraintViolationListInterface $violations , array $form , FormStateInterface $form_state ) {
// Manually flag violations of fields not handled by the form display.
foreach ( $violations -> getByField ( 'created' ) as $violation ) {
$form_state -> setErrorByName ( 'date' , $violation -> getMessage ());
}
foreach ( $violations -> getByField ( 'name' ) as $violation ) {
$form_state -> setErrorByName ( 'name' , $violation -> getMessage ());
}
parent :: flagViolations ( $violations , $form , $form_state );
}
/**
* Form submission handler for the 'preview' action .
*
2016-04-20 09:56:34 -07:00
* @ param array $form
2015-08-17 17:00:26 -07:00
* An associative array containing the structure of the form .
2016-04-20 09:56:34 -07:00
* @ param \Drupal\Core\Form\FormStateInterface $form_state
2015-08-17 17:00:26 -07:00
* The current state of the form .
*/
public function preview ( array & $form , FormStateInterface $form_state ) {
$comment_preview = comment_preview ( $this -> entity , $form_state );
$comment_preview [ '#title' ] = $this -> t ( 'Preview comment' );
$form_state -> set ( 'comment_preview' , $comment_preview );
$form_state -> setRebuild ();
}
/**
2015-11-04 11:11:27 -08:00
* { @ inheritdoc }
2015-08-17 17:00:26 -07:00
*/
public function save ( array $form , FormStateInterface $form_state ) {
$comment = $this -> entity ;
$entity = $comment -> getCommentedEntity ();
$field_name = $comment -> getFieldName ();
$uri = $entity -> urlInfo ();
$logger = $this -> logger ( 'content' );
if ( $this -> currentUser -> hasPermission ( 'post comments' ) && ( $this -> currentUser -> hasPermission ( 'administer comments' ) || $entity -> { $field_name } -> status == CommentItemInterface :: OPEN )) {
$comment -> save ();
$form_state -> setValue ( 'cid' , $comment -> id ());
// Add a log entry.
$logger -> notice ( 'Comment posted: %subject.' , array (
'%subject' => $comment -> getSubject (),
'link' => $this -> l ( t ( 'View' ), $comment -> urlInfo () -> setOption ( 'fragment' , 'comment-' . $comment -> id ()))
));
// Explain the approval queue if necessary.
if ( ! $comment -> isPublished ()) {
if ( ! $this -> currentUser -> hasPermission ( 'administer comments' )) {
drupal_set_message ( $this -> t ( 'Your comment has been queued for review by site administrators and will be published after approval.' ));
}
}
else {
drupal_set_message ( $this -> t ( 'Your comment has been posted.' ));
}
$query = array ();
// Find the current display page for this comment.
$field_definition = $this -> entityManager -> getFieldDefinitions ( $entity -> getEntityTypeId (), $entity -> bundle ())[ $field_name ];
$page = $this -> entityManager -> getStorage ( 'comment' ) -> getDisplayOrdinal ( $comment , $field_definition -> getSetting ( 'default_mode' ), $field_definition -> getSetting ( 'per_page' ));
if ( $page > 0 ) {
$query [ 'page' ] = $page ;
}
// Redirect to the newly posted comment.
$uri -> setOption ( 'query' , $query );
$uri -> setOption ( 'fragment' , 'comment-' . $comment -> id ());
}
else {
$logger -> warning ( 'Comment: unauthorized comment submitted or comment submitted to a closed post %subject.' , array ( '%subject' => $comment -> getSubject ()));
drupal_set_message ( $this -> t ( 'Comment: unauthorized comment submitted or comment submitted to a closed post %subject.' , array ( '%subject' => $comment -> getSubject ())), 'error' );
// Redirect the user to the entity they are commenting on.
}
$form_state -> setRedirectUrl ( $uri );
}
2016-06-02 15:56:09 -07:00
2015-08-17 17:00:26 -07:00
}