Commit ed0001e6 by Van

Initial commit

parents
config/config.yml
composer.lock
vendor/
tests/tmp/
sftp-config.json
The MIT License (MIT)
Copyright (c) 2016 Nicolas Béhier-Dévigne
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Send email for new content - Bolt Extension
===========================================
[Bolt](https://bolt.cm/) extension to send email when new content is published
### Known limitations
- Notification email send each time you "depublish -> publish", beacause I do not track notification already sent.
### Requirements
- Bolt 3.x installation
- [optional] [Newsletter Subscription](https://github.com/miguelavaqrod/bolt-newsletter-subscription) to save registered emails in database.
### Installation
1. Login to your Bolt installation
2. Go to "View/Install Extensions" (Hover over "Extras" menu item)
3. Type `sendemail-fornewcontent` into the input field
4. Click on the extension name
5. Click on "Browse Versions"
6. Click on "Install This Version" on the latest stable version
### Set up
Email notifications will be sent, you should configure the `mailoptions` setting in your Bolt `app/config/config.yml`.
**Note:**
- This extension uses the Swiftmailer library to send email notifications, based on the `mailoptions:` setting in your Bolt `app/config/config.yml` file.
- When first installed, Extension defaults to turning debugging on in the configuration. This should be turned off when deployed in production. When debugging is on, all outbound emails are sent to the configured debug email address.
- When you install Extension, you may have to create `app/config/extensions/boltsendemailfornewcontent.leskis.yml`.
**Tip:** If you want to modify the HTML templates, you should copy the `.yml` file to your `theme/` folder, and modify it there. Any changes in the file in the distribution might be overwritten after an update to the extension. For instance, if you copy `email.twig` to `theme/base-2016/my_email.twig`, the corresponding line in `config.yml` should be: `emailbody: my_email.twig`
### Extension Configuration
```(yml)
debug:
enabled: true
address: noreply@example.com # email used to send debug notifications
subscribers:
contenttype: subscribers # content type with the subscribers data
emailfield: email # email field name
# templates:
# emailbody: extensions/bolt-sendemail_fornewcontent/email_body.twig
# emailsubject: extensions/bolt-sendemail_fornewcontent/email_subject.twig
email:
from_name: Your website
from_email: your-email@your-website.com
# replyto_name: #
# replyto_email: #
notifications:
entries: # contenttype
# enabled: true
# subscribers:
# contenttype: subscribers
# emailfield: email
# filter:
# field: newcontentsubscription
# value: true
# debug: true
# email:
# from_name: # Default : Site name
# from_email: #
# replyto_name: #
# replyto_email: #
# templates: # Over ride the global Twig templates for this form
# emailbody: extensions/bolt-sendemail_fornewcontent/email_body.twig
# emailsubject: extensions/bolt-sendemail_fornewcontent/email_subject.twig
```
### Credits
Globally inspired by [BoltForms](https://github.com/bolt/boltforms) and [BoltBB](https://github.com/GawainLynch/bolt-extension-boltbb)
### License
This Bolt extension is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
{
"name": "appolo/bolt-sendemail-fornewcontent",
"description": "Bolt extension to send email when new content is published",
"type": "bolt-extension",
"keywords": [
"email",
"event",
"notification"
],
"require": {
"bolt/bolt": "^3.3"
},
"require-dev": {
"phpunit/phpunit": "^4.7"
},
"suggest": {
"miguelavaqrod/newsletter": "An extension to save newsletter email subscriptions."
},
"license": "MIT",
"authors": [
{
"name": "Nicolas Béhier-Dévigne",
"homepage": "http://www.leskis.net",
"role": "Developer"
}
],
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Bolt\\Extension\\Leskis\\BoltSendEmailForNewContent\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Bolt\\Extension\\Leskis\\BoltSendEmailForNewContent\\Tests\\": "tests",
"Bolt\\Tests\\": "vendor/bolt/bolt/tests/phpunit/unit/"
}
},
"extra": {
"bolt-class": "Bolt\\Extension\\Leskis\\BoltSendEmailForNewContent\\BoltSendEmailForNewContentExtension",
"bolt-icon": "extra/bolt-sendemail-fornewcontent-logo.png"
}
}
debug:
enabled: true
address: noreply@example.com # email used to send debug notifications
subscribers:
contenttype: subscribers # content type with the subscribers data
emailfield: email # email field name
# templates:
# emailbody: extensions/bolt-sendemail_fornewcontent/email_body.twig
# emailsubject: extensions/bolt-sendemail_fornewcontent/email_subject.twig
email:
from_name: Your website
from_email: your-email@your-website.com
# replyto_name: #
# replyto_email: #
notifications:
entries: # contenttype
# enabled: true
# subscribers:
# contenttype: subscribers
# emailfield: email
# filter:
# field: newcontentsubscription
# value: true
# debug: true
# email:
# from_name: # Default : Site name
# from_email: #
# replyto_name: #
# replyto_email: #
# templates: # Over ride the global Twig templates for this form
# emailbody: extensions/bolt-sendemail_fornewcontent/email_body.twig
# emailsubject: extensions/bolt-sendemail_fornewcontent/email_subject.twig
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false">
<testsuites>
<testsuite name="unit">
<directory>tests</directory>
</testsuite>
</testsuites>
<listeners>
<listener file="vendor/bolt/bolt/tests/phpunit/BoltListener.php" class="Bolt\Tests\BoltListener">
<arguments>
<!-- Configuration files. Can be either .yml or .yml.dist files -->
<!-- Locations can be relative to TEST_ROOT directory, the Bolt directory, or an absolute path -->
<array>
<element key="config">
<string>vendor/bolt/bolt/app/config/config.yml.dist</string>
</element>
<element key="contenttypes">
<string>vendor/bolt/bolt/app/config/contenttypes.yml.dist</string>
</element>
<element key="menu">
<string>vendor/bolt/bolt/app/config/menu.yml.dist</string>
</element>
<element key="permissions">
<string>vendor/bolt/bolt/app/config/permissions.yml.dist</string>
</element>
<element key="routing">
<string>vendor/bolt/bolt/app/config/routing.yml.dist</string>
</element>
<element key="taxonomy">
<string>vendor/bolt/bolt/app/config/taxonomy.yml.dist</string>
</element>
</array>
<!-- Theme directory. Can be relative to TEST_ROOT directory, the Bolt directory, or an absolute path -->
<array>
<element key="theme">
<string>theme/base-2014</string>
</element>
</array>
<!-- The Bolt SQLite database, leave empty to use the one bundled with Bolt's repository -->
<!-- Location can be relative to TEST_ROOT directory, the Bolt directory, or an absolute path -->
<array>
<element key="boltdb">
<string>vendor/bolt/bolt/tests/phpunit/unit/resources/db/bolt.db</string>
</element>
</array>
<!-- Reset the cache and test temporary directories -->
<boolean>true</boolean>
<!-- Create timer output in app/cache/phpunit-test-timer.txt -->
<boolean>true</boolean>
</arguments>
</listener>
</listeners>
</phpunit>
<?php
namespace Bolt\Extension\Leskis\BoltSendEmailForNewContent;
use Bolt\Events\StorageEvent;
use Bolt\Events\StorageEvents;
use Bolt\Extension\SimpleExtension;
use Bolt\Translation\Translator as Trans;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* BoltSendEmailForNewContent extension class.
*
* @author Nicolas Béhier-Dévigne
*/
class BoltSendEmailForNewContentExtension extends SimpleExtension
{
/**
* {@inheritdoc}
*/
protected function subscribe(EventDispatcherInterface $dispatcher)
{
// Pre-save hook
$dispatcher->addListener(StorageEvents::PRE_SAVE, [$this, 'hookPreSave']);
}
/**
* Pre-save hook
* @param \Bolt\Events\StorageEvent $event
*/
public function hookPreSave(StorageEvent $event)
{
$app = $this->getContainer();
// Get contenttype
$contenttype = $event->getContentType();
$aNotificationsContentTypes = $this->getNotifContentTypes();
if ( empty($contenttype)
|| empty($aNotificationsContentTypes)
|| !in_array($contenttype, $aNotificationsContentTypes) ) {
return;
}
// Get the record : Bolt\Storage\Entity\Content
$record = $event->getContent();
// Test if newly published
$contentNewlyPublished = false;
if ( $event->isCreate()
&& $record->getStatus() == 'published') {
$contentNewlyPublished = true;
}
else if ($record->getStatus() == 'published') {
// @todo : check if notification already sent
// use a temporary file or a db table ?
$repo = $app['storage']->getRepository($contenttype);
$oldRecord = $repo->find($record->getId() );
if ( !empty($oldRecord)
&& $oldRecord->getStatus() != 'published') {
$contentNewlyPublished = true;
}
}
if ($contentNewlyPublished) {
// Launch the notification
$notify = new Notifications($app, $this->getConfig(), $record, $contenttype);
// Search subscribers
try {
$aSubscribers = $this->getSubscribers($contenttype);
// Send email foreach subscriber
$notify->doNotification($aSubscribers);
} catch (\Exception $e) {
$app['logger.system']->error(sprintf("BoltSendEmailForNewContentExtension notifications can't be sent - %s", $e->getMessage() ), ['event' => 'extensions']);
return;
}
}
return;
}
/**
* Get Subscribers for notifications
* @throws Exception
* @return array records
*/
private function getSubscribers($contenttype)
{
$app = $this->getContainer();
$config = $this->getConfig();
$subConfig = $config['subscribers'];
$aSubscribers = false;
// Check if specific config inside ContentType config
if ( ! empty($contenttype) && array_key_exists('subscribers', $config['notifications'][$contenttype]) ) {
$subConfig = $config['notifications'][$contenttype]['subscribers'];
}
$entityName = $subConfig['contenttype'];
$fieldName = $subConfig['emailfield'];
$filter = array_key_exists('filter', $subConfig) ? $subConfig['filter'] : false;
try {
$meta = $app['storage.metadata']->getClassMetadata($entityName);
// Check if config email field exists
if ( array_key_exists($fieldName, $meta['fields'])
&& $meta['fields'][$fieldName]['type'] == 'string' ) {
$repo = $app['storage']->getRepository($entityName);
// Apply query filter if necessary
if ($filter) {
$aSubscribers = $repo->findBy([$filter['field'] => $filter['value']]);
}
else {
$aSubscribers = $repo->findAll();
}
}
} catch (\Exception $e) {
if ($filter) {
throw new \Exception(sprintf("No Subscribers table like %s with email field %s, filtered by %s = %s, as defined on config", $entityName, $fieldName, $filter['field'], $filter['value']), 1);
}
else {
throw new \Exception(sprintf("No Subscribers table like %s with email field %s, as defined on config", $entityName, $fieldName), 1);
}
}
return $aSubscribers;
}
/**
* Get ContentTypes which have subscriptions
* @return array
*/
private function getNotifContentTypes()
{
$config = $this->getConfig();
$aContentTypes = array_keys($config['notifications']);
foreach ($aContentTypes as $k => $sContentType) {
if ( array_key_exists('enabled', $config['notifications'][$sContentType])
&& $config['notifications'][$sContentType]['enabled'] == false ) {
unset($aContentTypes[$k]);
}
}
return $aContentTypes;
}
/**
* {@inheritdoc}
*/
protected function registerTwigPaths()
{
return [
'templates'
];
}
/**
* {@inheritdoc}
*/
protected function getDefaultConfig()
{
return [
'debug' => [
'enabled' => true
],
'templates' => [
'emailbody' => 'email_body.twig',
'emailsubject' => 'email_subject.twig'
],
'email' => [
'replyto_name' => NULL,
'replyto_email' => NULL
],
'notifications' => [
'entries' => [
'enabled' => true
],
]
];
}
/**
* {@inheritdoc}
*/
public function getDisplayName()
{
return 'Send email for new content';
}
}
<?php
namespace Bolt\Extension\Leskis\BoltSendEmailForNewContent;
use Silex;
use \Bolt\Storage\Entity\Content;
/**
* Notification class
*
* Copyright (C) 2014-2016 Gawain Lynch
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Gawain Lynch <gawain.lynch@gmail.com>
* @copyright Copyright (c) 2014, Gawain Lynch
* @license http://opensource.org/licenses/GPL-3.0 GNU Public License 3.0
*/
class Notifications
{
/**
* @var Application
*/
private $app;
/**
* @var array
*/
private $config;
/**
* @var bool
*/
private $debug;
/**
* @var string
*/
private $debug_address;
/**
* @var \Bolt\Content
*/
private $record;
/**
* @var string
*/
private $contentType;
/**
* @var string
*/
private $emailField;
/**
* @var string
*/
private $subjectTpl;
/**
* @var string
*/
private $bodyTpl;
/**
* @var string
*/
private $from_name;
/**
* @var string
*/
private $from_email;
/**
* @var string
*/
private $replyto_name;
/**
* @var string
*/
private $replyto_email;
/**
* @param Silex\Application $app
* @param \Bolt\Content $record
*/
public function __construct(Silex\Application $app, array $config, Content $record, $contenttype)
{
$this->app = $app;
$this->config = $config;
$this->record = $record;
$this->contentType = $contenttype;
$this->setVars();
}
/**
*
*/
public function doNotification($recipients)
{
// Sort out the "to whom" list
if ($this->debug) {
$this->recipients = [
new Content([
'title' => 'Test Showcase',
'slug' => 'test',
$this->emailField => $this->debug_address
])
];
} else {
// Get the subscribers
$this->recipients = $recipients;
}
foreach ($this->recipients as $recipient) {
// Get the email template
$this->doCompose($recipient);
$this->doSend($this->message, $recipient);
}
}
/**
* Compose the email data to be sent
*/
private function doCompose($recipient)
{
/*
* Subject
*/
$html = $this->app['render']->render($this->subjectTpl, [
'record' => $this->record
]);
$subject = new \Twig_Markup($html->getContent(), 'UTF-8');
/*
* Body
*/
$html = $this->app['render']->render($this->bodyTpl, [
'record' => $this->record,
'recipient' => $recipient
]);
$body = new \Twig_Markup($html->getContent(), 'UTF-8');
/*
* Build email
*/
$this->message = $this->app['mailer']
->createMessage('message')
->setSubject($subject)
->setFrom([$this->from_email => $this->from_name])
->setBody(strip_tags($body))
->addPart($body, 'text/html');
// Add reply to if necessary
if (! empty($this->replyto_email) ) {
$this->message->setReplyTo([$this->replyto_email => $this->replyto_name]);
}
}
/**
* Send a notification to a single user
*
* @param \Swift_Message $message
* @param array $recipient
*/
private function doSend(\Swift_Message $message, Content $recipient)
{
// Set the recipient for *this* message
$emailTo = $recipient->get($this->emailField);
try {
$message->setTo($emailTo);
// Queue the message in the mailer
$this->app['mailer']->send($message);
$this->app['logger.system']->info("Sent BoltSendEmailForNewContentExtension notification to {$emailTo}", ['event' => 'extensions']);
} catch (\Exception $e) {
$this->app['logger.system']->error(sprintf("BoltSendMailForNewContentExtension failed for address {$emailTo} - %s", $e->getMessage() ), ['event' => 'extensions']);
}
}
/**
* Set Config vars
*/
private function setVars()
{
// Set Debug
$this->debug = $this->config['debug']['enabled'];
$this->debug_address = $this->config['debug']['address'];
if ( $this->debug
&& ! empty($this->contentType)
&& array_key_exists('debug', $this->config['notifications'][$this->contentType]) ) {
if ( $this->config['notifications'][$this->contentType]['debug'] == false ) {
$this->debug = false;
}
}
// Set Email Field From Subscribers
$this->emailField = $this->config['subscribers']['emailfield'];
if ( ! empty($this->contentType)
&& array_key_exists('subscribers', $this->config['notifications'][$this->contentType]) ) {
if ( isset($this->config['notifications'][$this->contentType]['subscribers']['emailfield']) ) {
$this->emailField = $this->config['notifications'][$this->contentType]['subscribers']['emailfield'];
}
}
// Get Templates
$this->subjectTpl = $this->config['templates']['emailsubject'];
$this->bodyTpl = $this->config['templates']['emailbody'];
if ( ! empty($this->contentType)
&& array_key_exists('templates', $this->config['notifications'][$this->contentType]) ) {
if ( isset($this->config['notifications'][$this->contentType]['templates']['emailsubject']) ) {
$this->subjectTpl = $this->config['notifications'][$this->contentType]['templates']['emailsubject'];
}
if ( isset($this->config['notifications'][$this->contentType]['templates']['bodysubject']) ) {
$this->bodyTpl = $this->config['notifications'][$this->contentType]['templates']['bodysubject'];
}
}
// Get Sender
$this->from_name = $this->config['email']['from_name'];
$this->from_email = $this->config['email']['from_email'];
$this->replyto_name = $this->config['email']['replyto_name'];
$this->replyto_email = $this->config['email']['replyto_email'];
if ( ! empty($this->contentType)
&& array_key_exists('email', $this->config['notifications'][$this->contentType]) ) {
if ( isset($this->config['notifications'][$this->contentType]['email']['from_name'])
&& ! empty($this->config['notifications'][$this->contentType]['email']['from_name']) ) {
$this->from_name = $this->config['notifications'][$this->contentType]['email']['from_name'];
}
if ( isset($this->config['notifications'][$this->contentType]['email']['from_email'])
&& ! empty($this->config['notifications'][$this->contentType]['email']['from_email']) ) {
$this->from_email = $this->config['notifications'][$this->contentType]['email']['from_email'];
}
if ( isset($this->config['notifications'][$this->contentType]['email']['replyto_name'])
&& ! empty($this->config['notifications'][$this->contentType]['email']['replyto_name']) ) {
$this->replyto_name = $this->config['notifications'][$this->contentType]['email']['replyto_name'];
}
if ( isset($this->config['notifications'][$this->contentType]['email']['replyto_email'])
&& ! empty($this->config['notifications'][$this->contentType]['email']['replyto_email']) ) {
$this->replyto_email = $this->config['notifications'][$this->contentType]['email']['replyto_email'];
}
}
}
}
BoltSendEmailForNewContent Templates
====================================
Here, you will find the defaults templates:
* `email.twig` — Email notification body template
* `_subject.twig` — Email notification subject line template
{#
# Passed in variables:
# record — Record data
# recipient — Email recipient data
#}
<p>Hi {{ recipient.email }},</p>
<p>A new {{ record.contenttype.singular_name }} has been published : <a href="{{ app.paths.rooturl }}{{ record.link|slice(1) }}">{{ record.title }}</a>.</p>
<hr>
<em>Sent by the <a href="https://github.com/nbehier/bolt-sendemail-fornewcontent">BoltSendEmailForNewContent</a> extension for <a href="https://bolt.cm">Bolt</a>.</em>
{#
# Passed in variables:
# record — Record data
#}
{% spaceless %}
[{{ app.config.get('general/sitename') }}] A new {{ record.contenttype.singular_name }} has been published
{% endspaceless %}
<?php
namespace Bolt\Extension\Leskis\BoltSendEmailForNewContent\Tests;
use Bolt\Tests\BoltUnitTest;
use Bolt\Extension\Leskis\BoltSendEmailForNewContent\BoltSendEmailForNewContentExtension;
/**
* BoltSendEmailForNewContent testing class.
*
* @author Nicolas Béhier-Dévigne
*/
class ExtensionTest extends BoltUnitTest
{
/**
* Ensure that the BoltSendEmailForNewContent extension loads correctly.
*/
public function testExtensionBasics()
{
$app = $this->getApp(false);
$extension = new BoltSendEmailForNewContentExtension($app);
$name = $extension->getName();
$this->assertSame($name, 'BoltSendEmailForNewContent');
$this->assertInstanceOf('\Bolt\Extension\ExtensionInterface', $extension);
}
public function testExtensionComposerJson()
{
$composerJson = json_decode(file_get_contents(dirname(__DIR__) . '/composer.json'), true);
// Check that the 'bolt-class' key correctly matches an existing class
$this->assertArrayHasKey('bolt-class', $composerJson['extra']);
$this->assertTrue(class_exists($composerJson['extra']['bolt-class']));
// Check that the 'bolt-assets' key points to the correct directory
$this->assertArrayHasKey('bolt-assets', $composerJson['extra']);
$this->assertSame('web', $composerJson['extra']['bolt-assets']);
}
}
<?php
include_once __DIR__ . '/../vendor/bolt/bolt/tests/phpunit/bootstrap.php';
define('EXTENSION_AUTOLOAD', realpath(dirname(__DIR__) . '/vendor/autoload.php'));
require_once EXTENSION_AUTOLOAD;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment