Skip to content

Commit

Permalink
FEATURE: Support Slack for sending notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
rolandschuetz committed Oct 19, 2019
1 parent e6c2b42 commit 92fe641
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 38 deletions.
121 changes: 99 additions & 22 deletions Classes/Notifier.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
<?php

declare(strict_types=1);

namespace CodeQ\PublishNotifier;

use Maknz\Slack\Client;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Log\SystemLoggerInterface;
use Neos\Flow\Configuration\Exception\InvalidConfigurationException;
use Neos\SwiftMailer\Message;
use Neos\ContentRepository\Domain\Model\Workspace;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\ContentRepository\Domain\Service\PublishingServiceInterface;
use Neos\Neos\Domain\Service\UserService;

class Notifier
Expand All @@ -21,6 +28,12 @@ class Notifier
*/
protected $userService;

/**
* @Flow\Inject
* @var PublishingServiceInterface
*/
protected $publishingService;

/**
* @Flow\InjectConfiguration(package="Neos.Flow", path="http.baseUri")
* @var string
Expand All @@ -43,36 +56,100 @@ public function injectSettings(array $settings) {
}

/**
* @param NodeInterface $node
* Send out emails for a change in a workspace.
*
* @param Workspace $targetWorkspace
* @return void
* @throws InvalidConfigurationException
*/
public function notify($node, $targetWorkspace)
protected function sendEmails($targetWorkspace)
{
if ($targetWorkspace->isPrivateWorkspace() || $targetWorkspace->isInternalWorkspace()) {
$currentUser = $this->userService->getCurrentUser();
$userName = $currentUser->getLabel();
$targetWorkspaceName = $targetWorkspace->getTitle();
$reviewUrl = sprintf('%1$s/neos/management/workspaces/show?moduleArguments[workspace][__identity]=%2$s', $this->baseUri, $targetWorkspace->getName());

$senderAddress = $this->settings['senderAddress'];
$senderName = $this->settings['senderName'];
$subject = sprintf($this->settings['subject'], $userName);
$body = sprintf($this->settings['body'], $userName, $targetWorkspaceName, $reviewUrl);

foreach ($this->settings['notifyEmails'] as $email) {
try {
$mail = new Message();
$mail
if(!$this->settings['email']['enabled']) {
return;
}
if(!$this->settings['email']['senderAddress']) {
throw new InvalidConfigurationException('The CodeQ.PublishNotifier email.senderAddress configuration does not exist.');
}
if(!$this->settings['email']['notifyEmails']) {
throw new InvalidConfigurationException('The CodeQ.PublishNotifier email.notifyEmails configuration does not exist.');
}

$currentUser = $this->userService->getCurrentUser();
$currentUserName = $currentUser->getLabel();
$targetWorkspaceName = $targetWorkspace->getTitle();
$reviewUrl = sprintf('%1$s/neos/management/workspaces/show?moduleArguments[workspace][__identity]=%2$s', $this->baseUri, $targetWorkspace->getName());

$senderAddress = $this->settings['email']['senderAddress'];
$senderName = $this->settings['email']['senderName'];
$subject = sprintf($this->settings['email']['subject'], $currentUserName);
$body = sprintf($this->settings['email']['body'], $currentUserName, $targetWorkspaceName, $reviewUrl);

foreach ($this->settings['email']['notifyEmails'] as $email) {
try {
$mail = new Message();
$mail
->setFrom(array($senderAddress => $senderName))
->setTo(array($email => $email))
->setSubject($subject);
$mail->setBody($body, 'text/plain');
$mail->send();
} catch (\Exception $exception) {
$this->systemLogger->logException($exception);
}
$mail->setBody($body, 'text/plain');
$mail->send();
} catch (\Exception $exception) {
$this->systemLogger->logException($exception);
}
}
}

/**
* Send out a slack message for a change in a workspace.
*
* @param Workspace $targetWorkspace
* @return void
* @throws InvalidConfigurationException
*/
protected function sendSlackMessages($targetWorkspace)
{
if(!$this->settings['slack']['enabled']) {
return;
}
if(empty($this->settings['slack']['postTo'])) {
throw new InvalidConfigurationException('The CodeQ.PublishNotifier slack.postTo configuration expects at least one target if enabled.');
}

$currentUser = $this->userService->getCurrentUser();
$currentUserName = $currentUser->getLabel();
$targetWorkspaceName = $targetWorkspace->getTitle();
$reviewUrl = sprintf('%1$s/neos/management/workspaces/show?moduleArguments[workspace][__identity]=%2$s', $this->baseUri, $targetWorkspace->getName());

$message = sprintf($this->settings['slack']['message'], $currentUserName, $targetWorkspaceName, $reviewUrl);

foreach ($this->settings['slack']['postTo'] as $postToKey => $postTo) {
if (empty($postTo['webhookUrl'])) {
throw new InvalidConfigurationException('The CodeQ.PublishNotifier slack.postTo ' . $postToKey . ' requires a webhookUrl.');
}

$client = new Client($postTo['webhookUrl'], $postTo['clientSettings']);
$slackMessage = $client->createMessage();
$slackMessage->send($message);
}
}

/**
* @param NodeInterface $node
* @param Workspace $targetWorkspace
* @return void
* @throws InvalidConfigurationException
*/
public function notify($node, $targetWorkspace)
{
if($this->publishingService->getUnpublishedNodes($targetWorkspace)) {
// there were already notifications, because of earlier changes.
return;
}
if ($targetWorkspace->isPersonalWorkspace() || $targetWorkspace->isPublicWorkspace()) {
return;
}

$this->sendEmails($targetWorkspace);
$this->sendSlackMessages($targetWorkspace);
}
}
25 changes: 18 additions & 7 deletions Configuration/Settings.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
CodeQ:
PublishNotifier:
senderName: 'Neos'
senderAddress: '[email protected]'
notifyEmails: []
subject: '%1$s has published changes'
body: |+
%1$s has published changes to the private workspace %2$s.
Please review the changes and publish to live: %3$s'
email:
enabled: false
senderName: 'Neos'
senderAddress: '[email protected]'
notifyEmails: []
subject: '%1$s has published changes'
body: |+
%1$s has published changes to the private workspace %2$s.
Please review the changes and publish to live: %3$s'
slack:
enabled: false
postTo: []
#example:
# webhookUrl: 'https://hooks.slack.com/services/...'
# clientSettings: [] # additional client configurations
message: |+
%1$s has published changes to the private workspace %2$s.
Please review the changes and publish to live: %3$s'
61 changes: 53 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,61 @@
# Neos - Publisher Notifier
# Neos CMS - Publisher Notifier

This package sends notifications to admins every time someone publishes to an internal workspace.
This package sends notifications every time someone publishes to an internal workspace. If the internal workspace
already has unpublished changes it will not send notifications anymore to not spam slack channels or email inboxes.

## Usage
Simply install the package via composer:

```bash
composer require codeq/publishnotifier
```

Make sure your site has `Neos.Flow.http.baseUri` setting set, so your reviewers would get correct urls.

## Configuration for email notifications

In order to send messages to emails you need to add configure the [neos/swiftmailer](https://swiftmailer-for-flow.readthedocs.io/en/latest/) credentials

Then you need to configure the target email addresses, together with the email content:

```yaml
CodeQ:
PublishNotifier:
email:
enabled: false
senderName: 'Neos'
senderAddress: '[email protected]'
notifyEmails:
- '[email protected]'
subject: '%1$s has published changes'
body: |+
%1$s has published changes to the private workspace %2$s.
Please review the changes and publish to live: %3$s'
```
## Configuration for slack messages
In order to send messages to Slack you need to add an incoming WebHook to your Slack workspace. Read more about it here [https://api.slack.com/incoming-webhooks](https://api.slack.com/incoming-webhooks)
As the incoming webhooks are treated as Slack Apps they are bound to a single channel. Therefore you can configure multiple "postTo" to use several webhooks:
```yaml
CodeQ:
PublishNotifier:
slack:
enabled: false
postTo: []
myExampleTarget:
webhookUrl: 'https://hooks.slack.com/services/...'
clientSettings: [] # additional client configurations
message: |+
%1$s has published changes to the private workspace %2$s.
Please review the changes and publish to live: %3$s'
```
Read more about the possible client settings and options here: https://github.com/maknz/slack#settings
1. Install the package: `composer require codeq/publishnotifier`
2. Setup SwiftMailer SMTP credentials if you haven't already.
3. Adjust the settings. See Settings.yaml file for all configuration settings.
4. Make sure your site has `Neos.Flow.http.baseUri` setting set, so your reviewers would get correct urls.
## Possible Improvements
- Implement throttling
- Automatically get email addresses from all live-publishers.
- Show visual diff of changes
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"license": "GPL-3.0+",
"require": {
"neos/neos": "~3.2 || ~4.0 || ~5.0 || dev-master",
"neos/swiftmailer": "*"
"neos/swiftmailer": "*",
"maknz/slack": "~1.7.0"
},
"autoload": {
"psr-4": {
Expand Down

0 comments on commit 92fe641

Please sign in to comment.