diff --git a/CHANGELOG.md b/CHANGELOG.md index 8819fbd..a60c52c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 \ No newline at end of file diff --git a/README.md b/README.md index 0e9972a..0420f96 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 \ No newline at end of file +- Add a trigger system to send emails automatically when something happens on the system \ No newline at end of file diff --git a/composer.json b/composer.json index 5b75c04..7b6e83f 100644 --- a/composer.json +++ b/composer.json @@ -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": { diff --git a/src/Emails.php b/src/Emails.php index 061062f..228dd00 100644 --- a/src/Emails.php +++ b/src/Emails.php @@ -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; @@ -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; @@ -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(); @@ -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'; @@ -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 */ @@ -130,6 +152,11 @@ function (RegisterEmailSourcesEvent $e) { 'group' => $group ])); } + foreach (Emails::$plugin->mailchimp->lists as $list) { + $e->add(new MailchimpEmailSource([ + 'id' => $list['id'] + ])); + } } ); } diff --git a/src/Models/EmailShot.php b/src/Models/EmailShot.php index 8535858..126930f 100644 --- a/src/Models/EmailShot.php +++ b/src/Models/EmailShot.php @@ -101,9 +101,18 @@ 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])); } } }], @@ -111,20 +120,34 @@ public function defineRules(): array 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 * @@ -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; } diff --git a/src/Models/MailchimpList.php b/src/Models/MailchimpList.php new file mode 100644 index 0000000..292d2f9 --- /dev/null +++ b/src/Models/MailchimpList.php @@ -0,0 +1,29 @@ +members as $member) { + if ($member->status == 'subscribed') { + $emails[$member->email_address] = $member->full_name; + } + } + return $emails; + } +} \ No newline at end of file diff --git a/src/Models/MailchimpMember.php b/src/Models/MailchimpMember.php new file mode 100644 index 0000000..1002bf5 --- /dev/null +++ b/src/Models/MailchimpMember.php @@ -0,0 +1,9 @@ + $id])->one(); + $shot = EmailShotRecord::find()->where(['id' => $id])->one(); if (!$shot) { throw EmailShotException::noIdRecord($id); } diff --git a/src/Services/MailchimpService.php b/src/Services/MailchimpService.php new file mode 100644 index 0000000..9451e67 --- /dev/null +++ b/src/Services/MailchimpService.php @@ -0,0 +1,104 @@ +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; + } +} diff --git a/src/assets/src/emails.css b/src/assets/src/emails.css index e2fc8ce..3a8f714 100644 --- a/src/assets/src/emails.css +++ b/src/assets/src/emails.css @@ -38,6 +38,27 @@ #emails-modal, #content-modal { max-width: 100px; } +#emails-modal .body, #content-modal .body{ + overflow-y: auto; +} .justify-between { justify-content: space-between; +} +.field.third { + width: 33.3%; +} +.field.quarter { + width: 25%; +} +.field.half { + width: 50%; +} +.align-base { + align-items: baseline; +} +.email-element { + margin-top: 7px; +} +.email-element:first-child { + margin-top: 12px; } \ No newline at end of file diff --git a/src/controllers/CpEmailsController.php b/src/controllers/CpEmailsController.php index 3128063..1e37a9e 100644 --- a/src/controllers/CpEmailsController.php +++ b/src/controllers/CpEmailsController.php @@ -22,6 +22,11 @@ public function beforeAction($action) return true; } + /** + * Email dashboard action + * + * @return Response + */ public function actionIndex() { \Craft::$app->view->registerAssetBundle(EmailsAssetBundle::class); @@ -35,6 +40,11 @@ public function actionIndex() ]); } + /** + * Add email action + * + * @return Response + */ public function actionAdd() { $this->requirePermission('addDeleteEmailTemplates'); @@ -45,6 +55,12 @@ public function actionAdd() ]); } + /** + * Edit email content action + * + * @param int $id + * @return Response + */ public function actionEditContent(int $id) { $this->requirePermission('modifyEmailContent'); @@ -55,6 +71,12 @@ public function actionEditContent(int $id) ]); } + /** + * Edit email config action + * + * @param int $id + * @return Response + */ public function actionEditConfig(int $id) { $this->requirePermission('modifyEmailConfig'); @@ -65,6 +87,12 @@ public function actionEditConfig(int $id) ]); } + /** + * Delete email action + * + * @param int $id + * @return Response + */ public function actionDelete(int $id) { $this->requirePermission('addDeleteEmailTemplates'); @@ -90,6 +118,11 @@ public function actionDelete(int $id) return $this->redirect(UrlHelper::cpUrl('emails/list')); } + /** + * Save config action + * + * @return Response + */ public function actionSaveConfig() { $this->requirePostRequest(); @@ -115,6 +148,11 @@ public function actionSaveConfig() ]); } + /** + * Save email content action + * + * @return Response + */ public function actionSaveContent() { $this->requirePostRequest(); @@ -132,6 +170,11 @@ public function actionSaveContent() return $this->redirect(UrlHelper::cpUrl('emails')); } + /** + * Delete email logs action + * + * @return Response + */ public function actionDeleteLogs() { $this->requirePermission('deleteEmailLogs'); @@ -143,6 +186,11 @@ public function actionDeleteLogs() return true; } + /** + * View email logs action + * + * @return Response + */ public function actionLogs(int $emailId) { \Craft::$app->view->registerAssetBundle(EmailsAssetBundle::class); @@ -158,6 +206,11 @@ public function actionLogs(int $emailId) ]); } + /** + * View email action + * + * @return Response + */ public function actionView() { $this->requirePermission('seeEmailLogs'); @@ -166,6 +219,11 @@ public function actionView() return $this->asJson($log->toArray()); } + /** + * Resend email action + * + * @return Response + */ public function actionResend() { $this->requirePermission('sendEmails'); diff --git a/src/controllers/CpShotsController.php b/src/controllers/CpShotsController.php index 2c7af05..d06385b 100644 --- a/src/controllers/CpShotsController.php +++ b/src/controllers/CpShotsController.php @@ -10,6 +10,7 @@ use craft\helpers\UrlHelper; use craft\web\Controller; use yii\web\ForbiddenHttpException; +use yii\web\Response; class CpShotsController extends Controller { @@ -23,6 +24,11 @@ public function beforeAction($action) return true; } + /** + * Shots dashboard action + * + * @return Response + */ public function actionIndex() { \Craft::$app->view->registerAssetBundle(EmailsAssetBundle::class); @@ -31,6 +37,11 @@ public function actionIndex() ]); } + /** + * Add shot action + * + * @return Response + */ public function actionAddShot(?EmailShot $shot = null) { \Craft::$app->view->registerAssetBundle(EmailsAssetBundle::class); @@ -45,12 +56,23 @@ public function actionAddShot(?EmailShot $shot = null) ]); } + /** + * Edit shot action + * + * @param int $id + * @return Response + */ public function actionEditShot(int $id) { $shot = Emails::$plugin->emailShots->getById($id); return $this->actionAddShot($shot); } + /** + * Save shot action + * + * @return Response + */ public function actionSaveShot() { if ($id = \Craft::$app->request->getBodyParam('id')) { @@ -77,6 +99,11 @@ public function actionSaveShot() return $this->actionAddShot($shot); } + /** + * Delete shot action + * + * @return Response + */ public function actionDelete() { $id = $this->request->getRequiredParam('id'); @@ -102,6 +129,11 @@ public function actionDelete() return $this->redirect(UrlHelper::cpUrl('emails/shots')); } + /** + * Send shot action + * + * @return Response + */ public function actionSend() { $id = $this->request->getRequiredParam('id'); @@ -121,6 +153,11 @@ public function actionSend() return $this->redirect(UrlHelper::cpUrl('emails/shots')); } + /** + * Add quick shot action + * + * @return Response + */ public function actionQuickShot(?EmailShot $shot = null) { \Craft::$app->view->registerAssetBundle(EmailsAssetBundle::class); @@ -135,6 +172,11 @@ public function actionQuickShot(?EmailShot $shot = null) ]); } + /** + * Send quick shot action + * + * @return Response + */ public function actionSendQuickShot() { $users = $this->request->getBodyParam('users', []); @@ -158,6 +200,12 @@ public function actionSendQuickShot() return $this->actionQuickShot($shot); } + /** + * Shot logs action + * + * @param int $id + * @return Response + */ public function actionLogs(int $id) { \Craft::$app->view->registerAssetBundle(EmailsAssetBundle::class); @@ -172,6 +220,11 @@ public function actionLogs(int $id) ]); } + /** + * Delete shot logs action + * + * @return Response + */ public function actionDeleteLogs() { $this->requirePermission('deleteEmailLogs'); @@ -183,6 +236,11 @@ public function actionDeleteLogs() return true; } + /** + * Get shot log emails action + * + * @return Response + */ public function actionLogEmails() { $id = $this->request->getRequiredParam('id'); @@ -192,7 +250,26 @@ public function actionLogEmails() ]); } - protected function allEmails() + /** + * Get shot emails action + * + * @return Response + */ + public function actionShotEmails() + { + $id = $this->request->getRequiredParam('id'); + $shot = Emails::$plugin->emailShots->getById($id); + return $this->asJson([ + 'emails' => $shot->allEmails + ]); + } + + /** + * Get all emails + * + * @return array + */ + protected function allEmails(): array { $emails = []; foreach (Emails::$plugin->emails->all() as $email) { @@ -201,6 +278,11 @@ protected function allEmails() return $emails; } + /** + * Get all sources + * + * @return array + */ protected function allSources(): array { return array_map(function ($source) { diff --git a/src/emailSources/AllUsersEmailSource.php b/src/emailSources/AllUsersEmailSource.php index 5ea8253..b1e799d 100644 --- a/src/emailSources/AllUsersEmailSource.php +++ b/src/emailSources/AllUsersEmailSource.php @@ -29,8 +29,10 @@ public function getHandle(): string */ public function getEmails(): array { - return array_map(function ($user) { - return $user->email; - }, User::find()->all()); + $emails = []; + foreach (User::find()->all() as $user) { + $emails[$user->email] = $user->friendlyName; + } + return $emails; } } \ No newline at end of file diff --git a/src/emailSources/MailchimpEmailSource.php b/src/emailSources/MailchimpEmailSource.php new file mode 100644 index 0000000..76178db --- /dev/null +++ b/src/emailSources/MailchimpEmailSource.php @@ -0,0 +1,52 @@ +mailchimp->getList($this->id); + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return \Craft::t('emails', 'Mailchimp list {list}', ['list' => $this->list->name]); + } + + /** + * @inheritDoc + */ + public function getHandle(): string + { + return 'mailchimp_list_' . $this->id; + } + + /** + * @inheritDoc + */ + public function getEmails(): array + { + return $this->list->emails; + } +} \ No newline at end of file diff --git a/src/emailSources/UserGroupEmailSource.php b/src/emailSources/UserGroupEmailSource.php index 0b148d1..f544285 100644 --- a/src/emailSources/UserGroupEmailSource.php +++ b/src/emailSources/UserGroupEmailSource.php @@ -35,8 +35,10 @@ public function getHandle(): string */ public function getEmails(): array { - return array_map(function ($user) { - return $user->email; - }, User::find()->group($this->group)->all()); + $emails = []; + foreach (User::find()->group($this->group)->all() as $user) { + $emails[$user->email] = $user->friendlyName; + } + return $emails; } } \ No newline at end of file diff --git a/src/exceptions/MailchimpException.php b/src/exceptions/MailchimpException.php new file mode 100644 index 0000000..19175a6 --- /dev/null +++ b/src/exceptions/MailchimpException.php @@ -0,0 +1,11 @@ + 'Name', + * 'email2@domain.com' => null + * ] * * @return array */ diff --git a/src/templates/_includes/shot_form.twig b/src/templates/_includes/shot_form.twig index d4f7207..a7f6a2f 100644 --- a/src/templates/_includes/shot_form.twig +++ b/src/templates/_includes/shot_form.twig @@ -58,12 +58,12 @@ options: allSources, values: shot.sources, label: 'Sources'|t('emails'), - fieldClass: 'third', + fieldClass: 'quarter', name: 'sources', errors: errors['sources'] ?? [] }) }} -
+
{{ hiddenInput(name, '') }}
@@ -90,15 +90,16 @@
-
+
- {% for email in shot.emails %} + {% for email, name in shot.emails %} {% endfor %} @@ -121,29 +122,13 @@ on: shot.useQueue, instructions: "Send emails through the queue. the emails will be send in the background so you can do other things in the meantime."|t('emails') }) }} -{{ forms.lightswitchField({ - label: 'Save logs'|t('emails'), - name: 'saveLogs', - on: shot.saveLogs -}) }} - -{% css %} - .field.third { - width: 33.3%; - } - .field.half { - width: 50%; - } - .align-base { - align-items: baseline; - } - .email-element { - margin-top: 7px; - } - .email-element:first-child { - margin-top: 12px; - } -{% endcss %} +{% if mode == 'create' %} + {{ forms.lightswitchField({ + label: 'Save logs'|t('emails'), + name: 'saveLogs', + on: shot.saveLogs + }) }} +{% endif %} {% js %} new Craft.BaseElementSelectInput({{ jsSettings|json_encode|raw }}); @@ -153,10 +138,11 @@ }); }); $('.js-add-email').click(function () { - var elem = $(''); + var elem = $(''); elem.hide(); $('#field-emails .elements').append(elem); elem.slideDown('fast'); + elem.find('input').first().focus(); }); {% endjs %} diff --git a/src/templates/settings.twig b/src/templates/settings.twig index e529a3b..54a8eea 100644 --- a/src/templates/settings.twig +++ b/src/templates/settings.twig @@ -20,10 +20,23 @@ name: 'configDriven' }) }} -{{ - forms.lightswitchField({ - name: 'removeShotDuplicates', - on: settings.removeShotDuplicates, - label: 'Remove duplicated emails in email shots'|t('emails') - }) -}} \ No newline at end of file + +{{ forms.autosuggestField({ + label: 'Mailchimp API Key'|t('emails'), + type: 'text', + name: 'mailchimpApiKey', + value: settings.mailchimpApiKey, + suggestEnvVars: true, + errors: errors['mailchimpApiKey'] ?? [] +}) }} + +{{ forms.textField({ + label: 'Mailchimp Cache duration'|t('emails'), + type: 'number', + instructions: '0 means forever'|t('emails'), + min: 0, + unit: 'minutes'|t('emails'), + name: 'mailchimpCacheDuration', + value: settings.mailchimpCacheDuration, + errors: errors['mailchimpCacheDuration'] ?? [] +}) }} \ No newline at end of file diff --git a/src/templates/shots.twig b/src/templates/shots.twig index 44d7ead..1998477 100644 --- a/src/templates/shots.twig +++ b/src/templates/shots.twig @@ -6,9 +6,9 @@ {% block toolbar %} {% if currentUser.can('sendEmails') %} - {{ 'Quick shot'|t('emails') }} + {{ 'Quick email shot'|t('emails') }} {% endif %} - {{ 'New shot'|t('emails') }} + {{ 'New email shot'|t('emails') }} {% endblock %} {% block content %} @@ -59,9 +59,12 @@
- {% if shot.email and currentUser.can('sendEmails') %} - - {% endif %} +
+ {{ 'View emails'|t('emails') }} + {% if shot.email and currentUser.can('sendEmails') %} + + {% endif %} +
{% if currentUser.can('seeEmailLogs') and shot.saveLogs %} @@ -78,10 +81,48 @@ {% else %}

{{ 'No email shots found'|t('emails') }}

{% endif %} + {% include "emails/_includes/js-errors" %} {% endblock %} {% js %} + var modal = new Garnish.Modal($('#emails-modal'), { + autoShow: false + }); + $('#emails-modal .js-close').click(function(){ + modal.hide(); + }); + function formatEmails(emails) { + var html = ''; + for (var i in emails) { + html += '' + } + return html; + } + $('.js-view').click(function(e){ + e.preventDefault(); + $.ajax({ + url: Craft.getActionUrl('emails/cp-shots/shot-emails'), + data: {id: $(this).data('id')} + }).done(function (data) { + if (data.emails.length == 0) { + $('#emails-modal .content').html('

{{ "No emails for this shot"|t("emails") }}

'); + } else { + $('#emails-modal .content').html(formatEmails(data.emails)); + } + modal.show(); + }) + }); $('.js-delete').click(function (e) { e.preventDefault(); let id = $(this).data('id'); diff --git a/src/translations/en-GB/emails.php b/src/translations/en-GB/emails.php index c56ad2f..66b98a9 100644 --- a/src/translations/en-GB/emails.php +++ b/src/translations/en-GB/emails.php @@ -1,6 +1,7 @@ "0 means forever", "Actions" => "Actions", "Add an Email" => "Add an Email", "Add and delete email templates" => "Add and delete email templates", @@ -42,10 +43,13 @@ "Email has been deleted." => "Email has been deleted.", "Email has been resent." => "Email has been resent.", "Email saved." => "Email saved.", + "Email shot has been deleted." => "Email shot has been deleted.", "Email shot saved." => "Email shot saved.", "Email shots" => "Email shots", "Emails" => "Emails", "Emails sent" => "Emails sent", + "Error while deleting email shot." => "Error while deleting email shot.", + "Error while deleting email." => "Error while deleting email.", "Error while resending the email." => "Error while resending the email.", "From" => "From", "Handle" => "Handle", @@ -57,6 +61,10 @@ "Logs for {email}" => "Logs for {email}", "Logs for {shot}" => "Logs for {shot}", "Logs have been deleted." => "Logs have been deleted.", + "Mailchimp API Key" => "Mailchimp API Key", + "Mailchimp Cache duration" => "Mailchimp Cache duration", + "Mailchimp list {list}" => "Mailchimp list {list}", + "Mailchimp lists" => "Mailchimp lists", "Manage email shots" => "Manage email shots", "Modify emails config" => "Modify emails config", "Modify emails content" => "Modify emails content", @@ -64,14 +72,14 @@ "Name from" => "Name from", "New email" => "New email", "New email shot" => "New email shot", - "New shot" => "New shot", "No" => "No", "No email set" => "No email set", "No email shots found" => "No email shots found", "No emails for this log" => "No emails for this log", + "No emails for this shot" => "No emails for this shot", "No logs found" => "No logs found", "Plain text email" => "Plain text email", - "Quick shot" => "Quick shot", + "Quick email shot" => "Quick email shot", "Reply to" => "Reply to", "Reply to email" => "Reply to email", "Resend" => "Resend", @@ -84,9 +92,11 @@ "Send emails through the queue. the emails will be send in the background so you can do other things in the meantime." => "Send emails through the queue. the emails will be send in the background so you can do other things in the meantime.", "Send now" => "Send now", "Send quick email shot" => "Send quick email shot", + "Sending {shot}" => "Sending {shot}", "Sent" => "Sent", "Sent by" => "Sent by", "Shot" => "Shot", + "Source {source} doesn't exist" => "Source {source} doesn't exist", "Sources" => "Sources", "Subject" => "Subject", "Subject of the email, twig supported" => "Subject of the email, twig supported", @@ -94,6 +104,7 @@ "To" => "To", "Total emails" => "Total emails", "Use queue" => "Use queue", + "User {user} doesn't exist" => "User {user} doesn't exist", "Users" => "Users", "View emails" => "View emails", "View logs" => "View logs", @@ -101,6 +112,8 @@ "Yes" => "Yes", "You can save custom Redactor configs as .json files in config/redactor/" => "You can save custom Redactor configs as .json files in config/redactor/", "deleted user" => "deleted user", + "email shot '{name}'" => "email shot '{name}'", + "minutes" => "minutes", "system" => "system", "{number} emails have been sent to the queue." => "{number} emails have been sent to the queue.", "{number} emails sent." => "{number} emails sent.", diff --git a/src/translations/fr/emails.php b/src/translations/fr/emails.php index 9426d4b..f6fa006 100644 --- a/src/translations/fr/emails.php +++ b/src/translations/fr/emails.php @@ -1,6 +1,7 @@ "0 pour infini", "Actions" => "Actions", "Add and delete emails" => "Add and delete emails", "Add and delete email templates" => "Ajouter et supprimer des e-mails", @@ -11,6 +12,7 @@ "Bcc" => "Cci", "This is a system email and its key cannot be changed" => "Ceci est un e-mail système et sa clé ne peut être changée", "Key" => "Clé", + "Mailchimp API Key" => "Clé API Mailchimp", "Config" => "Configuration", "Body Redactor config" => "Configuration Redactor pour le corps", "Content" => "Contenu", @@ -19,6 +21,7 @@ "Date" => "Date", "To" => "Destinataire", "Details" => "Details", + "Mailchimp Cache duration" => "Durée du cache Mailchimp", "Default" => "Défaut", "Email" => "E-mail", "Email from" => "E-mail de l’expéditeur", @@ -29,46 +32,54 @@ "Emails" => "E-mails", "Emails sent" => "E-mails envoyés", "Comma separated emails" => "E-mails séparés par des virgules", + "Sending {shot}" => "Envoie {shot}", "Send" => "Envoyer", "Send emails through the queue. the emails will be send in the background so you can do other things in the meantime." => "Envoyer les emails en file d'attente. Les e-mails seront envoyés en arrière plan et vous pourrez faire d'autres choses en même temps.", "Send now" => "Envoyer maintenant", - "Send quick email shot" => "Envoyer un publipostage rapide", + "Send quick email shot" => "Envoyer un mail-shot rapide", "Sent" => "Envoyé", "Sent by" => "Envoyé par", "Error while resending the email." => "Erreur en envoyant l’e-mail", + "Error while deleting email shot." => "Erreur rencontrée en supprimant le mail-shot", + "Error while deleting email." => "Erreur rencontrée en supprimant l’e-mail", "From" => "Expéditeur", "Handle" => "Identificateur", "Instructions" => "Instructions", "Instructions for the user writing the email's body, displayed as a tip." => "Instructions pour l'utilisateur écrivant le contenu de l’e-mail.", + "User {user} doesn't exist" => "L'utilisateur {user} n'existe pas", + "Source {source} doesn't exist" => "La source {source} n'existe pas", + "Email shot has been deleted." => "Le mail-shot a été supprimé", "Logs have been deleted." => "Les logs ont été supprimés", + "Mailchimp list {list}" => "Liste Mailchimp {list}", + "Mailchimp lists" => "Listes Mailchimp", "Logs" => "Logs", "Logs for {email}" => "Logs pour {email}", "Logs for {shot}" => "Logs pour {shot}", "Email has been resent." => "L’e-mail a été renvoyé", "Email has been deleted." => "L’e-mail a été supprimé", + "Quick email shot" => "Mail-shot rapide", + "Email shot saved." => "Mail-shot sauvegardé", + "Email shots" => "Mail-shots", + "Shot" => "Mailshot", "Manage email shots" => "Manage email shots", "Edit" => "Modifier", "Changing this may break emails that use this template" => "Modifier ceci peut impacter les e-mails qui utilisent ce template", - "Edit Email Config" => "Modifier la configuration", "Edit config" => "Modifier la configuration", + "Edit Email Config" => "Modifier la configuration", "Modify emails config" => "Modifier la configuration des e-mails", "Edit Email Content" => "Modifier le contenu de l’e-mail", "Modify emails content" => "Modifier le contenu des e-mails", - "Edit email shot" => "Modifier le publipostage", + "Edit email shot" => "Modifier le mail-shot", "Do not use Html for this email's body" => "Ne pas utiliser d'html pour le contenu de cet e-mail", "Name" => "Nom", "Name from" => "Nom de l’expéditeur", "No" => "Non", - "New shot" => "Nouveau publipostage", - "New email shot" => "Nouveau publipostage", + "New email shot" => "Nouveau mail-shot", "New email" => "Nouvel e-mail", "Yes" => "Oui", - "No email shots found" => "Pas de publipostage trouvés", + "No email shots found" => "Pas de mail-shot trouvés", + "No emails for this shot" => "Pas d’e-mail pour ce mail-shot", "No emails for this log" => "Pas d’e-mails pour ce log", - "Shot" => "Publipostage", - "Quick shot" => "Publipostage rapide", - "Email shot saved." => "Publipostage sauvegardé", - "Email shots" => "Publipostages", "Resend" => "Renvoyer", "Back" => "Retour", "Reply to" => "Répondre a", @@ -95,13 +106,15 @@ "See emails logs" => "Voir les logs e-mails", "View more" => "Voir plus", "You can save custom Redactor configs as .json files in config/redactor/" => "Vous pouvez ajouter de nouvelles configurations en .json dans config/redactor/", + "email shot '{name}'" => "mail-shot '{name}'", + "minutes" => "minutes", "system" => "système", "deleted user" => "utilisateur supprimé", "{number} emails sent." => "{number} e-mails on été envoyés.", "{number} emails have been sent to the queue." => "{number} e-mails ont été envoyés en file d'attente.", - "Are you sure you want to send this email shot now?" => "Êtes vous sur de vouloir envoyer ce publipostage?", + "Are you sure you want to send this email shot now?" => "Êtes vous sur de vouloir envoyer ce mail-shot?", "Are you sure you want to resend this email now?" => "Êtes vous sur de vouloir renvoyer cet e-mail?", - "Are you sure you want to delete this email shot ?" => "Êtes vous sur de vouloir supprimer ce publipostage?", + "Are you sure you want to delete this email shot ?" => "Êtes vous sur de vouloir supprimer ce mail-shot?", "Are you sure you want to delete these logs?" => "Êtes vous sur de vouloir supprimer ces logs?", "Are you sure you want to delete all logs?" => "Êtes vous sur de vouloir supprimer tous les logs?", ];