diff --git a/src/app/Backends/LDAP.php b/src/app/Backends/LDAP.php
--- a/src/app/Backends/LDAP.php
+++ b/src/app/Backends/LDAP.php
@@ -757,8 +757,10 @@
*/
private static function setUserAttributes(User $user, array &$entry)
{
- $firstName = $user->getSetting('first_name');
- $lastName = $user->getSetting('last_name');
+ $settings = $user->getSettings(['first_name', 'last_name', 'organization']);
+
+ $firstName = $settings['first_name'];
+ $lastName = $settings['last_name'];
$cn = "unknown";
$displayname = "";
@@ -788,7 +790,7 @@
$entry['sn'] = $lastName;
$entry['userpassword'] = $user->password_ldap;
$entry['inetuserstatus'] = $user->status;
- $entry['o'] = $user->getSetting('organization');
+ $entry['o'] = $settings['organization'];
$entry['mailquota'] = 0;
$entry['alias'] = $user->aliases->pluck('alias')->toArray();
diff --git a/src/app/Console/Commands/Scalpel/TenantSetting/CreateCommand.php b/src/app/Console/Commands/Scalpel/TenantSetting/CreateCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Scalpel/TenantSetting/CreateCommand.php
@@ -0,0 +1,14 @@
+getObject(\App\Tenant::class, $this->argument('tenant'), 'title');
+
+ if (!$tenant) {
+ $this->error("Unable to find the tenant.");
+ return 1;
+ }
+
+ $tenant->settings()->orderBy('key')->get()
+ ->each(function ($entry) {
+ $text = "{$entry->key}: {$entry->value}";
+ $this->info($text);
+ });
+ }
+}
diff --git a/src/app/Documents/Receipt.php b/src/app/Documents/Receipt.php
--- a/src/app/Documents/Receipt.php
+++ b/src/app/Documents/Receipt.php
@@ -229,10 +229,9 @@
{
$user = $this->wallet->owner;
$name = $user->name();
- $organization = $user->getSetting('organization');
- $address = $user->getSetting('billing_address');
+ $settings = $user->getSettings(['organization', 'billing_address']);
- $customer = trim(($organization ?: $name) . "\n$address");
+ $customer = trim(($settings['organization'] ?: $name) . "\n" . $settings['billing_address']);
$customer = str_replace("\n", '
', htmlentities($customer));
return [
diff --git a/src/app/Http/Controllers/API/V4/OpenViduController.php b/src/app/Http/Controllers/API/V4/OpenViduController.php
--- a/src/app/Http/Controllers/API/V4/OpenViduController.php
+++ b/src/app/Http/Controllers/API/V4/OpenViduController.php
@@ -245,11 +245,12 @@
}
}
- $password = (string) $room->getSetting('password');
+ $settings = $room->getSettings(['locked', 'nomedia', 'password']);
+ $password = (string) $settings['password'];
$config = [
- 'locked' => $room->getSetting('locked') === 'true',
- 'nomedia' => $room->getSetting('nomedia') === 'true',
+ 'locked' => $settings['locked'] === 'true',
+ 'nomedia' => $settings['nomedia'] === 'true',
'password' => $isOwner ? $password : '',
'requires_password' => !$isOwner && strlen($password),
];
diff --git a/src/app/Http/Controllers/API/V4/PaymentsController.php b/src/app/Http/Controllers/API/V4/PaymentsController.php
--- a/src/app/Http/Controllers/API/V4/PaymentsController.php
+++ b/src/app/Http/Controllers/API/V4/PaymentsController.php
@@ -294,12 +294,14 @@
*/
public static function topUpWallet(Wallet $wallet): bool
{
- if ((bool) $wallet->getSetting('mandate_disabled')) {
+ $settings = $wallet->getSettings(['mandate_disabled', 'mandate_balance', 'mandate_amount']);
+
+ if (!empty($settings['mandate_disabled'])) {
return false;
}
- $min_balance = (int) (floatval($wallet->getSetting('mandate_balance')) * 100);
- $amount = (int) (floatval($wallet->getSetting('mandate_amount')) * 100);
+ $min_balance = (int) (floatval($settings['mandate_balance']) * 100);
+ $amount = (int) (floatval($settings['mandate_amount']) * 100);
// The wallet balance is greater than the auto-payment threshold
if ($wallet->balance >= $min_balance) {
@@ -346,16 +348,17 @@
public static function walletMandate(Wallet $wallet): array
{
$provider = PaymentProvider::factory($wallet);
+ $settings = $wallet->getSettings(['mandate_disabled', 'mandate_balance', 'mandate_amount']);
// Get the Mandate info
$mandate = (array) $provider->getMandate($wallet);
$mandate['amount'] = (int) (PaymentProvider::MIN_AMOUNT / 100);
$mandate['balance'] = 0;
- $mandate['isDisabled'] = !empty($mandate['id']) && $wallet->getSetting('mandate_disabled');
+ $mandate['isDisabled'] = !empty($mandate['id']) && $settings['mandate_disabled'];
foreach (['amount', 'balance'] as $key) {
- if (($value = $wallet->getSetting("mandate_{$key}")) !== null) {
+ if (($value = $settings["mandate_{$key}"]) !== null) {
$mandate[$key] = $value;
}
}
diff --git a/src/app/Http/Controllers/API/V4/WalletsController.php b/src/app/Http/Controllers/API/V4/WalletsController.php
--- a/src/app/Http/Controllers/API/V4/WalletsController.php
+++ b/src/app/Http/Controllers/API/V4/WalletsController.php
@@ -126,7 +126,7 @@
$wallet = Wallet::find($id);
if (empty($wallet) || !$this->checkTenant($wallet->owner)) {
- return $this->errorResponse(404);
+ abort(404);
}
// Only owner (or admin) has access to the wallet
diff --git a/src/app/Jobs/PasswordResetEmail.php b/src/app/Jobs/PasswordResetEmail.php
--- a/src/app/Jobs/PasswordResetEmail.php
+++ b/src/app/Jobs/PasswordResetEmail.php
@@ -7,7 +7,6 @@
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
-use Illuminate\Support\Facades\Mail;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -59,6 +58,10 @@
{
$email = $this->code->user->getSetting('external_email');
- Mail::to($email)->send(new PasswordReset($this->code));
+ \App\Mail\Helper::sendMail(
+ new PasswordReset($this->code),
+ $this->code->user->tenant_id,
+ ['to' => $email]
+ );
}
}
diff --git a/src/app/Jobs/PaymentEmail.php b/src/app/Jobs/PaymentEmail.php
--- a/src/app/Jobs/PaymentEmail.php
+++ b/src/app/Jobs/PaymentEmail.php
@@ -8,7 +8,6 @@
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
-use Illuminate\Support\Facades\Mail;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -82,28 +81,13 @@
list($to, $cc) = \App\Mail\Helper::userEmails($this->controller);
if (!empty($to)) {
- try {
- Mail::to($to)->cc($cc)->send($mail);
-
- $msg = sprintf(
- "[Payment] %s mail sent for %s (%s)",
- $label,
- $wallet->id,
- empty($cc) ? $to : implode(', ', array_merge([$to], $cc))
- );
-
- \Log::info($msg);
- } catch (\Exception $e) {
- $msg = sprintf(
- "[Payment] Failed to send mail for wallet %s (%s): %s",
- $wallet->id,
- empty($cc) ? $to : implode(', ', array_merge([$to], $cc)),
- $e->getMessage()
- );
-
- \Log::error($msg);
- throw $e;
- }
+ $params = [
+ 'to' => $to,
+ 'cc' => $cc,
+ 'add' => " for {$wallet->id}",
+ ];
+
+ \App\Mail\Helper::sendMail($mail, $this->controller->tenant_id, $params);
}
/*
diff --git a/src/app/Jobs/PaymentMandateDisabledEmail.php b/src/app/Jobs/PaymentMandateDisabledEmail.php
--- a/src/app/Jobs/PaymentMandateDisabledEmail.php
+++ b/src/app/Jobs/PaymentMandateDisabledEmail.php
@@ -8,7 +8,6 @@
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
-use Illuminate\Support\Facades\Mail;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -69,27 +68,13 @@
list($to, $cc) = \App\Mail\Helper::userEmails($this->controller);
if (!empty($to)) {
- try {
- Mail::to($to)->cc($cc)->send($mail);
-
- $msg = sprintf(
- "[PaymentMandateDisabled] Sent mail for %s (%s)",
- $this->wallet->id,
- empty($cc) ? $to : implode(', ', array_merge([$to], $cc))
- );
-
- \Log::info($msg);
- } catch (\Exception $e) {
- $msg = sprintf(
- "[PaymentMandateDisabled] Failed to send mail for wallet %s (%s): %s",
- $this->wallet->id,
- empty($cc) ? $to : implode(', ', array_merge([$to], $cc)),
- $e->getMessage()
- );
-
- \Log::error($msg);
- throw $e;
- }
+ $params = [
+ 'to' => $to,
+ 'cc' => $cc,
+ 'add' => " for {$this->wallet->id}",
+ ];
+
+ \App\Mail\Helper::sendMail($mail, $this->controller->tenant_id, $params);
}
/*
diff --git a/src/app/Jobs/SignupInvitationEmail.php b/src/app/Jobs/SignupInvitationEmail.php
--- a/src/app/Jobs/SignupInvitationEmail.php
+++ b/src/app/Jobs/SignupInvitationEmail.php
@@ -7,7 +7,6 @@
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
-use Illuminate\Support\Facades\Mail;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -50,7 +49,11 @@
*/
public function handle()
{
- Mail::to($this->invitation->email)->send(new SignupInvitationMail($this->invitation));
+ \App\Mail\Helper::sendMail(
+ new SignupInvitationMail($this->invitation),
+ $this->invitation->tenant_id,
+ ['to' => $this->invitation->email]
+ );
// Update invitation status
$this->invitation->status = SignupInvitation::STATUS_SENT;
diff --git a/src/app/Jobs/SignupVerificationEmail.php b/src/app/Jobs/SignupVerificationEmail.php
--- a/src/app/Jobs/SignupVerificationEmail.php
+++ b/src/app/Jobs/SignupVerificationEmail.php
@@ -7,7 +7,6 @@
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
-use Illuminate\Support\Facades\Mail;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@@ -58,6 +57,10 @@
*/
public function handle()
{
- Mail::to($this->code->email)->send(new SignupVerification($this->code));
+ \App\Mail\Helper::sendMail(
+ new SignupVerification($this->code),
+ null,
+ ['to' => $this->code->email]
+ );
}
}
diff --git a/src/app/Jobs/WalletCheck.php b/src/app/Jobs/WalletCheck.php
--- a/src/app/Jobs/WalletCheck.php
+++ b/src/app/Jobs/WalletCheck.php
@@ -10,7 +10,6 @@
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
-use Illuminate\Support\Facades\Mail;
class WalletCheck implements ShouldQueue
{
@@ -121,9 +120,7 @@
// TODO: Should we check if the account is already suspended?
- $label = "Notification sent for";
-
- $this->sendMail(\App\Mail\NegativeBalance::class, false, $label);
+ $this->sendMail(\App\Mail\NegativeBalance::class, false);
$now = \Carbon\Carbon::now()->toDateTimeString();
$this->wallet->setSetting('balance_warning_initial', $now);
@@ -140,9 +137,7 @@
// TODO: Should we check if the account is already suspended?
- $label = "Reminder sent for";
-
- $this->sendMail(\App\Mail\NegativeBalanceReminder::class, false, $label);
+ $this->sendMail(\App\Mail\NegativeBalanceReminder::class, false);
$now = \Carbon\Carbon::now()->toDateTimeString();
$this->wallet->setSetting('balance_warning_reminder', $now);
@@ -173,9 +168,7 @@
}
}
- $label = "Account suspended";
-
- $this->sendMail(\App\Mail\NegativeBalanceSuspended::class, true, $label);
+ $this->sendMail(\App\Mail\NegativeBalanceSuspended::class, true);
$now = \Carbon\Carbon::now()->toDateTimeString();
$this->wallet->setSetting('balance_warning_suspended', $now);
@@ -195,9 +188,7 @@
return;
}
- $label = "Last warning sent for";
-
- $this->sendMail(\App\Mail\NegativeBalanceBeforeDelete::class, true, $label);
+ $this->sendMail(\App\Mail\NegativeBalanceBeforeDelete::class, true);
$now = \Carbon\Carbon::now()->toDateTimeString();
$this->wallet->setSetting('balance_warning_before_delete', $now);
@@ -232,9 +223,8 @@
*
* @param string $class Mailable class name
* @param bool $with_external Use users's external email
- * @param ?string $log_label Log label
*/
- protected function sendMail($class, $with_external = false, $log_label = null): void
+ protected function sendMail($class, $with_external = false): void
{
// TODO: Send the email to all wallet controllers?
@@ -243,30 +233,13 @@
list($to, $cc) = \App\Mail\Helper::userEmails($this->wallet->owner, $with_external);
if (!empty($to) || !empty($cc)) {
- try {
- Mail::to($to)->cc($cc)->send($mail);
-
- if ($log_label) {
- $msg = sprintf(
- "[WalletCheck] %s %s (%s)",
- $log_label,
- $this->wallet->id,
- empty($cc) ? $to : implode(', ', array_merge([$to], $cc)),
- );
-
- \Log::info($msg);
- }
- } catch (\Exception $e) {
- $msg = sprintf(
- "[WalletCheck] Failed to send mail for %s (%s): %s",
- $this->wallet->id,
- empty($cc) ? $to : implode(', ', array_merge([$to], $cc)),
- $e->getMessage()
- );
+ $params = [
+ 'to' => $to,
+ 'cc' => $cc,
+ 'add' => " for {$this->wallet->id}",
+ ];
- \Log::error($msg);
- throw $e;
- }
+ \App\Mail\Helper::sendMail($mail, $this->wallet->owner->tenant_id, $params);
}
}
diff --git a/src/app/Mail/Helper.php b/src/app/Mail/Helper.php
--- a/src/app/Mail/Helper.php
+++ b/src/app/Mail/Helper.php
@@ -2,7 +2,9 @@
namespace App\Mail;
+use App\Tenant;
use Illuminate\Mail\Mailable;
+use Illuminate\Support\Facades\Mail;
class Helper
{
@@ -32,6 +34,73 @@
}
/**
+ * Sends an email
+ *
+ * @param Mailable $mail Email content generator
+ * @param int|null $tenantId Tenant identifier
+ * @param array $params Email parameters: to, cc
+ *
+ * @throws \Exception
+ */
+ public static function sendMail(Mailable $mail, $tenantId = null, array $params = []): void
+ {
+ $class = explode("\\", get_class($mail));
+ $class = end($class);
+
+ $getRecipients = function () use ($params) {
+ $recipients = [];
+
+ // For now we do not support addresses + names, only addresses
+ foreach (['to', 'cc'] as $idx) {
+ if (!empty($params[$idx])) {
+ if (is_array($params[$idx])) {
+ $recipients = array_merge($recipients, $params[$idx]);
+ } else {
+ $recipients[] = $params[$idx];
+ }
+ }
+ }
+
+ return implode(', ', $recipients);
+ };
+
+ try {
+ if (!empty($params['to'])) {
+ $mail->to($params['to']);
+ }
+
+ if (!empty($params['cc'])) {
+ $mail->cc($params['cc']);
+ }
+
+ $fromAddress = Tenant::getConfig($tenantId, 'mail.from.address');
+ $fromName = Tenant::getConfig($tenantId, 'mail.from.name');
+ $replytoAddress = Tenant::getConfig($tenantId, 'mail.reply_to.address');
+ $replytoName = Tenant::getConfig($tenantId, 'mail.reply_to.name');
+
+ if ($fromAddress) {
+ $mail->from($fromAddress, $fromName);
+ }
+
+ if ($replytoAddress) {
+ $mail->replyTo($replytoAddress, $replytoName);
+ }
+
+ Mail::send($mail);
+
+ $msg = sprintf("[%s] Sent mail to %s%s", $class, $getRecipients(), $params['add'] ?? '');
+
+ \Log::info($msg);
+ } catch (\Exception $e) {
+ $format = "[%s] Failed to send mail to %s%s: %s";
+ $msg = sprintf($format, $class, $getRecipients(), $params['add'] ?? '', $e->getMessage());
+
+ \Log::error($msg);
+ throw $e;
+ }
+ }
+
+ /**
* Return user's email addresses, separately for use in To and Cc.
*
* @param \App\User $user The user
diff --git a/src/app/Mail/NegativeBalance.php b/src/app/Mail/NegativeBalance.php
--- a/src/app/Mail/NegativeBalance.php
+++ b/src/app/Mail/NegativeBalance.php
@@ -2,6 +2,7 @@
namespace App\Mail;
+use App\Tenant;
use App\User;
use App\Utils;
use App\Wallet;
@@ -42,17 +43,20 @@
*/
public function build()
{
- $subject = \trans('mail.negativebalance-subject', ['site' => \config('app.name')]);
+ $appName = Tenant::getConfig($this->user->tenant_id, 'app.name');
+ $supportUrl = Tenant::getConfig($this->user->tenant_id, 'app.support_url');
+
+ $subject = \trans('mail.negativebalance-subject', ['site' => $appName]);
$this->view('emails.html.negative_balance')
->text('emails.plain.negative_balance')
->subject($subject)
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'subject' => $subject,
'username' => $this->user->name(true),
- 'supportUrl' => \config('app.support_url'),
- 'walletUrl' => Utils::serviceUrl('/wallet'),
+ 'supportUrl' => $supportUrl,
+ 'walletUrl' => Utils::serviceUrl('/wallet', $this->user->tenant_id),
]);
return $this;
diff --git a/src/app/Mail/NegativeBalanceBeforeDelete.php b/src/app/Mail/NegativeBalanceBeforeDelete.php
--- a/src/app/Mail/NegativeBalanceBeforeDelete.php
+++ b/src/app/Mail/NegativeBalanceBeforeDelete.php
@@ -3,6 +3,7 @@
namespace App\Mail;
use App\Jobs\WalletCheck;
+use App\Tenant;
use App\User;
use App\Utils;
use App\Wallet;
@@ -44,18 +45,20 @@
public function build()
{
$threshold = WalletCheck::threshold($this->wallet, WalletCheck::THRESHOLD_DELETE);
+ $appName = Tenant::getConfig($this->user->tenant_id, 'app.name');
+ $supportUrl = Tenant::getConfig($this->user->tenant_id, 'app.support_url');
- $subject = \trans('mail.negativebalancebeforedelete-subject', ['site' => \config('app.name')]);
+ $subject = \trans('mail.negativebalancebeforedelete-subject', ['site' => $appName]);
$this->view('emails.html.negative_balance_before_delete')
->text('emails.plain.negative_balance_before_delete')
->subject($subject)
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'subject' => $subject,
'username' => $this->user->name(true),
- 'supportUrl' => \config('app.support_url'),
- 'walletUrl' => Utils::serviceUrl('/wallet'),
+ 'supportUrl' => $supportUrl,
+ 'walletUrl' => Utils::serviceUrl('/wallet', $this->user->tenant_id),
'date' => $threshold->toDateString(),
]);
diff --git a/src/app/Mail/NegativeBalanceReminder.php b/src/app/Mail/NegativeBalanceReminder.php
--- a/src/app/Mail/NegativeBalanceReminder.php
+++ b/src/app/Mail/NegativeBalanceReminder.php
@@ -3,6 +3,7 @@
namespace App\Mail;
use App\Jobs\WalletCheck;
+use App\Tenant;
use App\User;
use App\Utils;
use App\Wallet;
@@ -44,18 +45,20 @@
public function build()
{
$threshold = WalletCheck::threshold($this->wallet, WalletCheck::THRESHOLD_SUSPEND);
+ $appName = Tenant::getConfig($this->user->tenant_id, 'app.name');
+ $supportUrl = Tenant::getConfig($this->user->tenant_id, 'app.support_url');
- $subject = \trans('mail.negativebalancereminder-subject', ['site' => \config('app.name')]);
+ $subject = \trans('mail.negativebalancereminder-subject', ['site' => $appName]);
$this->view('emails.html.negative_balance_reminder')
->text('emails.plain.negative_balance_reminder')
->subject($subject)
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'subject' => $subject,
'username' => $this->user->name(true),
- 'supportUrl' => \config('app.support_url'),
- 'walletUrl' => Utils::serviceUrl('/wallet'),
+ 'supportUrl' => $supportUrl,
+ 'walletUrl' => Utils::serviceUrl('/wallet', $this->user->tenant_id),
'date' => $threshold->toDateString(),
]);
diff --git a/src/app/Mail/NegativeBalanceSuspended.php b/src/app/Mail/NegativeBalanceSuspended.php
--- a/src/app/Mail/NegativeBalanceSuspended.php
+++ b/src/app/Mail/NegativeBalanceSuspended.php
@@ -3,6 +3,7 @@
namespace App\Mail;
use App\Jobs\WalletCheck;
+use App\Tenant;
use App\User;
use App\Utils;
use App\Wallet;
@@ -44,18 +45,20 @@
public function build()
{
$threshold = WalletCheck::threshold($this->wallet, WalletCheck::THRESHOLD_DELETE);
+ $appName = Tenant::getConfig($this->user->tenant_id, 'app.name');
+ $supportUrl = Tenant::getConfig($this->user->tenant_id, 'app.support_url');
- $subject = \trans('mail.negativebalancesuspended-subject', ['site' => \config('app.name')]);
+ $subject = \trans('mail.negativebalancesuspended-subject', ['site' => $appName]);
$this->view('emails.html.negative_balance_suspended')
->text('emails.plain.negative_balance_suspended')
->subject($subject)
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'subject' => $subject,
'username' => $this->user->name(true),
- 'supportUrl' => \config('app.support_url'),
- 'walletUrl' => Utils::serviceUrl('/wallet'),
+ 'supportUrl' => $supportUrl,
+ 'walletUrl' => Utils::serviceUrl('/wallet', $this->user->tenant_id),
'date' => $threshold->toDateString(),
]);
diff --git a/src/app/Mail/PasswordReset.php b/src/app/Mail/PasswordReset.php
--- a/src/app/Mail/PasswordReset.php
+++ b/src/app/Mail/PasswordReset.php
@@ -2,6 +2,7 @@
namespace App\Mail;
+use App\Tenant;
use App\User;
use App\Utils;
use App\VerificationCode;
@@ -38,15 +39,19 @@
*/
public function build()
{
+ $appName = Tenant::getConfig($this->code->user->tenant_id, 'app.name');
+ $supportUrl = Tenant::getConfig($this->code->user->tenant_id, 'app.support_url');
+
$href = Utils::serviceUrl(
- sprintf('/password-reset/%s-%s', $this->code->short_code, $this->code->code)
+ sprintf('/password-reset/%s-%s', $this->code->short_code, $this->code->code),
+ $this->code->user->tenant_id
);
$this->view('emails.html.password_reset')
->text('emails.plain.password_reset')
- ->subject(__('mail.passwordreset-subject', ['site' => \config('app.name')]))
+ ->subject(\trans('mail.passwordreset-subject', ['site' => $appName]))
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'code' => $this->code->code,
'short_code' => $this->code->short_code,
'link' => sprintf('%s', $href, $href),
diff --git a/src/app/Mail/PaymentFailure.php b/src/app/Mail/PaymentFailure.php
--- a/src/app/Mail/PaymentFailure.php
+++ b/src/app/Mail/PaymentFailure.php
@@ -3,6 +3,7 @@
namespace App\Mail;
use App\Payment;
+use App\Tenant;
use App\User;
use App\Utils;
use Illuminate\Bus\Queueable;
@@ -42,19 +43,20 @@
*/
public function build()
{
- $user = $this->user;
+ $appName = Tenant::getConfig($this->user->tenant_id, 'app.name');
+ $supportUrl = Tenant::getConfig($this->user->tenant_id, 'app.support_url');
- $subject = \trans('mail.paymentfailure-subject', ['site' => \config('app.name')]);
+ $subject = \trans('mail.paymentfailure-subject', ['site' => $appName]);
$this->view('emails.html.payment_failure')
->text('emails.plain.payment_failure')
->subject($subject)
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'subject' => $subject,
- 'username' => $user->name(true),
- 'walletUrl' => Utils::serviceUrl('/wallet'),
- 'supportUrl' => \config('app.support_url'),
+ 'username' => $this->user->name(true),
+ 'walletUrl' => Utils::serviceUrl('/wallet', $this->user->tenant_id),
+ 'supportUrl' => $supportUrl,
]);
return $this;
diff --git a/src/app/Mail/PaymentMandateDisabled.php b/src/app/Mail/PaymentMandateDisabled.php
--- a/src/app/Mail/PaymentMandateDisabled.php
+++ b/src/app/Mail/PaymentMandateDisabled.php
@@ -2,6 +2,7 @@
namespace App\Mail;
+use App\Tenant;
use App\User;
use App\Utils;
use App\Wallet;
@@ -42,19 +43,20 @@
*/
public function build()
{
- $user = $this->user;
+ $appName = Tenant::getConfig($this->user->tenant_id, 'app.name');
+ $supportUrl = Tenant::getConfig($this->user->tenant_id, 'app.support_url');
- $subject = \trans('mail.paymentmandatedisabled-subject', ['site' => \config('app.name')]);
+ $subject = \trans('mail.paymentmandatedisabled-subject', ['site' => $appName]);
$this->view('emails.html.payment_mandate_disabled')
->text('emails.plain.payment_mandate_disabled')
->subject($subject)
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'subject' => $subject,
- 'username' => $user->name(true),
- 'walletUrl' => Utils::serviceUrl('/wallet'),
- 'supportUrl' => \config('app.support_url'),
+ 'username' => $this->user->name(true),
+ 'walletUrl' => Utils::serviceUrl('/wallet', $this->user->tenant_id),
+ 'supportUrl' => $supportUrl,
]);
return $this;
diff --git a/src/app/Mail/PaymentSuccess.php b/src/app/Mail/PaymentSuccess.php
--- a/src/app/Mail/PaymentSuccess.php
+++ b/src/app/Mail/PaymentSuccess.php
@@ -3,6 +3,7 @@
namespace App\Mail;
use App\Payment;
+use App\Tenant;
use App\User;
use App\Utils;
use Illuminate\Bus\Queueable;
@@ -42,19 +43,20 @@
*/
public function build()
{
- $user = $this->user;
+ $appName = Tenant::getConfig($this->user->tenant_id, 'app.name');
+ $supportUrl = Tenant::getConfig($this->user->tenant_id, 'app.support_url');
- $subject = \trans('mail.paymentsuccess-subject', ['site' => \config('app.name')]);
+ $subject = \trans('mail.paymentsuccess-subject', ['site' => $appName]);
$this->view('emails.html.payment_success')
->text('emails.plain.payment_success')
->subject($subject)
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'subject' => $subject,
- 'username' => $user->name(true),
- 'walletUrl' => Utils::serviceUrl('/wallet'),
- 'supportUrl' => \config('app.support_url'),
+ 'username' => $this->user->name(true),
+ 'walletUrl' => Utils::serviceUrl('/wallet', $this->user->tenant_id),
+ 'supportUrl' => $supportUrl,
]);
return $this;
diff --git a/src/app/Mail/SignupInvitation.php b/src/app/Mail/SignupInvitation.php
--- a/src/app/Mail/SignupInvitation.php
+++ b/src/app/Mail/SignupInvitation.php
@@ -3,6 +3,7 @@
namespace App\Mail;
use App\SignupInvitation as SI;
+use App\Tenant;
use App\Utils;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
@@ -37,13 +38,15 @@
*/
public function build()
{
- $href = Utils::serviceUrl('/signup/invite/' . $this->invitation->id);
+ $appName = Tenant::getConfig($this->invitation->tenant_id, 'app.name');
+
+ $href = Utils::serviceUrl('/signup/invite/' . $this->invitation->id, $this->invitation->tenant_id);
$this->view('emails.html.signup_invitation')
->text('emails.plain.signup_invitation')
- ->subject(__('mail.signupinvitation-subject', ['site' => \config('app.name')]))
+ ->subject(\trans('mail.signupinvitation-subject', ['site' => $appName]))
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'href' => $href,
]);
diff --git a/src/app/Mail/SignupVerification.php b/src/app/Mail/SignupVerification.php
--- a/src/app/Mail/SignupVerification.php
+++ b/src/app/Mail/SignupVerification.php
@@ -49,7 +49,7 @@
$this->view('emails.html.signup_code')
->text('emails.plain.signup_code')
- ->subject(__('mail.signupcode-subject', ['site' => \config('app.name')]))
+ ->subject(\trans('mail.signupcode-subject', ['site' => \config('app.name')]))
->with([
'site' => \config('app.name'),
'username' => $username ?: 'User',
diff --git a/src/app/Mail/SuspendedDebtor.php b/src/app/Mail/SuspendedDebtor.php
--- a/src/app/Mail/SuspendedDebtor.php
+++ b/src/app/Mail/SuspendedDebtor.php
@@ -2,6 +2,7 @@
namespace App\Mail;
+use App\Tenant;
use App\User;
use App\Utils;
use Illuminate\Bus\Queueable;
@@ -36,13 +37,15 @@
*/
public function build()
{
- $user = $this->account;
+ $appName = Tenant::getConfig($this->account->tenant_id, 'app.name');
+ $supportUrl = Tenant::getConfig($this->account->tenant_id, 'app.support_url');
+ $cancelUrl = Tenant::getConfig($this->account->tenant_id, 'app.kb.account_delete');
- $subject = \trans('mail.suspendeddebtor-subject', ['site' => \config('app.name')]);
+ $subject = \trans('mail.suspendeddebtor-subject', ['site' => $appName]);
$moreInfoHtml = null;
$moreInfoText = null;
- if ($moreInfoUrl = \config('app.kb.account_suspended')) {
+ if ($moreInfoUrl = Tenant::getConfig($this->account->tenant_id, 'app.kb.account_suspended')) {
$moreInfoHtml = \trans('mail.more-info-html', ['href' => $moreInfoUrl]);
$moreInfoText = \trans('mail.more-info-text', ['href' => $moreInfoUrl]);
}
@@ -51,12 +54,12 @@
->text('emails.plain.suspended_debtor')
->subject($subject)
->with([
- 'site' => \config('app.name'),
+ 'site' => $appName,
'subject' => $subject,
- 'username' => $user->name(true),
- 'cancelUrl' => \config('app.kb.account_delete'),
- 'supportUrl' => \config('app.support_url'),
- 'walletUrl' => Utils::serviceUrl('/wallet'),
+ 'username' => $this->account->name(true),
+ 'cancelUrl' => $cancelUrl,
+ 'supportUrl' => $supportUrl,
+ 'walletUrl' => Utils::serviceUrl('/wallet', $this->account->tenant_id),
'moreInfoHtml' => $moreInfoHtml,
'moreInfoText' => $moreInfoText,
'days' => 14 // TODO: Configurable
diff --git a/src/app/Providers/AppServiceProvider.php b/src/app/Providers/AppServiceProvider.php
--- a/src/app/Providers/AppServiceProvider.php
+++ b/src/app/Providers/AppServiceProvider.php
@@ -89,20 +89,7 @@
Builder::macro(
'withObjectTenantContext',
function (object $object, string $table = null) {
- // backend artisan cli
- if (app()->runningInConsole()) {
- /** @var Builder $this */
- return $this->where(($table ? "$table." : "") . "tenant_id", $object->tenant_id);
- }
-
- $subject = auth()->user();
-
- if ($subject->role == "admin") {
- /** @var Builder $this */
- return $this->where(($table ? "$table." : "") . "tenant_id", $object->tenant_id);
- }
-
- $tenantId = $subject->tenant_id;
+ $tenantId = $object->tenant_id;
if ($tenantId) {
/** @var Builder $this */
diff --git a/src/app/Providers/Payment/Mollie.php b/src/app/Providers/Payment/Mollie.php
--- a/src/app/Providers/Payment/Mollie.php
+++ b/src/app/Providers/Payment/Mollie.php
@@ -470,13 +470,12 @@
*/
protected static function mollieMandate(Wallet $wallet)
{
- $customer_id = $wallet->getSetting('mollie_id');
- $mandate_id = $wallet->getSetting('mollie_mandate_id');
+ $settings = $wallet->getSettings(['mollie_id', 'mollie_mandate_id']);
// Get the manadate reference we already have
- if ($customer_id && $mandate_id) {
+ if ($settings['mollie_id'] && $settings['mollie_mandate_id']) {
try {
- return mollie()->mandates()->getForId($customer_id, $mandate_id);
+ return mollie()->mandates()->getForId($settings['mollie_id'], $settings['mollie_mandate_id']);
} catch (ApiException $e) {
// FIXME: What about 404?
if ($e->getCode() == 410) {
diff --git a/src/app/Providers/PaymentProvider.php b/src/app/Providers/PaymentProvider.php
--- a/src/app/Providers/PaymentProvider.php
+++ b/src/app/Providers/PaymentProvider.php
@@ -49,9 +49,11 @@
private static function providerName($provider_or_wallet = null): string
{
if ($provider_or_wallet instanceof Wallet) {
- if ($provider_or_wallet->getSetting('stripe_id')) {
+ $settings = $provider_or_wallet->getSettings(['stripe_id', 'mollie_id']);
+
+ if ($settings['stripe_id']) {
$provider = self::PROVIDER_STRIPE;
- } elseif ($provider_or_wallet->getSetting('mollie_id')) {
+ } elseif ($settings['mollie_id']) {
$provider = self::PROVIDER_MOLLIE;
}
} else {
diff --git a/src/app/Tenant.php b/src/app/Tenant.php
--- a/src/app/Tenant.php
+++ b/src/app/Tenant.php
@@ -2,6 +2,7 @@
namespace App;
+use App\Traits\SettingsTrait;
use Illuminate\Database\Eloquent\Model;
/**
@@ -12,6 +13,8 @@
*/
class Tenant extends Model
{
+ use SettingsTrait;
+
protected $fillable = [
'id',
'title',
@@ -20,6 +23,44 @@
protected $keyType = 'bigint';
/**
+ * Utility method to get tenant-specific system setting.
+ * If the setting is not specified for the tenant a system-wide value will be returned.
+ *
+ * @param int $tenantId Tenant identifier
+ * @param string $key Setting name
+ *
+ * @return mixed Setting value
+ */
+ public static function getConfig($tenantId, string $key)
+ {
+ // Cache the tenant instance in memory
+ static $tenant;
+
+ if (empty($tenant) || $tenant->id != $tenantId) {
+ $tenant = null;
+ if ($tenantId) {
+ $tenant = self::findOrFail($tenantId);
+ }
+ }
+
+ // Supported options (TODO: document this somewhere):
+ // - app.name (tenants.title will be returned)
+ // - app.public_url and app.url
+ // - app.support_url
+ // - mail.from.address and mail.from.name
+ // - mail.reply_to.address and mail.reply_to.name
+ // - app.kb.account_delete and app.kb.account_suspended
+
+ if ($key == 'app.name') {
+ return $tenant ? $tenant->title : \config($key);
+ }
+
+ $value = $tenant ? $tenant->getSetting($key) : null;
+
+ return $value !== null ? $value : \config($key);
+ }
+
+ /**
* Discounts assigned to this tenant.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
@@ -30,6 +71,16 @@
}
/**
+ * Any (additional) settings of this tenant.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\HasMany
+ */
+ public function settings()
+ {
+ return $this->hasMany('App\TenantSetting');
+ }
+
+ /**
* SignupInvitations assigned to this tenant.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
diff --git a/src/app/TenantSetting.php b/src/app/TenantSetting.php
new file mode 100644
--- /dev/null
+++ b/src/app/TenantSetting.php
@@ -0,0 +1,30 @@
+belongsTo('\App\Tenant', 'tenant_id', 'id');
+ }
+}
diff --git a/src/app/Traits/SettingsTrait.php b/src/app/Traits/SettingsTrait.php
--- a/src/app/Traits/SettingsTrait.php
+++ b/src/app/Traits/SettingsTrait.php
@@ -2,8 +2,6 @@
namespace App\Traits;
-use Illuminate\Support\Facades\Cache;
-
trait SettingsTrait
{
/**
@@ -16,21 +14,39 @@
* $locale = $user->getSetting('locale');
* ```
*
- * @param string $key Setting name
+ * @param string $key Setting name
+ * @param mixed $default Default value, to be used if not found
*
* @return string|null Setting value
*/
- public function getSetting(string $key)
+ public function getSetting(string $key, $default = null)
+ {
+ $setting = $this->settings()->where('key', $key)->first();
+
+ return $setting ? $setting->value : $default;
+ }
+
+ /**
+ * Obtain the values for many settings in one go (for better performance).
+ *
+ * @param array $keys Setting names
+ *
+ * @return array Setting key=value hash, includes also requested but non-existing settings
+ */
+ public function getSettings(array $keys): array
{
- $settings = $this->getCache();
+ $settings = [];
- if (!array_key_exists($key, $settings)) {
- return null;
+ foreach ($keys as $key) {
+ $settings[$key] = null;
}
- $value = $settings[$key];
+ $this->settings()->whereIn('key', $keys)->get()
+ ->each(function ($setting) use (&$settings) {
+ $settings[$setting->key] = $setting->value;
+ });
- return empty($value) ? null : $value;
+ return $settings;
}
/**
@@ -70,7 +86,6 @@
public function setSetting(string $key, $value): void
{
$this->storeSetting($key, $value);
- $this->setCache();
}
/**
@@ -92,10 +107,16 @@
foreach ($data as $key => $value) {
$this->storeSetting($key, $value);
}
-
- $this->setCache();
}
+ /**
+ * Create or update a setting.
+ *
+ * @param string $key Setting name
+ * @param string|null $value The new value for the setting.
+ *
+ * @return void
+ */
private function storeSetting(string $key, $value): void
{
if ($value === null || $value === '') {
@@ -110,35 +131,4 @@
);
}
}
-
- private function getCache()
- {
- $model = \strtolower(get_class($this));
-
- if (Cache::has("{$model}_settings_{$this->id}")) {
- return Cache::get("{$model}_settings_{$this->id}");
- }
-
- return $this->setCache();
- }
-
- private function setCache()
- {
- $model = \strtolower(get_class($this));
-
- if (Cache::has("{$model}_settings_{$this->id}")) {
- Cache::forget("{$model}_settings_{$this->id}");
- }
-
- $cached = [];
- foreach ($this->settings()->get() as $entry) {
- if ($entry->value !== null && $entry->value !== '') {
- $cached[$entry->key] = $entry->value;
- }
- }
-
- Cache::forever("{$model}_settings_{$this->id}", $cached);
-
- return $this->getCache();
- }
}
diff --git a/src/app/User.php b/src/app/User.php
--- a/src/app/User.php
+++ b/src/app/User.php
@@ -545,13 +545,12 @@
*/
public function name(bool $fallback = false): string
{
- $firstname = $this->getSetting('first_name');
- $lastname = $this->getSetting('last_name');
+ $settings = $this->getSettings(['first_name', 'last_name']);
- $name = trim($firstname . ' ' . $lastname);
+ $name = trim($settings['first_name'] . ' ' . $settings['last_name']);
if (empty($name) && $fallback) {
- return \config('app.name') . ' User';
+ return trim(\trans('app.siteuser', ['site' => \App\Tenant::getConfig($this->tenant_id, 'app.name')]));
}
return $name;
diff --git a/src/app/Utils.php b/src/app/Utils.php
--- a/src/app/Utils.php
+++ b/src/app/Utils.php
@@ -471,17 +471,19 @@
/**
* Create self URL
*
- * @param string $route Route/Path
+ * @param string $route Route/Path
+ * @param int|null $tenantId Current tenant
+ *
* @todo Move this to App\Http\Controllers\Controller
*
* @return string Full URL
*/
- public static function serviceUrl(string $route): string
+ public static function serviceUrl(string $route, $tenantId = null): string
{
- $url = \config('app.public_url');
+ $url = \App\Tenant::getConfig($tenantId, 'app.public_url');
if (!$url) {
- $url = \config('app.url');
+ $url = \App\Tenant::getConfig($tenantId, 'app.url');
}
return rtrim(trim($url, '/') . '/' . ltrim($route, '/'), '/');
diff --git a/src/database/migrations/2021_07_12_100000_create_tenant_settings_table.php b/src/database/migrations/2021_07_12_100000_create_tenant_settings_table.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2021_07_12_100000_create_tenant_settings_table.php
@@ -0,0 +1,45 @@
+bigIncrements('id');
+ $table->unsignedBigInteger('tenant_id');
+ $table->string('key');
+ $table->text('value');
+ $table->timestamp('created_at')->useCurrent();
+ $table->timestamp('updated_at')->useCurrent();
+
+ $table->foreign('tenant_id')->references('id')->on('tenants')
+ ->onDelete('cascade')->onUpdate('cascade');
+
+ $table->unique(['tenant_id', 'key']);
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('tenant_settings');
+ }
+}
diff --git a/src/resources/lang/en/app.php b/src/resources/lang/en/app.php
--- a/src/resources/lang/en/app.php
+++ b/src/resources/lang/en/app.php
@@ -68,6 +68,8 @@
'support-request-success' => 'Support request submitted successfully.',
'support-request-error' => 'Failed to submit the support request.',
+ 'siteuser' => ':site User',
+
'wallet-award-success' => 'The bonus has been added to the wallet successfully.',
'wallet-penalty-success' => 'The penalty has been added to the wallet successfully.',
'wallet-update-success' => 'User wallet updated successfully.',
diff --git a/src/tests/Browser/Meet/RoomControlsTest.php b/src/tests/Browser/Meet/RoomControlsTest.php
--- a/src/tests/Browser/Meet/RoomControlsTest.php
+++ b/src/tests/Browser/Meet/RoomControlsTest.php
@@ -167,7 +167,7 @@
// Test muting audio
$owner->click('@menu button.link-audio')
->assertToolbarButtonState('audio', RoomPage::BUTTON_ACTIVE | RoomPage::BUTTON_ENABLED)
- ->assertVisible('div.meet-video.self .status .status-audio');
+ ->waitFor('div.meet-video.self .status .status-audio');
// FIXME: It looks that we can't just check the