Skip to content

Commit

Permalink
Custom SMTP Settings (#561)
Browse files Browse the repository at this point in the history
* Custom SMTP Settings

* Fix lint

* Custom SMTP add in Pricing plan

* Allow reset email settings

* improve custom SMTP using seprate abstract class

* test case for custom SMTP

* fix test case

* UI improvement

* add CASHIER_KEY in phpunit for testcase

* Attempt to fix tests

* Run pint and attempt to fix cache tests

* Fix user management tests

* Fix code linters

* Merged main & fix linting

---------

Co-authored-by: Julien Nahum <[email protected]>
  • Loading branch information
chiragchhatrala and JhumanJ authored Sep 24, 2024
1 parent 5dcd4ff commit 504c7a0
Show file tree
Hide file tree
Showing 31 changed files with 876 additions and 510 deletions.
20 changes: 20 additions & 0 deletions api/app/Http/Controllers/WorkspaceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Http\Controllers;

use App\Http\Requests\Workspace\CustomDomainRequest;
use App\Http\Requests\Workspace\EmailSettingsRequest;
use App\Http\Resources\WorkspaceResource;
use App\Models\Workspace;
use Illuminate\Http\Request;
Expand All @@ -24,12 +25,31 @@ public function index()

public function saveCustomDomain(CustomDomainRequest $request)
{
if (!$request->workspace->is_pro) {
return $this->error([
'message' => 'A Pro plan is required to use this feature.',
], 403);
}

$request->workspace->custom_domains = $request->customDomains;
$request->workspace->save();

return new WorkspaceResource($request->workspace);
}

public function saveEmailSettings(EmailSettingsRequest $request)
{
if (!$request->workspace->is_pro) {
return $this->error([
'message' => 'A Pro plan is required to use this feature.',
], 403);
}

$request->workspace->update(['settings' => array_merge($request->workspace->settings, ['email_settings' => $request->validated()])]);

return new WorkspaceResource($request->workspace);
}

public function delete($id)
{
$workspace = Workspace::findOrFail($id);
Expand Down
65 changes: 65 additions & 0 deletions api/app/Http/Requests/Workspace/EmailSettingsRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace App\Http\Requests\Workspace;

use App\Models\Workspace;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;

class EmailSettingsRequest extends FormRequest
{
public Workspace $workspace;

public function __construct(Request $request, Workspace $workspace)
{
$this->workspace = Workspace::findOrFail($request->workspaceId);
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
$allFieldsPresent = $this->filled(['host', 'port', 'username', 'password']);

return [
'host' => [
$allFieldsPresent ? 'required' : 'nullable',
'required_with:port,username,password',
'string',
],
'port' => [
$allFieldsPresent ? 'required' : 'nullable',
'required_with:host,username,password',
'integer',
],
'username' => [
$allFieldsPresent ? 'required' : 'nullable',
'required_with:host,port,password',
'string',
],
'password' => [
$allFieldsPresent ? 'required' : 'nullable',
'required_with:host,port,username',
'string',
],
];
}

/**
* Get the validation messages that apply to the request.
*
* @return array
*/
public function messages()
{
return [
'host.required_with' => 'The host field is required.',
'port.required_with' => 'The port field is required.',
'username.required_with' => 'The username field is required.',
'password.required_with' => 'The password field is required.',
];
}
}
2 changes: 1 addition & 1 deletion api/app/Integrations/Google/Google.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function __construct(

public function getClient(): Client
{
if($this->client->isAccessTokenExpired()) {
if ($this->client->isAccessTokenExpired()) {
$this->refreshToken();
}

Expand Down
47 changes: 47 additions & 0 deletions api/app/Integrations/Handlers/AbstractEmailIntegrationHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace App\Integrations\Handlers;

use App\Events\Forms\FormSubmitted;
use App\Models\Integration\FormIntegration;
use Illuminate\Support\Facades\Log;

abstract class AbstractEmailIntegrationHandler extends AbstractIntegrationHandler
{
protected $mailer;

public function __construct(FormSubmitted $event, FormIntegration $formIntegration, array $integration)
{
parent::__construct($event, $formIntegration, $integration);
$this->initializeMailer();
}

protected function initializeMailer()
{
$this->mailer = config('mail.default');
$this->setWorkspaceSMTPSettings();

if (!$this->mailer) {
Log::error('Mailer not specified', [
'form_id' => $this->form->id
]);
}
}

protected function setWorkspaceSMTPSettings()
{
$workspace = $this->form->workspace;
$emailSettings = $workspace->settings['email_settings'] ?? [];
if (!$workspace->is_pro || !$emailSettings || empty($emailSettings['host']) || empty($emailSettings['port']) || empty($emailSettings['username']) || empty($emailSettings['password'])) {
return;
}

config([
'mail.mailers.custom_smtp.host' => $emailSettings['host'],
'mail.mailers.custom_smtp.port' => $emailSettings['port'],
'mail.mailers.custom_smtp.username' => $emailSettings['username'],
'mail.mailers.custom_smtp.password' => $emailSettings['password']
]);
$this->mailer = 'custom_smtp';
}
}
5 changes: 3 additions & 2 deletions api/app/Integrations/Handlers/EmailIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Illuminate\Support\Facades\Notification;
use App\Notifications\Forms\FormSubmissionNotification;

class EmailIntegration extends AbstractIntegrationHandler
class EmailIntegration extends AbstractEmailIntegrationHandler
{
public static function getValidationRules(): array
{
Expand Down Expand Up @@ -36,10 +36,11 @@ public function handle(): void
'recipients' => $subscribers->toArray(),
'form_id' => $this->form->id,
'form_slug' => $this->form->slug,
'mailer' => $this->mailer
]);
$subscribers->each(function ($subscriber) {
Notification::route('mail', $subscriber)->notify(
new FormSubmissionNotification($this->event, $this->integrationData)
new FormSubmissionNotification($this->event, $this->integrationData, $this->mailer)
);
});
}
Expand Down
2 changes: 1 addition & 1 deletion api/app/Integrations/Handlers/GoogleSheetsIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function handle(): void

protected function getSpreadsheetId(): string
{
if(!isset($this->integrationData->spreadsheet_id)) {
if (!isset($this->integrationData->spreadsheet_id)) {
throw new Exception('The spreadsheed is not instantiated');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/**
* Sends a confirmation to form respondant that form was submitted
*/
class SubmissionConfirmationIntegration extends AbstractIntegrationHandler
class SubmissionConfirmationIntegration extends AbstractEmailIntegrationHandler
{
public const RISKY_USERS_LIMIT = 120;

Expand Down Expand Up @@ -54,8 +54,9 @@ public function handle(): void
'recipient' => $email,
'form_id' => $this->form->id,
'form_slug' => $this->form->slug,
'mailer' => $this->mailer
]);
Mail::to($email)->send(new SubmissionConfirmationMail($this->event, $this->integrationData));
Mail::mailer($this->mailer)->to($email)->send(new SubmissionConfirmationMail($this->event, $this->integrationData));
}

private function getRespondentEmail()
Expand Down
2 changes: 1 addition & 1 deletion api/app/Jobs/Billing/WorkspaceUsersUpdated.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function __construct(public Workspace $workspace)
public function handle(): void
{
// If self-hosted, no need to update billing
if (!pricing_enabled()) {
if (!pricing_enabled() || \App::environment('testing')) {
return;
}

Expand Down
6 changes: 3 additions & 3 deletions api/app/Listeners/Forms/FormIntegrationCreatedHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ public function handle(FormIntegrationCreated $event)
{
$integration = FormIntegration::getIntegration($event->formIntegration->integration_id);

if(!$integration) {
if (!$integration) {
return;
}

if(!isset($integration['file_name'])) {
if (!isset($integration['file_name'])) {
return;
}

$className = 'App\Integrations\Handlers\Events\\' . $integration['file_name'] . 'Created';

if(!class_exists($className)) {
if (!class_exists($className)) {
return;
}

Expand Down
2 changes: 1 addition & 1 deletion api/app/Mail/Forms/SubmissionConfirmationMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function build()

private function getFromEmail()
{
if(config('app.self_hosted')) {
if (config('app.self_hosted')) {
return config('mail.from.address');
}

Expand Down
5 changes: 3 additions & 2 deletions api/app/Models/Workspace.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Workspace extends Model implements CachableAttributes
'icon',
'user_id',
'custom_domain',
'settings'
];

protected $appends = [
Expand All @@ -33,10 +34,11 @@ class Workspace extends Model implements CachableAttributes
'is_enterprise',
];

protected function casts()
protected function casts(): array
{
return [
'custom_domains' => 'array',
'settings' => 'array'
];
}

Expand Down Expand Up @@ -201,5 +203,4 @@ public function forms()
{
return $this->hasMany(Form::class);
}

}
7 changes: 5 additions & 2 deletions api/app/Notifications/Forms/FormSubmissionNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ class FormSubmissionNotification extends Notification implements ShouldQueue
use Queueable;

public FormSubmitted $event;
private $mailer;

/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(FormSubmitted $event, private $integrationData)
public function __construct(FormSubmitted $event, private $integrationData, string $mailer)
{
$this->event = $event;
$this->mailer = $mailer;
}

/**
Expand Down Expand Up @@ -52,6 +54,7 @@ public function toMail($notifiable)
->useSignedUrlForFiles();

return (new MailMessage())
->mailer($this->mailer)
->replyTo($this->getReplyToEmail($notifiable->routes['mail']))
->from($this->getFromEmail(), config('app.name'))
->subject('New form submission for "' . $this->event->form->title . '"')
Expand All @@ -63,7 +66,7 @@ public function toMail($notifiable)

private function getFromEmail()
{
if(config('app.self_hosted')) {
if (config('app.self_hosted')) {
return config('mail.from.address');
}
$originalFromAddress = Str::of(config('mail.from.address'))->explode('@');
Expand Down
2 changes: 1 addition & 1 deletion api/app/Rules/IntegrationLogicRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public function passes($attribute, $value)

public function validate(string $attribute, mixed $value, Closure $fail): void
{
if(!$this->passes($attribute, $value)) {
if (!$this->passes($attribute, $value)) {
$fail($this->message());
}
}
Expand Down
2 changes: 1 addition & 1 deletion api/app/Rules/OneEmailPerLine.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function passes($attribute, $value)

public function validate(string $attribute, mixed $value, Closure $fail): void
{
if(!$this->passes($attribute, $value)) {
if (!$this->passes($attribute, $value)) {
$fail($this->message());
}
}
Expand Down
2 changes: 1 addition & 1 deletion api/app/Rules/StorageFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public function passes($attribute, $value): bool

public function validate(string $attribute, mixed $value, Closure $fail): void
{
if(!$this->passes($attribute, $value)) {
if (!$this->passes($attribute, $value)) {
$fail($this->message());
}
}
Expand Down
2 changes: 1 addition & 1 deletion api/app/Rules/ValidHCaptcha.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function passes($attribute, $value)
}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if(!$this->passes($attribute, $value)) {
if (!$this->passes($attribute, $value)) {
$fail($this->message());
}
}
Expand Down
10 changes: 5 additions & 5 deletions api/app/Service/Forms/FormLogicConditionChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ private function checkContains($condition, $fieldValue): bool
private function checkMatrixContains($condition, $fieldValue): bool
{

foreach($condition['value'] as $key => $value) {
if(!(array_key_exists($key, $condition['value']) && array_key_exists($key, $fieldValue))) {
foreach ($condition['value'] as $key => $value) {
if (!(array_key_exists($key, $condition['value']) && array_key_exists($key, $fieldValue))) {
return false;
}
if($condition['value'][$key] == $fieldValue[$key]) {
if ($condition['value'][$key] == $fieldValue[$key]) {
return true;
}
}
Expand All @@ -108,8 +108,8 @@ private function checkMatrixContains($condition, $fieldValue): bool

private function checkMatrixEquals($condition, $fieldValue): bool
{
foreach($condition['value'] as $key => $value) {
if($condition['value'][$key] !== $fieldValue[$key]) {
foreach ($condition['value'] as $key => $value) {
if ($condition['value'][$key] !== $fieldValue[$key]) {
return false;
}
}
Expand Down
2 changes: 1 addition & 1 deletion api/app/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ function front_url($path = '')
if (!function_exists('pricing_enabled')) {
function pricing_enabled(): bool
{
return App::environment() !== 'testing' && !is_null(config('cashier.key'));
return !is_null(config('cashier.key'));
}
}
Loading

0 comments on commit 504c7a0

Please sign in to comment.