This is a major enhancement of the Content Moderation Notifications (CMN) module allowing complete control of TO, CC, BCC, FROM, REPLY-TO, SUBJECT, and MESSAGE fields, adding full Twig support to all fields, changing Token support, allowing dynamic messages to be displayed, improved support for hook_mail_alter(), intelligent non-sending of notifications if no recipients or if a specified "Abort" email is found, expanded help text on fields, a VERY helpful debugging mode (no more switching back and forth between email and web a zillion times) and on-screen list of commonly used {{ variables }}.

  1. Add Twig support
    1. Working patch in #2953489: Add Twig support in Subject, Email, and Message fields, which is being dropped in favor of this expanded module.
    2. All text fields are now draggable-expandable textarea fields
    3. Helpful UI feature auto-expands all fields (on page load) to show full contents of all fields without having to manually resize them
  2. Change Token support
    - (Left this as is and expanded Token support to all fields for better compatability)
    1. We could add direct parsing of tokens as well, but I think using the Twig Tweak module's drupal_token() function is
      sufficient, as it allows the use of all tokens within Twig's framework
    2. Supporting both Twig and Token could result in unwanted tokenizing of Twig output (or vice versa)
    3. Using token filtering could result in the stripping of token-like text from fields, such as "[DRAFT] {{ title }}" in a Subject field
  3. Replace "Recipients" (in #2946360: Make the recipient configurable) and "Adhoc Email addresses" fields with more standard TO, CC, BCC, FROM and REPLY-TO
    fields. (high priority)
    1. This would allow people to form the emails however they want, particularly if we include the Twig templating
      logic on all of them.
  4. Prevent sending emails if no recipients
    1. No email will be sent if there are no recipients specified in the TO, CC and BCC fields.
  5. Allow an Abort ("kill-switch") email address (maybe "abort@example.com" or "noemail@example.com") that, if found in any
    address or message field, will cause the module to NOT send a notification message. (Medium Priority)
    1. This would be useful in use cases such as "if the editor is also the author, don't send them an email (since
      they already know it's reviewable or published)", and the twig logic could return the abort@example.com email in
      this case to prevent emails from being sent.
    2. The current module would still send the email to the Recipient (site email address) even if no other
      recipients are specified.
  6. Allow Dynamic messages to be displayed on-screen after notifications are sent by using Twig Tweak module's drupal_set_message() function. For example, in any field you can add the Twig code:
    1. {{ drupal_set_message( ':author has been notified of this edit'|t({':author' : user.displayName})) }}
  7. Add a "Debug Mode" checkbox that would display all the fields (To, Cc, Bcc, From, Message, etc.) to the screen via
    drupal_set_message() and NOT send out any emails.
    1. This will make debugging messages and Twig code much simpler and much less spammy.
    2. Debugging can be done without switching between Drupal and email apps.
  8. Pass mail key containing Notification.id instead of module name
    1. This would allow hook_mail_alter() calls to be able to target specific notifications
  9. Add an expandable "Common Replacement Patterns" section to the Edit Notification page
    1. Include a list of the most common tags, such as {{title}}, used in the messages to make life easier for admins
  10. Figure out a decent way of having non-WYSIWYG Twig code in the Message value (Lower Priority)
    1. Putting Twig code in the WYSIWYG can be challenging as it adds DIV, SPAN and NBSP in the code
    2. Working in Source view defeats the point of having the WYSIWYG
    3. Simple solution would be to include an external template. Examples:
      1. {% include('cmn--editorial--draft.twig.html') %}{# have the template do all logic and output #}
      2. {% include('cmn--editorial--draft.twig.html') %}{# have the template do only logic and put output after here #}{{ user.email }} Edited this Draft
    4. Any better ideas on how to do this?
  11. Figure out a better way of handling Twig errors than WSOD and having to look at the logs for the error
    message
    1. Maybe wrap in try..catch and display an error (at least if in Debugging mode)

These updates aren't perfect yet but I wanted to get it up ASAP to gather any feedback before final polishing. Any help (particularly with writing tests) is welcome!

The existing patch is fully functional and is in production use now.

Things Still To Do (that I know of):

  1. Fix ConfigFactory dependency injection
    1. Currently getting settings directly
  2. Remove Token processing code - Decided against. Added Token processing to all fields for compatibility.
    1. Make Token's context a Twig variable so it can be passed via drupal_token() if needed
    2. Remove token processing call where used in module
  3. Update/Create Tests
    1. Update existing tests to work with new fields
    2. Create new tests to validate new functionality
  4. Update README.txt
    1. Update for the new features
    2. Provide examples
  5. Clean up code
    1. Remove commented debugging/obsolete code
    2. Conform to Coding Standards

Comments

diamondsea created an issue. See original summary.

diamondsea’s picture

Screenshot of updated Notification Listing Page

Added additional column for debugging and showing the various fields/templates used for each notification
screenshot of listing page

Screenshot of updated Notification Edit Page

Added new fields and updated help text for previous fields (roles, author) to describe how they will interact with the new fields. Fields will automatically expand to show all text within them (at least according to the number of newlines, could be off if lines are longer than the field width).
Screenshot of Edit Notification page

Screenshot of updated Notification Edit Page with expanded Help section

Shows the expanded "Common Replacement Patterns" fieldset with the most-used codes provided.
Screenshot of Edit Notification page with expanded help section

Screenshot of Notification Debugging output and Dynamic Message

The first line "admin has been notified of this edit" is custom drupal_set_message() output defined in the twig template. Any template can output messages to the user to appear on the screen, including identifying them as "warning" or "error" if desired.

The Twig Tweak module is needed for the drupal_set_message() and drupal_token() functions, if needed.

Below the message is the notification's debugging output, showing all the templates and resulting output for each field, as well as diagnostic information if a message would not be sent for any reason.

The final message is the standard message Drupal provides when a node is updated.
Screenshot of Notification Debugging output and Dynamic Message

diamondsea’s picture

Issue summary: View changes
diamondsea’s picture

Issue summary: View changes
StatusFileSize
new41.59 KB

Initial monster patch updating everything

diamondsea’s picture

Issue summary: View changes

Note: the above patch should install properly and be fully functional despite any test failures.

See the "Things to Do" section at the bottom of the Issue Summary for details.

2pha’s picture

These additions are great.
Thanks a lot.

I did have a bit of a problem though.

I am wanting to send html emails.
I removed the line:
$email_message = PlainTextOutput::renderFromHtml($email_message);

And I also had to set the content-type header:
$data['params']['headers']['Content-Type'] = 'text/html; charset=UTF-8';

diamondsea’s picture

Issue tags: +#Nashville2018 #TwigEverywhere

@2pha The updated patch should work better. You'll probably want to use the https://www.drupal.org/project/swiftmailer module with https://www.drupal.org/project/mailsystem for HTML emails and not put the headers in this module. The next patch should work better with it as well.

diamondsea’s picture

Issue tags: -#Nashville2018 #TwigEverywhere +#Nashville2018, +#TwigEverywhere
StatusFileSize
new57.21 KB

Updated patch adding Token support for all fields, improved test coverage.
Needs install script to update existing schemas/configs.
Uninstall/Reinstall until then to get it to work on a system with existing CMN module.

diamondsea’s picture

Issue summary: View changes
dhumed’s picture

Thank you for your great work on this @diamondsea ! This should definitely be part of the CMN module. I am using SMTP for the email functionality. Emails are sent out successfully after I edit content. After I edit content however, I get this error:

Notice: Undefined variable: content_type in Drupal\smtp\Plugin\Mail\SMTPMailSystem->mail() (line 294 of modules/contrib/smtp/src/Plugin/Mail/SMTPMailSystem.php).

After digging into that part of the SMTP module's code, it seems that the error is related to headers.

Adding $data['params']['headers']['Content-Type'] = 'text/plain; charset=UTF-8'; to the Notification.php file solved that issue.

diamondsea’s picture

I'm not sure about the effects of adding the Content-Type header - wouldn't adding that break mail for people using MIME emails?

2pha’s picture

Adding a text/plain content-type header will break it for mime mail users, notice I had to hard code it to text/html as I am using mime mails.
It somehow needs to be dynamic or selectable.

diamondsea’s picture

I would think that this header should be added by one of the mail-handling modules depending on what is being sent, not by this module.

Anybody have more insight into this than I do?

khaled.zaidan’s picture

StatusFileSize
new57.25 KB

Just one thing with replacing the headers in content_moderation_notifications_mail(), as this seems to replacing a bunch of headers set in core that we'd like to keep.

Can instead do this:
CODE

so we're only adding/replacing the ones in our $params['headers'], without affecting any extra headers in $message['headers'] that we're unaware of.

Patch attached.

diamondsea’s picture

Can you add an interdiff for your patch so we can see what you changed?

dhumed’s picture

StatusFileSize
new56.81 KB

Here is my patch describing what I added in comment #10

Anonymous’s picture

StatusFileSize
new58.4 KB

re-roll patch #14 to work with latest dev version.

jhedstrom’s picture

Status: Active » Needs review
jhedstrom’s picture

I wonder if we could split the Twig support out into a separate issue? This patch is attempting to change quite a bit all at once.

Status: Needs review » Needs work

The last submitted patch, 17: cmn-twig-2959026-17.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

diamondsea’s picture

The Twig support part of it is actually pretty trivial (and the conditional logic it allows critical for my use-case).

I think the test is failing because I'm not modifying the schema in the upgrade process (but works great if you enable the already-patched version), but I haven't had a chance to fix it yet (I got moved to a new project).

netsliver’s picture

In Content Moderation Notifications, which token is efficient for the contributor to be notified ? (we do not want to notify the original author but the person who edited and modified the content after) we tested with those token : {{ user.email }}, [latest_revision_author:mail], [latest_revision_author:email], {{ revision-user:mail }}, [current-user:mail]
(even though after installing the patch https://www.drupal.org/project/content_moderation_notifications/issues/2...), but no one work. An idea ?

diamondsea’s picture

From https://drupal.stackexchange.com/a/258937:

If you need the user who created the revision, you need to use $node->getRevisionUser(), or $node->getRevisionUserId() for the user ID.

I'm not sure offhand if you can get that directly from the Twig node object or if you'd need to create a twig function to return it.

diamondsea’s picture

anruether’s picture

I just want to mention that this patch blocks other patches like 3015276. I guess it's because it changes so much code of the original module...

anruether’s picture

With #17 installed I get this error on every attempt to create node or block. The creation fails with a WSOD. It goes away after removing the patch.

TypeError: Argument 6 passed to Drupal\content_moderation_notifications\Notification::__construct() must implement interface Drupal\Core\Config\ConfigFactoryInterface, instance of Drupal\token\TokenEntityMapper given, called in /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/Container.php on line 282 in Drupal\content_moderation_notifications\Notification->__construct() (line 92 of /var/www/html/web/modules/contrib/content_moderation_notifications/src/Notification.php)

#0 /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/Container.php(282): Drupal\content_moderation_notifications\Notification->__construct(Object(Drupal\Core\Session\AccountProxy), Object(Drupal\Core\Entity\EntityTypeManager), Object(Drupal\Core\Mail\MailManager), Object(Drupal\Core\Extension\ModuleHandler), Object(Drupal\content_moderation_notifications\NotificationInformation), Object(Drupal\token\TokenEntityMapper))
#1 /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/Container.php(171): Drupal\Component\DependencyInjection\Container->createService(Array, 'content_moderat...')
#2 /var/www/html/web/core/lib/Drupal.php(158): Drupal\Component\DependencyInjection\Container->get('content_moderat...')
#3 /var/www/html/web/modules/contrib/content_moderation_notifications/content_moderation_notifications.module(33): Drupal::service('content_moderat...')
#4 [internal function]: content_moderation_notifications_entity_insert(Object(Drupal\block_content\Entity\BlockContent))
#5 /var/www/html/web/core/lib/Drupal/Core/Extension/ModuleHandler.php(403): call_user_func_array('content_moderat...', Array)
#6 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php(206): Drupal\Core\Extension\ModuleHandler->invokeAll('entity_insert', Array)
#7 /var/www/html/web/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php(756): Drupal\Core\Entity\EntityStorageBase->invokeHook('insert', Object(Drupal\block_content\Entity\BlockContent))
#8 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php(507): Drupal\Core\Entity\ContentEntityStorageBase->invokeHook('insert', Object(Drupal\block_content\Entity\BlockContent))
#9 /var/www/html/web/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php(641): Drupal\Core\Entity\EntityStorageBase->doPostSave(Object(Drupal\block_content\Entity\BlockContent), false)
#10 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php(432): Drupal\Core\Entity\ContentEntityStorageBase->doPostSave(Object(Drupal\block_content\Entity\BlockContent), false)
#11 /var/www/html/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php(774): Drupal\Core\Entity\EntityStorageBase->save(Object(Drupal\block_content\Entity\BlockContent))
#12 /var/www/html/web/core/lib/Drupal/Core/Entity/Entity.php(390): Drupal\Core\Entity\Sql\SqlContentEntityStorage->save(Object(Drupal\block_content\Entity\BlockContent))
#13 /var/www/html/web/core/modules/block_content/src/BlockContentForm.php(49): Drupal\Core\Entity\Entity->save()
#14 [internal function]: Drupal\block_content\BlockContentForm->save(Array, Object(Drupal\Core\Form\FormState))
#15 /var/www/html/web/core/lib/Drupal/Core/Form/FormSubmitter.php(111): call_user_func_array(Array, Array)
#16 /var/www/html/web/core/lib/Drupal/Core/Form/FormSubmitter.php(51): Drupal\Core\Form\FormSubmitter->executeSubmitHandlers(Array, Object(Drupal\Core\Form\FormState))
#17 /var/www/html/web/core/lib/Drupal/Core/Form/FormBuilder.php(589): Drupal\Core\Form\FormSubmitter->doSubmitForm(Array, Object(Drupal\Core\Form\FormState))
#18 /var/www/html/web/core/lib/Drupal/Core/Form/FormBuilder.php(318): Drupal\Core\Form\FormBuilder->processForm('block_content_b...', Array, Object(Drupal\Core\Form\FormState))
#19 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityFormBuilder.php(48): Drupal\Core\Form\FormBuilder->buildForm('block_content_b...', Object(Drupal\Core\Form\FormState))
#20 /var/www/html/web/core/modules/block_content/src/Controller/BlockContentController.php(114): Drupal\Core\Entity\EntityFormBuilder->getForm(Object(Drupal\block_content\Entity\BlockContent))
#21 [internal function]: Drupal\block_content\Controller\BlockContentController->addForm(Object(Drupal\block_content\Entity\BlockContentType), Object(Symfony\Component\HttpFoundation\Request))
#22 /var/www/html/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array(Array, Array)
#23 /var/www/html/web/core/lib/Drupal/Core/Render/Renderer.php(582): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#24 /var/www/html/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#25 /var/www/html/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)
#26 /var/www/html/vendor/symfony/http-kernel/HttpKernel.php(151): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#27 /var/www/html/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#28 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#29 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#30 /var/www/html/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(99): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#31 /var/www/html/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(78): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#32 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#33 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#34 /var/www/html/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#35 /var/www/html/web/core/lib/Drupal/Core/DrupalKernel.php(665): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#36 /var/www/html/web/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#37 {main}
el1_1el’s picture

seems to be working for me. Thanks all!

mattsqd’s picture

StatusFileSize
new63.62 KB
new3.76 KB

I updated patch 17 to:

  • Use Drupal\Core\Url instead of Drupal\Core\URL to fix failed tests.
  • If two notifications were created on the same transition, the body from the first would still be in the first entry of $data['params']['body'] for the second.
  • Implemented TODO for using state label instead of state ID.
mattsqd’s picture

StatusFileSize
new63.55 KB
new3.76 KB

Updated #28 so that the body of the email created was still an array.

avpaderno’s picture

Issue tags: -#TwigEverywhere
el1_1el’s picture

StatusFileSize
new59.02 KB
new23.02 KB

patch in 17 and 29 no longer applied to latest version. rerolling

el1_1el’s picture

StatusFileSize
new59.14 KB

reroll for 3.2 only
this patch does not apply to dev

caspervoogt’s picture

I couldn't get any of the patches from 2019 to apply, and didn't try any of the 2018 ones. I'm running 3.x-dev on Drupal 8.8.1.

Adamation’s picture

Any update on re-rolling this patch? Patch 32 failed for me too.

hgupta28’s picture

StatusFileSize
new54.98 KB

Updated the patch to work with Version 8.x-3.3.

hgupta28’s picture

StatusFileSize
new54.82 KB

Updated the patch to work with Version 8.x-3.3. Resolved multiple notifications not being generated.

letrollpoilu’s picture

StatusFileSize
new152.72 KB

I could apply #36 with the 3.3 version. I cleared the caches, ran cron and did the db updates.
I still don't have all the fields, this is pretty strange. Here is what I have:

issue

avpaderno’s picture

@hgupta28 The patch needs to be provided for the 8.x-3.x branch (the one used for the 8.x-3.x-dev development snapshot). When fixing bugs or implementing code for a feature request, it's the branch code that is changed.

anruether’s picture

Not sure if this patch will ever be committed. @jhedstrom suggested in #19 (3 yrs ago) to split off the twig functionality.

rob holmes’s picture

@anruether the scope of this issue is too wide to really be able to be properly reviewed, tested and committed as a whole. It would have to be split out into much smaller patches each focusing on specific elements to be dealt with in isolation to get to the point where it can be added to a release etc.

Once aspects are completed separately they can be removed from this patch until this patch is no longer necessary.

avpaderno’s picture

I agree: A 54 KB patch is too wide to be properly handled.
I would split the issue and keep in this the code that is common to all the issues, if such code exists.

dww’s picture

Agreed with #19 and #39-#41. This patch is way too big and changing way too many things to be reviewed, tested and committed in one shot. We need to split this up into child issues of more manageable scope. As-is, this will never be committed. Tagging for rescope and summary update.

https://www.drupal.org/docs/develop/issues/issue-procedures-and-etiquett... for the interested reader. 😉 While that's specific to core, the principles are generally applicable.

Thanks,
-Derek

dww’s picture

Category: Feature request » Plan
Status: Needs work » Active
Related issues: -#2953489: Add Twig support in Subject, Email, and Message fields

For starters, I re-opened #2953489-13: Add Twig support in Subject, Email, and Message fields and posted a patch there (with tests) for Twig support in body, subject and to (adhoc 'emails') fields. Reviews most welcome!

Thanks,
-Derek

p.s. Turning this issue into an active 'plan', and put #2953489 as a child. This summary/issue still needs major help, so leaving it tagged for such.

khaled.zaidan’s picture

StatusFileSize
new55.1 KB

Just re-uploading the patch from #36 with deprecated code replaced (for D9-compatibility).

avpaderno’s picture

@khaled.zaidan See the previous comments: Patches should not be provided for this issue, which is categorized as plan.

avpaderno’s picture

Issue tags: -#Nashville2018 +Nashville2018
teknocat’s picture

All of this stuff would be ideal - especially being able to use twig in the message body.

dww’s picture

bkosborne’s picture

Status: Active » Closed (won't fix)
Related issues: +#3273627: Provide full to/cc/bcc email configuration

Since Twig support was already added, the next step here would be to split out more of this work into additional separate issues to make them easier to review and merge in.

Note that there's another issue for adding support for more control of the To, Cc, Bcc, etc. headers: #3273627: Provide full to/cc/bcc email configuration. I haven't compared the implementation details of that issue with this one yet but I plan to.

The other functionality of this (abort support, debugging, tips, etc.) should all be split out into separate issues.

Now that this issue is closed, please review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, please credit people who helped resolve this issue.