Skip to content

Commit 243e44c

Browse files
committed
Email: all current members
close #314
1 parent a03be50 commit 243e44c

22 files changed

+2288
-554
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
namespace App\Http\Controllers;
4+
5+
use HMS\Entities\Role;
6+
use Illuminate\Http\Request;
7+
use App\Mail\ToCurrentMembers;
8+
use Illuminate\Support\HtmlString;
9+
use App\Jobs\EmailCurrentMembersJob;
10+
use HMS\Repositories\RoleRepository;
11+
use Illuminate\Contracts\View\Factory as ViewFactory;
12+
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;
13+
14+
class EmailController extends Controller
15+
{
16+
/**
17+
* @var RoleRepository
18+
*/
19+
protected $roleRepository;
20+
21+
/**
22+
* Create a new controller instance.
23+
*
24+
* @param RoleRepository $roleRepository
25+
*/
26+
public function __construct(
27+
RoleRepository $roleRepository
28+
) {
29+
$this->roleRepository = $roleRepository;
30+
31+
$this->middleware('can:email.allMembers');
32+
}
33+
34+
/**
35+
* Show the draft email view.
36+
*
37+
* @return \Illuminate\Http\Response
38+
*/
39+
public function draft()
40+
{
41+
$draft = \Cache::get('emailMembers.draft', [
42+
'subject' => '',
43+
'emailContent' => '',
44+
]);
45+
46+
return view('emailMembers.draft', $draft);
47+
}
48+
49+
/**
50+
* Store the draft email and return a preview.
51+
*
52+
* @param Illuminate\Http\Request $request
53+
*
54+
* @return \Illuminate\Http\Response
55+
*/
56+
public function review(Request $request)
57+
{
58+
\Cache::put('emailMembers.draft', [
59+
'subject' => $request->subject,
60+
'emailContent' => $request->emailContent,
61+
], now()->addMinutes(30));
62+
63+
$emailView = new ToCurrentMembers($request->subject, $request->emailContent);
64+
$renderedTextPlain = $emailView->renderText();
65+
66+
$currentMemberCount = $this->roleRepository->findOneByName(Role::MEMBER_CURRENT)->getUsers()->count();
67+
68+
return view('emailMembers.review')
69+
->with([
70+
'subject' => $request->subject,
71+
'emailPlain' => $renderedTextPlain,
72+
'currentMemberCount' => $currentMemberCount,
73+
]);
74+
}
75+
76+
/**
77+
* Show a html review of the draft email.
78+
*
79+
* @param ViewFactory $viewFactory
80+
* @param CssToInlineStyles $cssToInlineStyles
81+
*
82+
* @return \Illuminate\Http\Response
83+
*/
84+
public function reviewHtml(ViewFactory $viewFactory, CssToInlineStyles $cssToInlineStyles)
85+
{
86+
$draft = \Cache::get('emailMembers.draft', [
87+
'subject' => '',
88+
'emailContent' => '',
89+
]);
90+
91+
$emailView = new ToCurrentMembers($draft['subject'], $draft['emailContent']);
92+
$renderedHtml = $emailView->render();
93+
// TODO: build theme string from config
94+
$renderedHtmlCSS = new HtmlString(
95+
$cssToInlineStyles->convert(
96+
$renderedHtml,
97+
$viewFactory->make('vendor.mail.html.themes.hms')->render()
98+
)
99+
);
100+
101+
return response($renderedHtmlCSS);
102+
}
103+
104+
/**
105+
* Send the email.
106+
*
107+
* @param Illuminate\Http\Request $request
108+
*
109+
* @return \Illuminate\Http\RedirectResponse
110+
*/
111+
public function send(Request $request)
112+
{
113+
$draft = \Cache::get('emailMembers.draft');
114+
115+
EmailCurrentMembersJob::dispatch($draft['subject'], $draft['emailContent'], $request->testSend);
116+
117+
if (! $request->testSend) {
118+
flash('Email queued for sending', 'success');
119+
\Cache::forget('emailMembers.draft');
120+
} else {
121+
flash('Test email queued for sending', 'success');
122+
}
123+
124+
return redirect()->route('email-members.draft');
125+
}
126+
}

app/Jobs/EmailCurrentMembersJob.php

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
3+
namespace App\Jobs;
4+
5+
use HMS\Entities\Role;
6+
use Soundasleep\Html2Text;
7+
use Illuminate\Bus\Queueable;
8+
use App\Mail\ToCurrentMembers;
9+
use Illuminate\Support\HtmlString;
10+
use HMS\Repositories\RoleRepository;
11+
use Bogardo\Mailgun\Contracts\Mailgun;
12+
use Illuminate\Queue\SerializesModels;
13+
use Illuminate\Queue\InteractsWithQueue;
14+
use Illuminate\Contracts\Queue\ShouldQueue;
15+
use Illuminate\Foundation\Bus\Dispatchable;
16+
use Illuminate\Contracts\View\Factory as ViewFactory;
17+
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;
18+
19+
class EmailCurrentMembersJob implements ShouldQueue
20+
{
21+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
22+
23+
/**
24+
* The email subject.
25+
*
26+
* @var string
27+
*/
28+
public $subject;
29+
30+
/**
31+
* The email content as text/html.
32+
*
33+
* @var string
34+
*/
35+
public $htmlContent;
36+
37+
/**
38+
* Should this only sent as a test.
39+
*
40+
* @var bool
41+
*/
42+
public $testSend;
43+
44+
/**
45+
* Create a new job instance.
46+
*
47+
* @param string $subject The email subject.
48+
* @param string $htmlContent The email content as text/html.
49+
* @param bool $testSend Should this only sent as a test.
50+
*/
51+
public function __construct(
52+
string $subject,
53+
string $htmlContent,
54+
bool $testSend = true
55+
) {
56+
$this->subject = $subject;
57+
$this->htmlContent = $htmlContent;
58+
$this->testSend = $testSend;
59+
}
60+
61+
/**
62+
* Execute the job.
63+
*
64+
* @param Mailgun $mailgun
65+
* @param CssToInlineStyles $cssToInlineStyles
66+
* @param ViewFactory $viewFactory
67+
* @param RoleRepository $roleRepository
68+
*
69+
* @return void
70+
*/
71+
public function handle(
72+
Mailgun $mailgun,
73+
CssToInlineStyles $cssToInlineStyles,
74+
ViewFactory $viewFactory,
75+
RoleRepository $roleRepository
76+
) {
77+
$trusteesRole = $roleRepository->findOneByName(Role::TEAM_TRUSTEES);
78+
$trusteesEmail = $trusteesRole->getEmail();
79+
80+
$currentMembers = $roleRepository->findOneByName(Role::MEMBER_CURRENT)->getUsers();
81+
82+
// Send using Mailgun
83+
$views = [
84+
'html' => 'emails.emailMembers.toCurrentMembers_mailgun',
85+
'text' => 'emails.emailMembers.toCurrentMembers_plain',
86+
];
87+
88+
$emailView = new ToCurrentMembers($this->subject, $this->htmlContent);
89+
$renderedHtml = $emailView->render();
90+
$renderedHtmlCSS = new HtmlString(
91+
$cssToInlineStyles->convert(
92+
$renderedHtml,
93+
$viewFactory->make('vendor.mail.html.themes.default')->render()
94+
)
95+
);
96+
97+
$data = [
98+
'htmlContent' => $renderedHtmlCSS,
99+
'textPlain' => Html2Text::convert($this->htmlContent),
100+
];
101+
102+
if ($this->testSend) {
103+
// only send to the trustees address
104+
$to = [
105+
$trusteesEmail => [
106+
'name' => 'Trustees',
107+
],
108+
];
109+
} else {
110+
// send to all current members
111+
// this converts to an Illuminate\Support\Collection first then maps our User object
112+
$to = collect($currentMembers)->mapWithKeys(function ($user) {
113+
return [$user->getEmail() => [
114+
'name' => $user->getFirstname(),
115+
],
116+
];
117+
})->toArray();
118+
}
119+
120+
$trusteesMgEmail = preg_replace('/@/m', '@mg.', $trusteesEmail);
121+
$subject = $this->subject;
122+
$mailgun->send($views, $data, function ($message) use ($subject, $trusteesEmail, $trusteesMgEmail, $to) {
123+
$message
124+
->trackOpens(true)
125+
->subject($subject)
126+
->header('sender', 'Nottingham Hackspace Trustees <' . $trusteesMgEmail . '>')
127+
->replyTo($trusteesEmail, 'Nottingham Hackspace Trustees')
128+
->from($trusteesMgEmail, 'Nottingham Hackspace Trustees')
129+
->to($to);
130+
});
131+
}
132+
}

app/Mail/ToCurrentMembers.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
namespace App\Mail;
4+
5+
use Soundasleep\Html2Text;
6+
use Illuminate\Bus\Queueable;
7+
use Illuminate\Mail\Mailable;
8+
use Illuminate\Container\Container;
9+
use Illuminate\Queue\SerializesModels;
10+
use Illuminate\Contracts\Queue\ShouldQueue;
11+
12+
class ToCurrentMembers extends Mailable implements ShouldQueue
13+
{
14+
use Queueable, SerializesModels;
15+
16+
/**
17+
* The email subject.
18+
*
19+
* @var string
20+
*/
21+
public $subject;
22+
23+
/**
24+
* The email content as text/html.
25+
*
26+
* @var string
27+
*/
28+
public $htmlContent;
29+
30+
/**
31+
* The email content as text/plain.
32+
*
33+
* @var string
34+
*/
35+
public $textPlain;
36+
37+
/**
38+
* Create a new message instance.
39+
*
40+
* @param string $subject The email subject.
41+
* @param string $htmlContent The email content as text/html.
42+
*/
43+
public function __construct(string $subject, string $htmlContent)
44+
{
45+
$this->subject = $subject;
46+
$this->htmlContent = $htmlContent;
47+
$this->textPlain = Html2Text::convert($htmlContent);
48+
49+
//TODO: for hms2 pull trustee address from Role::TEAM_TRUSTEES
50+
}
51+
52+
/**
53+
* Build the message.
54+
*
55+
* @return $this
56+
*/
57+
public function build()
58+
{
59+
return $this->from('[email protected]', 'Nottingham Hackspace Trustees')
60+
->subject($this->subject)
61+
->view('emails.emailMembers.toCurrentMembers')
62+
->text('emails.emailMembers.toCurrentMembers_plain');
63+
}
64+
65+
/**
66+
* Render the template into text.
67+
*
68+
* @return \Illuminate\Support\HtmlString
69+
*/
70+
public function renderText()
71+
{
72+
Container::getInstance()->call([$this, 'build']);
73+
74+
return Container::getInstance()->make('mailer')->render(
75+
'emails.emailMembers.toCurrentMembers_plain', $this->buildViewData()
76+
);
77+
}
78+
}

app/Providers/AppServiceProvider.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,12 @@ public function boot()
4242
\Blade::directive('format_pennies', function ($pennies) {
4343
return "<?php echo money_format('%n', (" . $pennies . ')/100); ?>';
4444
});
45+
46+
// setup Guzzle6 for mailgun batch sender
47+
$this->app->bind('mailgun.client', function () {
48+
return \Http\Adapter\Guzzle6\Client::createWithConfig([
49+
// your Guzzle6 configuration
50+
]);
51+
});
4552
}
4653
}

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
"type": "project",
77
"require": {
88
"php": "^7.1.3",
9+
"bogardo/mailgun": "^5.1",
910
"brainrepo/carbon-normalizer": "dev-master",
1011
"fideloper/proxy": "^4.0",
1112
"garygreen/pretty-routes": "^1.0",
1213
"gedmo/doctrine-extensions": "^2.4",
1314
"guzzlehttp/guzzle": "^6.3",
14-
"karpy47/php-mqtt-client": "^1.0",
15+
"karpy47/php-mqtt-client": "dev-master",
1516
"laracasts/flash": "^3.0",
1617
"laravel-doctrine/acl": "1.0.*",
1718
"laravel-doctrine/extensions": "^1.0",
@@ -26,7 +27,9 @@
2627
"maxbrokman/safe-queue": "^0.3.0",
2728
"orphans/git-deploy-laravel": "dev-master",
2829
"pda/pheanstalk": "~4.0",
30+
"php-http/guzzle6-adapter": "^1.1",
2931
"predis/predis": "^1.1",
32+
"soundasleep/html2text": "^1.1",
3033
"spatie/laravel-cookie-consent": "^2.0",
3134
"tremby/laravel-git-version": "^1.1"
3235
},

0 commit comments

Comments
 (0)