Skip to content

Commit

Permalink
1.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
ryssbowh committed Jan 8, 2022
1 parent 79dfedd commit 8450632
Show file tree
Hide file tree
Showing 23 changed files with 585 additions and 91 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
# ryssbowh/craft-emails Changelog

## 1.0.1 - 09/01/2022

### Fixed
- Compress email logs setting
- Email duplication in email shots

### Added
- "View emails" button on email shots dashboard
- Mailchimp lists integration through API

## 1.0.0 - 08/01/2022

### Added
- Initial release
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@ Define a new email shots using the dashboard :

You can either save an email shot for future use, or create a quick shot that will be sent instantly.

Each of them will require an email, and some emails to send to, which can come from 3 places :
Each of them will require an email, and some emails to send to, which can come from 4 places :
- A source : A source of emails, the default comes with a "All users" and a source for each user group. More source can be defined
- Users : Choose users
- Email : Enter emails
- Mailchimp lists : Enter your api key in the settings to enable your lists. Lists will be cached for 30min by default

Email shots can use the queue to send emails, using the queue present advantages as emails will be sent in the background, but you do need to run the queue manually if you're scheduling email shots.

Expand Down Expand Up @@ -149,5 +150,4 @@ Craft >= 3.5

## Roadmap

- Add a trigger system to send emails automatically when something happens on the system
- mailchimp lists integration, as sources
- Add a trigger system to send emails automatically when something happens on the system
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
],
"require": {
"craftcms/cms": "^3.5.0",
"craftcms/redactor": "^2.7"
"craftcms/redactor": "^2.7",
"drewm/mailchimp-api": "^2.5"
},
"autoload": {
"psr-4": {
Expand Down
29 changes: 28 additions & 1 deletion src/Emails.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
use Ryssbowh\CraftEmails\Services\EmailShotsService;
use Ryssbowh\CraftEmails\Services\EmailSourceService;
use Ryssbowh\CraftEmails\Services\EmailsService;
use Ryssbowh\CraftEmails\Services\MailchimpService;
use Ryssbowh\CraftEmails\emailSources\AllUsersEmailSource;
use Ryssbowh\CraftEmails\emailSources\MailchimpEmailSource;
use Ryssbowh\CraftEmails\emailSources\UserGroupEmailSource;
use craft\base\Plugin;
use craft\events\RebuildConfigEvent;
use craft\events\RegisterCacheOptionsEvent;
use craft\events\RegisterComponentTypesEvent;
use craft\events\RegisterEmailMessagesEvent;
use craft\events\RegisterUrlRulesEvent;
Expand All @@ -21,6 +24,7 @@
use craft\services\SystemMessages;
use craft\services\UserPermissions;
use craft\services\Utilities;
use craft\utilities\ClearCaches;
use craft\utilities\SystemMessages as SystemMessagesUtility;
use craft\web\UrlManager;
use craft\web\twig\variables\CraftVariable;
Expand Down Expand Up @@ -60,7 +64,8 @@ public function init()
$this->setComponents([
'emails' => EmailsService::class,
'emailSources' => EmailSourceService::class,
'emailShots' => EmailShotsService::class
'emailShots' => EmailShotsService::class,
'mailchimp' => MailchimpService::class,
]);

$this->registerProjectConfig();
Expand All @@ -70,6 +75,7 @@ public function init()
$this->registerTwigVariables();
$this->registerPermissions();
$this->registerEmailSources();
$this->registerClearCacheEvent();

if (Craft::$app->request->getIsConsoleRequest()) {
$this->controllerNamespace = 'Ryssbowh\\CraftEmails\\console';
Expand Down Expand Up @@ -115,6 +121,22 @@ public function getCpNavItem ()
return null;
}

/**
* Registers Clear cache options
*/
protected function registerClearCacheEvent()
{
Event::on(ClearCaches::class, ClearCaches::EVENT_REGISTER_CACHE_OPTIONS, function (RegisterCacheOptionsEvent $event) {
$event->options[] = [
'key' => 'mailchimp_lists',
'label' => Craft::t('emails', 'Mailchimp lists'),
'action' => function () {
Emails::$plugin->mailchimp->clearCaches();
}
];
});
}

/**
* Register default email sources
*/
Expand All @@ -130,6 +152,11 @@ function (RegisterEmailSourcesEvent $e) {
'group' => $group
]));
}
foreach (Emails::$plugin->mailchimp->lists as $list) {
$e->add(new MailchimpEmailSource([
'id' => $list['id']
]));
}
}
);
}
Expand Down
46 changes: 27 additions & 19 deletions src/Models/EmailShot.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,30 +101,53 @@ public function defineRules(): array
}
}, 'on' => 'create'],
['emails', function () {
foreach ($this->emails as $email) {
if ($names = \Craft::$app->request->getBodyParam('names')) {
$emails = [];
foreach ($this->emails as $index => $email) {
if ($email) {
$emails[$email] = $names[$index];
}
}
$this->emails = $emails;
}
foreach ($this->emails as $email => $name) {
if ($email and !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->addError('emails', \Craft::t('emails', 'Email ' . $email . ' is not a valid email'));
$this->addError('emails', \Craft::t('yii', '{attribute} is not a valid email address.', ['attribute' => $email]));
}
}
}],
['users', function () {
foreach ($this->users as $user) {
$elem = User::find()->id($user)->one();
if (!$elem) {
$this->addError('users', \Craft::t('emails', 'User ' . $user . " doesn't exist"));
$this->addError('users', \Craft::t('emails', "User {user} doesn't exist", ['user' => $user]));
}
}
}],
['sources', function () {
foreach ($this->sources as $source) {
if (!Emails::$plugin->emailSources->has($source)) {
$this->addError('sources', \Craft::t('emails', 'Source ' . $source . " doesn't exist"));
$this->addError('sources', \Craft::t('emails', "Source {source} doesn't exist", ['source' => $source]));
}
}
}]
];
}

public function __serialize(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'handle' => $this->handle,
'sources' => $this->sources,
'users' => $this->users,
'emails' => $this->emails,
'email_id' => $this->email_id,
'useQueue' => $this->useQueue,
'saveLogs' => $this->saveLogs
];
}
/**
* Users setter
*
Expand Down Expand Up @@ -260,21 +283,6 @@ public function getAllEmails(): array
foreach ($this->sourceObjects as $source) {
$emails = array_merge($emails, $source->emails);
}
if (Emails::$plugin->settings->removeShotDuplicates) {
$allAddresses = [];
$filtered = [];
foreach ($emails as $address => $name) {
if (is_int($address)) {
$address = $name;
$name = null;
}
if (!in_array($address, $allAddresses)) {
$filtered[$address] = $name;
$allAddresses[] = $address;
}
}
$emails = $filtered;
}
return $emails;
}

Expand Down
29 changes: 29 additions & 0 deletions src/Models/MailchimpList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Ryssbowh\CraftEmails\Models;

use yii\base\DynamicModel;

class MailchimpList extends DynamicModel
{
/**
* @var MailchimpMember[]
*/
public $members = [];

/**
* Get all subscribed members emails of this list
*
* @return string[]
*/
public function getEmails(): array
{
$emails = [];
foreach ($this->members as $member) {
if ($member->status == 'subscribed') {
$emails[$member->email_address] = $member->full_name;
}
}
return $emails;
}
}
9 changes: 9 additions & 0 deletions src/Models/MailchimpMember.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Ryssbowh\CraftEmails\Models;

use yii\base\DynamicModel;

class MailchimpMember extends DynamicModel
{
}
11 changes: 8 additions & 3 deletions src/Models/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ class Settings extends Model
/**
* @var boolean
*/
public $removeShotDuplicates = true;
public $compressLogs = true;

/**
* @var boolean
* @var string
*/
public $mailchimpApiKey;

/**
* @var integer
*/
public $compressLogs = false;
public $mailchimpCacheDuration = 60;

/**
* Get all parameters that can be considered config
Expand Down
2 changes: 1 addition & 1 deletion src/Services/EmailShotsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ public function sendNow(EmailShot $shot): bool
*/
public function getRecordById(int $id): EmailShotRecord
{
$shot = EmailShotRecord::find(['id' => $id])->one();
$shot = EmailShotRecord::find()->where(['id' => $id])->one();
if (!$shot) {
throw EmailShotException::noIdRecord($id);
}
Expand Down
104 changes: 104 additions & 0 deletions src/Services/MailchimpService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace Ryssbowh\CraftEmails\Services;

use DrewM\MailChimp\MailChimp;
use Ryssbowh\CraftEmails\Emails;
use Ryssbowh\CraftEmails\Models\MailchimpList;
use Ryssbowh\CraftEmails\Models\MailchimpMember;
use Ryssbowh\CraftEmails\exceptions\MailchimpException;
use craft\base\Component;

class MailchimpService extends Component
{
const CACHE_KEY = 'emails.mailchimp_lists';

/**
* @var MailChimp
*/
protected $_mailchimp;

/**
* @var array
*/
protected $_lists;

/**
* Is the api up and running
*
* @return boolean
*/
public function isEnabled(): bool
{
return !is_null($this->api);
}

/**
* Clear mailchimp caches
*/
public function clearCaches()
{
\Craft::$app->cache->delete(self::CACHE_KEY);
}

/**
* Get all lists
*
* @return array
*/
public function getLists(): array
{
if (!$this->isEnabled()) {
return [];
}
if ($this->_lists === null) {
$cached = \Craft::$app->cache->get(self::CACHE_KEY);
if ($cached === false) {
$lists = $this->api->get('lists');
$cached = [];
foreach ($lists['lists'] ?? [] as $attributes) {
$list = new MailchimpList($attributes);
$list->members = [];
$details = $this->api->get('lists/' . $list->id . '/members');
foreach ($details['members'] as $member) {
$list->members[] = new MailchimpMember($member);
}
$cached[$attributes['id']] = $list;
}
$duration = Emails::$plugin->settings->mailchimpCacheDuration;
\Craft::$app->cache->set(self::CACHE_KEY, $cached, $duration * 60);
}
$this->_lists = $cached;
}
return $this->_lists;
}

/**
* Get a list by id
*
* @param string $id
* @return MailchimpList
*/
public function getList(string $id): MailchimpList
{
if (isset($this->lists[$id])) {
return $this->lists[$id];
}
throw MailchimpException::noList($id);
}

/**
* Get api instance
*
* @return ?MailChimp
*/
public function getApi(): ?MailChimp
{
if (is_null($this->_mailchimp)) {
if (Emails::$plugin->settings->mailchimpApiKey) {
$this->_mailchimp = new MailChimp(\Craft::parseEnv(Emails::$plugin->settings->mailchimpApiKey));
}
}
return $this->_mailchimp;
}
}
Loading

0 comments on commit 8450632

Please sign in to comment.