Commit 7e6b1d38 authored by Arla's avatar Arla Committed by greggles

Issue #2221061 by Arla, pjonckiere | Berdir: Port comment_notify to Drupal 8

parent d56ea598
......@@ -5,6 +5,7 @@
*
* Contains functions which utilize the database and other internal helpers.
*/
use Drupal\comment\CommentInterface;
/**
* Get the notification preferences for a specific user.
......@@ -21,9 +22,7 @@ function comment_notify_get_user_notification_setting($uid) {
}
// Handle anonymous users with defaults.
if ($uid == 0) {
$users[0] = new stdClass();
$users[0]->comment_notify = comment_notify_variable_registry_get('default_registered_mailalert');
$users[0]->node_notify = comment_notify_variable_registry_get('node_notify_default_mailalert');
$users[0] = comment_notify_get_default_notification_setting();
}
else {
$setting = db_select('comment_notify_user_settings', 'cnus')
......@@ -45,8 +44,8 @@ function comment_notify_get_user_notification_setting($uid) {
function comment_notify_get_default_notification_setting() {
return (object) array(
'comment_notify' => comment_notify_variable_registry_get('default_registered_mailalert'),
'node_notify' => comment_notify_variable_registry_get('node_notify_default_mailalert')
'comment_notify' => \Drupal::config('comment_notify.settings')->get('enable_default.watcher'),
'node_notify' => \Drupal::config('comment_notify.settings')->get('enable_default.entity_author')
);
}
......@@ -188,15 +187,16 @@ function comment_notify_get_notification_type($cid) {
* Get a list of mails which need to be contacted for a node.
*
* @param integer $nid
* @return QueryStatement
* @return \Drupal\comment\CommentInterface[]
* A list of comment entities.
*/
function comment_notify_get_watchers($nid) {
$cids = db_query("SELECT c.cid FROM {comment} c INNER JOIN {comment_notify} cn ON c.cid = cn.cid LEFT JOIN {users} u ON c.uid = u.uid WHERE c.nid = :nid AND c.status = :status AND cn.notify <> :notify AND (u.uid = 0 OR u.status = 1)", array(
$cids = db_query("SELECT c.cid FROM {comment_field_data} c INNER JOIN {comment_notify} cn ON c.cid = cn.cid LEFT JOIN {users_field_data} u ON c.uid = u.uid WHERE c.entity_id = :nid AND c.status = :status AND cn.notify <> :notify AND (u.uid = 0 OR u.status = 1)", array(
':nid' => $nid,
':status' => COMMENT_PUBLISHED,
':status' => CommentInterface::PUBLISHED,
':notify' => COMMENT_NOTIFY_DISABLED,
))->fetchCol();
return comment_load_multiple($cids);
return \Drupal::entityManager()->getStorage('comment')->loadMultiple($cids);
}
/**
......@@ -214,7 +214,7 @@ function comment_notify_mark_comment_as_notified($comment) {
->fields(array(
'notified' => 1,
))
->condition('cid', $comment->cid)
->condition('cid', $comment->id())
->execute();
}
......@@ -231,16 +231,10 @@ function comment_notify_unsubscribe_by_email($mail) {
$update_query = db_update('comment_notify');
$update_query->fields(array('notify' => 0));
$comment_query = db_select('comment', 'c');
$comment_query->fields('c', array('cid'));
$comment_query = \Drupal::entityQuery('comment');
$uid = db_select('users', 'u')
->fields('u', array('uid'))
->condition('mail', $mail)
->execute()
->fetchField();
if ($uid) {
$comment_query->condition('uid', $uid);
if ($user = user_load_by_mail($mail)) {
$comment_query->condition('uid', $user->id());
}
else {
$comment_query->condition('mail', $mail);
......@@ -264,6 +258,10 @@ function comment_notify_unsubscribe_by_hash($hash) {
->condition('notify_hash', $hash)
->execute()->fetchAll();
if (empty($notification)) {
return FALSE;
}
// If this notification is at the node level, delete all notifications for this node.
if (COMMENT_NOTIFY_NODE == $notification[0]->notify) {
// Get all this user's comments for this node.
......@@ -297,42 +295,3 @@ function comment_notify_unsubscribe_by_hash($hash) {
->execute();
}
}
/**
* Helper function to centralize variable management and defaults.
*
* All variables fall under the "comment_notify" psuedo namespace. This ensures
* consistancy, and eliminates some verbosity in the calling code. In addition
* by storing all of the variables in one place, we avoid repeating duplicate
* defaults which are harder to maintain.
*
* @param string $name
* @return mixed
*/
function comment_notify_variable_registry_get($name) {
$variables = array();
$variables['author_subject'] = t('[site:name] :: new comment for your post.');
$variables['available_alerts'] = array(COMMENT_NOTIFY_NODE, COMMENT_NOTIFY_COMMENT);
$variables['default_anon_mailalert'] = COMMENT_NOTIFY_NODE;
$variables['node_notify_default_mailtext'] = AUTHOR_MAILTEXT;
$variables['default_registered_mailalert'] = COMMENT_NOTIFY_DISABLED;
$variables['node_notify_default_mailalert'] = 0;
$variables['watcher_subject'] = '[site:name] :: new comment on [comment:node:title]';
$variables['comment_notify_default_mailtext'] = DEFAULT_MAILTEXT;
$variables['node_types'] = array('article' => 'article');
// Errors
$variables['error_anonymous_email_missing'] = 'If you want to subscribe to comments you must supply a valid e-mail address.';
return variable_get("comment_notify_" . $name, $variables[$name]);
}
/**
* Helper function to centralize setting variables.
*
* @param string $name
* @param mixed $value
* @return boolean
*/
function comment_notify_variable_registry_set($name, $value) {
return variable_set("comment_notify_" . $name, $value);
}
name = Comment Notify
description = "Comment follow-up e-mail notification for anonymous as well as registered users."
dependencies[] = comment
dependencies[] = token
core = 7.x
configure = admin/config/people/comment_notify
files[] = comment_notify.install
files[] = comment_notify.module
files[] = comment_notify.migrate.inc
files[] = comment_notify.tokens.inc
files[] = comment_notify.inc
files[] = comment_notify.test
name: 'Comment Notify'
description: 'Comment follow-up e-mail notification for anonymous as well as registered users.'
configure: comment_notify.settings
dependencies:
- comment
- token
core: 8.x
type: module
......@@ -8,38 +8,23 @@
* Implements hook_install().
*/
function comment_notify_install() {
// Create entries for existing comments.
$comments_select = db_select('comment', 'c');
$comments_select->join('users', 'u', 'c.uid = u.uid');
$comments_select = db_select('comment_field_data', 'c');
$comments_select->join('users_field_data', 'u', 'c.uid = u.uid');
$comments_select->addField('c', 'cid');
$comments_select->addExpression('0', 'notify');
// Mix in a random string to all values.
$salt = uniqid(mt_rand(), TRUE);
if (db_driver() == 'pgsql') {
$comments_select->addExpression("MD5(:salt || c.mail || COALESCE(u.mail, u.init) || c.uid || c.name || c.nid || c.hostname || c.cid)", 'notify_hash', array(':salt' => $salt));
$comments_select->addExpression("MD5(:salt || c.mail || COALESCE(u.mail, u.init) || c.uid || c.name || c.entity_id || c.hostname || c.cid)", 'notify_hash', array(':salt' => $salt));
}
else {
$comments_select->addExpression("MD5(CONCAT(:salt, c.mail, COALESCE(u.mail, u.init), c.uid, c.name, c.nid, c.hostname, c.cid))", 'notify_hash', array(':salt' => $salt));
$comments_select->addExpression("MD5(CONCAT_WS('', :salt, c.mail, COALESCE(u.mail, u.init), c.uid, c.name, c.entity_id, c.hostname, c.cid))", 'notify_hash', array(':salt' => $salt));
}
// Set module weight low so that other modules act on the comment first.
db_insert('comment_notify')->from($comments_select)->execute();
db_update('system')->
fields(array(
'weight' => 10
))
->condition('name', 'comment_notify');
}
/**
* Implements hook_uninstall().
*/
function comment_notify_uninstall() {
variable_del('node_notify_default_mailtext');
db_delete('variable')
->where('name', "comment_notify_%", 'LIKE');
// Set module weight low so that other modules act on the comment first.
module_set_weight('comment_notify', 10);
}
/**
......@@ -107,78 +92,3 @@ function comment_notify_schema() {
return $schema;
}
/**
* Head 2 head update for the new size of the hash field.
*/
function comment_notify_update_7002() {
$new_spec = array(
'type' => 'varchar',
'description' => 'A hash of unique information about the commenter. Used for unsubscribing users.',
'length' => '128',
'not null' => TRUE,
'default' => ''
);
$keys = array(
'indexes' => array('notify_hash' => array('notify_hash')),
);
db_drop_index('comment_notify', 'notify_hash');
db_change_field('comment_notify', 'notify_hash', 'notify_hash', $new_spec, $keys);
}
/**
* Fix tokens in comment subscriber e-mail template.
*/
function comment_notify_update_7003() {
$variables = array(
'comment_notify_author_subject' => 'author',
'comment_notify_node_notify_default_mailtext' => 'author',
'comment_notify_watcher_subject' => 'commenter',
'comment_notify_comment_notify_default_mailtext' => 'commenter',
);
foreach ($variables as $variable => $context) {
// If the variable is using the default value, this will return NULL and we
// can skip it.
if ($text = variable_get($variable)) {
$replacements = array(
'[comment:unsubscribe-url]' => '[comment-subscribed:unsubscribe-url]',
'[comment:name]' => '[comment:author]',
'[node:' => '[comment:node:', // Replace all node tokens with comment:node.
);
if ($context == 'author') {
$replacements['[user:name]'] = '[comment:node:author]';
$replacements['[user:'] = '[comment:node:author:';
}
elseif ($context == 'commenter') {
$replacements['[user:name]'] = '[comment-subscribed:author]';
$replacements['[user:'] = '[comment-subscribed:author:';
}
$text = strtr($text, $replacements);
variable_set($variable, $text);
}
}
return 'Comment Notify token strings updated.';
}
/**
* Fix a missing default causes warnings for Postgresql and some MySQL.
*/
function comment_notify_update_7004() {
db_change_field('comment_notify', 'notified', 'notified', array('type' => 'int', 'size' => 'small', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0));
}
/**
* Fix minor schema inconsistencies caused by the last update.
*/
function comment_notify_update_7005() {
// Previous update altered size and unsigned unnecessarily.
$notified_field = array(
'type' => 'int',
'description' => 'A boolean indicator for whether or not a notification for the comment has been sent: 1 means yes, 0 means no.',
'size' => 'tiny',
'not null' => TRUE,
'default' => 0,
'disp-width' => '11',
);
db_change_field('comment_notify', 'notified', 'notified', $notified_field);
}
comment_notify:
version: 1.x
css:
theme:
comment_notify.css: {}
js:
comment_notify.js: {}
dependencies:
- core/jquery
comment_notify.settings:
route_name: comment_notify.settings
title: 'Comment notification'
description: 'Configure settings for e-mails about new comments.'
parent: user.admin_index
comment_notify.settings:
route_name: comment_notify.settings
title: Settings
base_route: comment_notify.settings
comment_notify.unsubscribe:
route_name: comment_notify.unsubscribe
title: Unsubscribe
weight: 2
base_route: comment_notify.settings
This diff is collapsed.
'administer comment notify':
title: 'Administer Comment Notify'
description: 'Change global comment notification settings.'
'subscribe to comments':
title: 'Subscribe to comment notifications'
description: 'Subscribe to recieve notifications when new comments are posted.'
comment_notify.settings:
path: /admin/config/people/comment_notify
defaults:
_title: Comment notification
_form: \Drupal\comment_notify\Form\CommentNotifySettings
requirements:
_permission: 'administer comment notify'
comment_notify.unsubscribe:
path: /admin/config/people/comment_notify/unsubscribe
defaults:
_title: Unsubscribe from comment notifications
_form: \Drupal\comment_notify\Form\CommentNotifyUnsubscribe
requirements:
_permission: 'administer comment notify'
comment_notify.disable:
path: '/comment_notify/disable/{hash}'
defaults:
_title: 'Disable comment notification'
_controller: '\Drupal\comment_notify\Controller\CommentNotifyController::disable'
requirements:
_permission: 'access content'
......@@ -4,6 +4,7 @@
* @file
* Builds placeholder replacement tokens for comment_notify.module.
*/
use Drupal\Core\Render\BubbleableMetadata;
/**
* Implements hook_token_info().
......@@ -29,7 +30,7 @@ function comment_notify_token_info() {
/**
* Implements hook_tokens().
*/
function comment_notify_tokens($type, $tokens, array $data = array(), array $options = array()) {
function comment_notify_tokens($type, $tokens, array $data = array(), array $options = array(), BubbleableMetadata $bubbleable_metadata) {
$url_options = array('absolute' => TRUE);
if (isset($options['language'])) {
$url_options['language'] = $options['language'];
......@@ -49,21 +50,21 @@ function comment_notify_tokens($type, $tokens, array $data = array(), array $opt
switch ($name) {
case 'unsubscribe-url':
if ($unsubscribe_url = comment_notify_get_unsubscribe_url($comment)) {
$replacements[$original] = url($unsubscribe_url, $url_options);
$replacements[$original] = $unsubscribe_url;
}
break;
}
}
// [comment:unsubscribe-url:*] chained token replacements.
if (($unsubscribe_url_tokens = token_find_with_prefix($tokens, 'unsubscribe-url')) && $unsubscribe_url = comment_notify_get_unsubscribe_url($comment)) {
$replacements += token_generate('url', $unsubscribe_url_tokens, array('path' => $unsubscribe_url), $options);
if (($unsubscribe_url_tokens = \Drupal::token()->findWithPrefix($tokens, 'unsubscribe-url')) && $unsubscribe_url = comment_notify_get_unsubscribe_url($comment)) {
$replacements += \Drupal::token()->generate('url', $unsubscribe_url_tokens, array('path' => $unsubscribe_url), $options, $bubbleable_metadata);
}
}
// Comment subscriber tokens (pass through to comment token replacement).
if ($type == 'comment-subscribed' && !empty($data['comment-subscribed'])) {
$replacements += token_generate('comment', $tokens, array('comment' => $data['comment-subscribed']), $options);
$replacements += \Drupal::token()->generate('comment', $tokens, array('comment' => $data['comment-subscribed']), $options, $bubbleable_metadata);
}
return $replacements;
......
node_types:
- article
available_alerts:
1: true
2: true
enable_default:
watcher: none
entity_author: false
mail_templates:
watcher:
subject: '[site:name] :: new comment on [comment:node:title]'
body: |
Hi [comment-subscribed:author],
[comment:author] has commented on: "[comment:node:title]"
----
[comment:title]
[comment:body]
----
You can view the comment at the following url
[comment:url]
You can stop receiving emails when someone replies to this post,
by going to [comment-subscribed:unsubscribe-url]
You can set up auto-following feature for all future posts
by creating your own user with a few clicks here [site:login-url]
-- [site:name] team
[site:url]
entity_author:
subject: '[site:name] :: new comment for your post'
body: |
Hi [comment:node:author],
You have received a comment on: "[comment:node:title]"
----
[comment:title]
[comment:body]
----
You can view the comment at the following url
[comment:url]
You will receive emails like this for all replies to your posts. You can
disable this by logging in and changing the settings on your user account at
[comment:node:author:edit-url].
-- [site:name] team
[site:url]
comment_notify.settings:
type: config_object
mapping:
node_types:
type: sequence
label: Content types to enable for comment notification
sequence:
type: string
available_alerts:
type: mapping
label: Available subscription modes
mapping:
# COMMENT_NOTIFY_NODE
1:
type: boolean
# COMMENT_NOTIFY_COMMENT
2:
type: boolean
enable_default:
type: mapping
mapping:
watcher:
type: string
label: Default state for the notification selection box
entity_author:
type: boolean
label: Subscribe users to their node follow-up notification emails by default
mail_templates:
type: mapping
label: Default mail text for sending out notifications
mapping:
watcher:
type: mapping
label: Notifications to commenters
mapping:
subject:
type: label
label: Subject
body:
type: text
label: Body
entity_author:
type: mapping
label: Notifications to owner of commented entity
mapping:
subject:
type: label
label: Subject
body:
type: text
label: Body
<?php
/**
* @file
* Contains \Drupal\comment_notify\Controller\CommentNotifyController.
*/
namespace Drupal\comment_notify\Controller;
use Drupal\Core\Controller\ControllerBase;
/**
* Default controller for the comment_notify module.
*/
class CommentNotifyController extends ControllerBase {
/**
* Creates a page for disabling notifications.
*
* @param string $hash
* A hash identifying the notification entry to disable.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
*/
public function disable($hash) {
module_load_include('inc', 'comment_notify', 'comment_notify');
if (comment_notify_unsubscribe_by_hash($hash)) {
return ['#markup' => $this->t('Your comment follow-up notification for this post was disabled. Thanks.')];
}
else {
return ['#markup' => $this->t('Sorry, there was a problem unsubscribing from notifications.')];
}
}
}
<?php
/**
* @file
* Contains \Drupal\comment_notify\Form\CommentNotifySettings.
*/
namespace Drupal\comment_notify\Form;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\field\Entity\FieldConfig;
use Drupal\node\Entity\NodeType;
use Drupal\user\Entity\User;
/**
* Settings form for the Comment Notify module.
*/
class CommentNotifySettings extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'comment_notify_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['comment_notify.settings'];
}
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('comment_notify.settings');
// Only perform comment_notify for certain node types.
$enabled_types = $config->get('node_types');
$anonymous_problems = '';
$checkboxes = [];
foreach (NodeType::loadMultiple() as $type_id => $type) {
$checkboxes[$type_id] = Html::escape($type->label());
$default[] = $type_id;
// If they don't have the ability to leave contact info, then we make a report
$comment_field = FieldConfig::loadByName('node', $type_id, 'comment');
if (in_array($type_id, $enabled_types) && $comment_field && $comment_field->getSetting('anonymous') == COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
if (User::getAnonymousUser()->hasPermission('subscribe to comments')) {
$anonymous_problems[] = $type->link($type->label());
}
}
}
if (!empty($anonymous_problems)) {
drupal_set_message($this->t('Anonymous commenters have the permission to subscribe to comments but cannot leave their contact information on the following content types: !types. You should either disable subscriptions on those types here, revoke the permission for anonymous users, or enable anonymous users to leave their contact information in the comment settings.', [
'!types' => implode(', ', $anonymous_problems),
]), 'status', FALSE);
}
$form['node_types'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Content types to enable for comment notification'),
'#default_value' => $enabled_types,
'#options' => $checkboxes,
'#description' => $this->t('Comments on content types enabled here will have the option of comment notification.'),
];
$form['available_alerts'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Available subscription modes'),
'#return_value' => 1,
'#default_value' => array_keys(array_filter($config->get('available_alerts'))),
'#description' => $this->t('Choose which notification subscription styles are available for users'),
'#options' => [
COMMENT_NOTIFY_NODE => $this->t('All comments'),
COMMENT_NOTIFY_COMMENT => $this->t('Replies to my comment'),
],
];
$available_options[COMMENT_NOTIFY_DISABLED] = $this->t('No notifications');
$available_options += _comment_notify_options();
$form['enable_default'] = [
'#type' => 'container',
'#tree' => TRUE,
];
$form['enable_default']['watcher'] = [
'#type' => 'select',
'#title' => $this->t('Default state for the notification selection box'),
'#return_value' => 1,
'#default_value' => $config->get('enable_default.watcher'),
'#description' => $this->t('This flag presets the flag for the follow-up notification on the form that users will see when posting a comment'),
'#options' => $available_options,
];
$form['enable_default']['entity_author'] = [
'#type' => 'checkbox',
'#title' => $this->t('Subscribe users to their node follow-up notification emails by default'),
'#default_value' => $config->get('enable_default.entity_author'),
'#description' => $this->t('If this is checked, new users will receive e-mail notifications for follow-ups on their nodes by default until they individually disable the feature.'),
];
$form['mail_templates'] = [
'#type' => 'container',
'#tree' => TRUE,
];
$form['mail_templates']['watcher'] = [
'#type' => 'container',
'#tree' => TRUE,
];
$form['mail_templates']['watcher']['body'] = [
'#type' => 'textarea',
'#title' => $this->t('Default mail text for sending out notifications to commenters'),
'#default_value' => $config->get('mail_templates.watcher.body'),
'#cols' => 80,
'#rows' => 15,
'#token_types' => [
'comment'
],
'#element_validate' => ['token_element_validate'],
];
$form['mail_templates']['entity_author'] = [
'#type' => 'container',
'#tree' => TRUE,
];
$form['mail_templates']['entity_author']['body'] = [
'#type' => 'textarea',
'#title' => $this->t('Default mail text for sending out the notifications to node authors'),
'#default_value' => $config->get('mail_templates.entity_author.body'),
'#cols' => 80,
'#rows' => 15,
'#token_types' => [
'comment'
],
'#element_validate' => ['token_element_validate'],
];
$form['token_help'] = [
'#theme' => 'token_tree',
'#token_types' => [
'comment'
],
];
return parent::buildForm($form, $form_state);
}
public function validateForm(array &$form, FormStateInterface $form_state) {
if (!array_filter($form_state->getValue('available_alerts'))) {
$form_state->setErrorByName('available_alerts', 'You must enable at least one subscription mode.');
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('comment_notify.settings')
->set('node_types', array_keys(array_filter($form_state->getValue('node_types'))))
->set('available_alerts', $form_state->getValue('available_alerts'))
->set('enable_default', $form_state->getValue('enable_default'))
->set('mail_templates.watcher.body', $form_state->getValue(['mail_templates', 'watcher', 'body']))
->set('mail_templates.entity_author.body', $form_state->getValue(['mail_templates', 'entity_author', 'body']))
->save();
parent::submitForm($form, $form_state);
}
}
<?php
/**
* @file
* Contains \Drupal\comment_notify\Form\CommentNotifyUnsubscribe.
*/
namespace Drupal\comment_notify\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
* Unsubscribe form for Comment Notify.
*/
class CommentNotifyUnsubscribe extends FormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'comment_notify_unsubscribe';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['comment_notify_unsubscribe'] = [];