Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117767567
D2659.1775234089.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
63 KB
Referenced Files
None
Subscribers
None
D2659.1775234089.diff
View Options
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 @@
+<?php
+
+namespace App\Console\Commands\Scalpel\TenantSetting;
+
+use App\Console\ObjectCreateCommand;
+
+class CreateCommand extends ObjectCreateCommand
+{
+ protected $cacheKeys = ['app\tenant_settings_%tenant_id%'];
+ protected $commandPrefix = 'scalpel';
+ protected $objectClass = \App\TenantSetting::class;
+ protected $objectName = 'tenant-setting';
+ protected $objectTitle = null;
+}
diff --git a/src/app/Console/Commands/Scalpel/TenantSetting/ReadCommand.php b/src/app/Console/Commands/Scalpel/TenantSetting/ReadCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Scalpel/TenantSetting/ReadCommand.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Console\Commands\Scalpel\TenantSetting;
+
+use App\Console\ObjectReadCommand;
+
+class ReadCommand extends ObjectReadCommand
+{
+ protected $hidden = true;
+
+ protected $commandPrefix = 'scalpel';
+ protected $objectClass = \App\TenantSetting::class;
+ protected $objectName = 'tenant-setting';
+ protected $objectTitle = null;
+}
diff --git a/src/app/Console/Commands/Scalpel/TenantSetting/UpdateCommand.php b/src/app/Console/Commands/Scalpel/TenantSetting/UpdateCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Scalpel/TenantSetting/UpdateCommand.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace App\Console\Commands\Scalpel\TenantSetting;
+
+use App\Console\ObjectUpdateCommand;
+
+class UpdateCommand extends ObjectUpdateCommand
+{
+ protected $cacheKeys = ['app\tenant_settings_%tenant_id%'];
+ protected $commandPrefix = 'scalpel';
+ protected $objectClass = \App\TenantSetting::class;
+ protected $objectName = 'tenant-setting';
+ protected $objectTitle = null;
+}
diff --git a/src/app/Console/Commands/Tenant/ListSettingsCommand.php b/src/app/Console/Commands/Tenant/ListSettingsCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Tenant/ListSettingsCommand.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Console\Commands\Tenant;
+
+use App\Console\Command;
+
+class ListSettingsCommand extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'tenant:list-settings {tenant}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'List settings for the tenant.';
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $tenant = $this->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", '<br>', 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
{
@@ -31,6 +33,73 @@
return $mail->build()->render(); // @phpstan-ignore-line
}
+ /**
+ * 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.
*
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('<a href="%s">%s</a>', $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
@@ -82,20 +82,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',
@@ -19,6 +22,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.
*
@@ -29,6 +70,16 @@
return $this->hasMany('App\Discount');
}
+ /**
+ * 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.
*
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 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * A collection of settings for a Tenant.
+ *
+ * @property int $id
+ * @property string $key
+ * @property int $tenant_id
+ * @property string $value
+ */
+class TenantSetting extends Model
+{
+ protected $fillable = [
+ 'tenant_id', 'key', 'value'
+ ];
+
+ /**
+ * The tenant to which this setting belongs.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ */
+ public function tenant()
+ {
+ return $this->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
@@ -543,13 +543,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
@@ -367,17 +367,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 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Schema;
+
+// phpcs:ignore
+class CreateTenantSettingsTable extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::create(
+ 'tenant_settings',
+ function (Blueprint $table) {
+ $table->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
@@ -66,6 +66,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 <video> element state
// We might consider using OpenVidu API to make sure
diff --git a/src/tests/Browser/Meet/RoomSetupTest.php b/src/tests/Browser/Meet/RoomSetupTest.php
--- a/src/tests/Browser/Meet/RoomSetupTest.php
+++ b/src/tests/Browser/Meet/RoomSetupTest.php
@@ -409,8 +409,8 @@
// Demote the guest to a subscriber
$browser
- ->waitFor('div.meet-video.self')
- ->waitFor('div.meet-video:not(.self)')
+ ->waitFor('div.meet-video.self video')
+ ->waitFor('div.meet-video:not(.self) video')
->assertElementsCount('@session div.meet-video', 2)
->assertElementsCount('@session video', 2)
->assertElementsCount('@session .meet-subscriber', 0)
diff --git a/src/tests/Feature/Console/Scalpel/TenantSetting/CreateCommandTest.php b/src/tests/Feature/Console/Scalpel/TenantSetting/CreateCommandTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Scalpel/TenantSetting/CreateCommandTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Tests\Feature\Console\Scalpel\TenantSetting;
+
+use Tests\TestCase;
+
+class CreateCommandTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ \App\TenantSetting::truncate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ \App\TenantSetting::truncate();
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test the command
+ */
+ public function testHandle(): void
+ {
+ $tenant = \App\Tenant::whereNotIn('id', [1])->first();
+
+ $this->artisan("scalpel:tenant-setting:create --key=test --value=init --tenant_id={$tenant->id}")
+ ->assertExitCode(0);
+
+ $setting = $tenant->settings()->where('key', 'test')->first();
+
+ $this->assertSame('init', $setting->fresh()->value);
+ $this->assertSame('init', $tenant->fresh()->getSetting('test'));
+ }
+}
diff --git a/src/tests/Feature/Console/Scalpel/TenantSetting/UpdateCommandTest.php b/src/tests/Feature/Console/Scalpel/TenantSetting/UpdateCommandTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Scalpel/TenantSetting/UpdateCommandTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Tests\Feature\Console\Scalpel\TenantSetting;
+
+use Tests\TestCase;
+
+class UpdateCommandTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ \App\TenantSetting::truncate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ \App\TenantSetting::truncate();
+
+ parent::tearDown();
+ }
+
+ public function testHandle(): void
+ {
+ $this->artisan("scalpel:tenant-setting:update unknown --value=test")
+ ->assertExitCode(1)
+ ->expectsOutput("No such tenant-setting unknown");
+
+ $tenant = \App\Tenant::whereNotIn('id', [1])->first();
+ $tenant->setSetting('test', 'test-old');
+ $setting = $tenant->settings()->where('key', 'test')->first();
+
+ $this->assertSame('test-old', $setting->value);
+
+ $this->artisan("scalpel:tenant-setting:update {$setting->id} --value=test")
+ ->assertExitCode(0);
+
+ $this->assertSame('test', $setting->fresh()->value);
+ $this->assertSame('test', $tenant->fresh()->getSetting('test'));
+ }
+}
diff --git a/src/tests/Feature/Console/Tenant/ListSettingsTest.php b/src/tests/Feature/Console/Tenant/ListSettingsTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Tenant/ListSettingsTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Tests\Feature\Console\Tenant;
+
+use App\TenantSetting;
+use Tests\TestCase;
+
+class ListSettingsTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ TenantSetting::truncate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ TenantSetting::truncate();
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command runs
+ */
+ public function testHandle(): void
+ {
+ $code = \Artisan::call("tenant:list-settings unknown");
+ $output = trim(\Artisan::output());
+ $this->assertSame(1, $code);
+ $this->assertSame("Unable to find the tenant.", $output);
+
+ $tenant = \App\Tenant::whereNotIn('id', [1])->first();
+
+ // A tenant without settings
+ $code = \Artisan::call("tenant:list-settings {$tenant->id}");
+ $output = trim(\Artisan::output());
+ $this->assertSame(0, $code);
+ $this->assertSame("", $output);
+
+ $tenant->setSetting('app.test1', 'test1');
+ $tenant->setSetting('app.test2', 'test2');
+
+ // A tenant with some settings
+ $expected = "app.test1: test1\napp.test2: test2";
+
+ $code = \Artisan::call("tenant:list-settings {$tenant->id}");
+ $output = trim(\Artisan::output());
+ $this->assertSame(0, $code);
+ $this->assertSame($expected, $output);
+ }
+}
diff --git a/src/tests/Feature/Jobs/PasswordResetEmailTest.php b/src/tests/Feature/Jobs/PasswordResetEmailTest.php
--- a/src/tests/Feature/Jobs/PasswordResetEmailTest.php
+++ b/src/tests/Feature/Jobs/PasswordResetEmailTest.php
@@ -67,5 +67,11 @@
Mail::assertSent(PasswordReset::class, function ($mail) use ($code) {
return $mail->hasTo($code->user->getSetting('external_email'));
});
+
+ // Assert sender
+ Mail::assertSent(PasswordReset::class, function ($mail) {
+ return $mail->hasFrom(\config('mail.from.address'), \config('mail.from.name'))
+ && $mail->hasReplyTo(\config('mail.reply_to.address'), \config('mail.reply_to.name'));
+ });
}
}
diff --git a/src/tests/Feature/TenantTest.php b/src/tests/Feature/TenantTest.php
--- a/src/tests/Feature/TenantTest.php
+++ b/src/tests/Feature/TenantTest.php
@@ -3,6 +3,7 @@
namespace Tests\Feature;
use App\Tenant;
+use App\TenantSetting;
use Tests\TestCase;
class TenantTest extends TestCase
@@ -14,6 +15,8 @@
public function setUp(): void
{
parent::setUp();
+
+ TenantSetting::truncate();
}
/**
@@ -21,9 +24,31 @@
*/
public function tearDown(): void
{
+ TenantSetting::truncate();
+
parent::tearDown();
}
+ /**
+ * Test Tenant::getConfig() method
+ */
+ public function testGetConfig(): void
+ {
+ // No tenant id specified
+ $this->assertSame(\config('app.name'), Tenant::getConfig(null, 'app.name'));
+ $this->assertSame(\config('app.env'), Tenant::getConfig(null, 'app.env'));
+ $this->assertSame(null, Tenant::getConfig(null, 'app.unknown'));
+
+ $tenant = Tenant::whereNotIn('id', [1])->first();
+ $tenant->setSetting('app.test', 'test');
+
+ // Tenant specified
+ $this->assertSame($tenant->title, Tenant::getConfig($tenant->id, 'app.name'));
+ $this->assertSame('test', Tenant::getConfig($tenant->id, 'app.test'));
+ $this->assertSame(\config('app.env'), Tenant::getConfig($tenant->id, 'app.env'));
+ $this->assertSame(null, Tenant::getConfig($tenant->id, 'app.unknown'));
+ }
+
/**
* Test Tenant::wallet() method
*/
diff --git a/src/tests/Feature/UserTest.php b/src/tests/Feature/UserTest.php
--- a/src/tests/Feature/UserTest.php
+++ b/src/tests/Feature/UserTest.php
@@ -776,7 +776,7 @@
}
/**
- * Tests for UserSettingsTrait::setSettings() and getSetting()
+ * Tests for UserSettingsTrait::setSettings() and getSetting() and getSettings()
*/
public function testUserSettings(): void
{
@@ -858,6 +858,16 @@
$all_settings = $user->settings()->orderBy('key')->get();
$this->assertCount(3, $all_settings);
+
+ // Test getSettings() method
+ $this->assertSame(
+ [
+ 'first_name' => 'Firstname2',
+ 'last_name' => 'Lastname2',
+ 'unknown' => null,
+ ],
+ $user->getSettings(['first_name', 'last_name', 'unknown'])
+ );
}
/**
diff --git a/src/tests/MailInterceptTrait.php b/src/tests/MailInterceptTrait.php
--- a/src/tests/MailInterceptTrait.php
+++ b/src/tests/MailInterceptTrait.php
@@ -26,7 +26,7 @@
Mail::send($mail);
- $message = $this->interceptedMail()->first();
+ $message = $this->interceptedMail()->last();
// SwiftMailer does not have methods to get the bodies, we'll parse the message
list($plain, $html) = $this->extractMailBody($message->toString());
diff --git a/src/tests/Unit/Mail/HelperTest.php b/src/tests/Unit/Mail/HelperTest.php
--- a/src/tests/Unit/Mail/HelperTest.php
+++ b/src/tests/Unit/Mail/HelperTest.php
@@ -3,6 +3,7 @@
namespace Tests\Unit\Mail;
use App\Mail\Helper;
+use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class HelperTest extends TestCase
@@ -13,7 +14,9 @@
public function setUp(): void
{
parent::setUp();
+
$this->deleteTestUser('mail-helper-test@kolabnow.com');
+ \App\TenantSetting::truncate();
}
/**
@@ -22,9 +25,74 @@
public function tearDown(): void
{
$this->deleteTestUser('mail-helper-test@kolabnow.com');
+ \App\TenantSetting::truncate();
+
parent::tearDown();
}
+ /**
+ * Test Helper::sendMail()
+ */
+ public function testSendMail(): void
+ {
+ Mail::fake();
+
+ $tenant = \App\Tenant::whereNotIn('id', [1])->first();
+ $invitation = new \App\SignupInvitation();
+ $invitation->id = 'test';
+ $mail = new \App\Mail\SignupInvitation($invitation);
+
+ Helper::sendMail($mail, null, ['to' => 'to@test.com', 'cc' => 'cc@test.com']);
+
+ Mail::assertSent(\App\Mail\SignupInvitation::class, 1);
+ Mail::assertSent(\App\Mail\SignupInvitation::class, function ($mail) {
+ return $mail->hasTo('to@test.com')
+ && $mail->hasCc('cc@test.com')
+ && $mail->hasFrom(\config('mail.from.address'), \config('mail.from.name'))
+ && $mail->hasReplyTo(\config('mail.reply_to.address'), \config('mail.reply_to.name'));
+ });
+
+ // Test with a tenant (but no per-tenant settings)
+ Mail::fake();
+
+ $invitation->tenant_id = $tenant->id;
+ $mail = new \App\Mail\SignupInvitation($invitation);
+
+ Helper::sendMail($mail, $tenant->id, ['to' => 'to@test.com', 'cc' => 'cc@test.com']);
+
+ Mail::assertSent(\App\Mail\SignupInvitation::class, 1);
+ Mail::assertSent(\App\Mail\SignupInvitation::class, function ($mail) {
+ return $mail->hasTo('to@test.com')
+ && $mail->hasCc('cc@test.com')
+ && $mail->hasFrom(\config('mail.from.address'), \config('mail.from.name'))
+ && $mail->hasReplyTo(\config('mail.reply_to.address'), \config('mail.reply_to.name'));
+ });
+
+ // Test with a tenant (but with per-tenant settings)
+ Mail::fake();
+
+ $tenant->setSettings([
+ 'mail.from.address' => 'from@test.com',
+ 'mail.from.name' => 'from name',
+ 'mail.reply_to.address' => 'replyto@test.com',
+ 'mail.reply_to.name' => 'replyto name',
+ ]);
+
+ $mail = new \App\Mail\SignupInvitation($invitation);
+
+ Helper::sendMail($mail, $tenant->id, ['to' => 'to@test.com']);
+
+ Mail::assertSent(\App\Mail\SignupInvitation::class, 1);
+ Mail::assertSent(\App\Mail\SignupInvitation::class, function ($mail) {
+ return $mail->hasTo('to@test.com')
+ && $mail->hasFrom('from@test.com', 'from name')
+ && $mail->hasReplyTo('replyto@test.com', 'replyto name');
+ });
+
+ // TODO: Test somehow log entries, maybe with timacdonald/log-fake package
+ // TODO: Test somehow exception case
+ }
+
/**
* Test Helper::userEmails()
*/
@@ -56,7 +124,7 @@
$this->assertSame([], $cc);
// User with mailbox and external email
- $sku = \App\Sku::where('title', 'mailbox')->first();
+ $sku = \App\Sku::withEnvTenantContext()->where('title', 'mailbox')->first();
$user->assignSku($sku);
list($to, $cc) = Helper::userEmails($user);
diff --git a/src/tests/Unit/Mail/NegativeBalanceBeforeDeleteTest.php b/src/tests/Unit/Mail/NegativeBalanceBeforeDeleteTest.php
--- a/src/tests/Unit/Mail/NegativeBalanceBeforeDeleteTest.php
+++ b/src/tests/Unit/Mail/NegativeBalanceBeforeDeleteTest.php
@@ -13,6 +13,26 @@
{
use MailInterceptTrait;
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ \App\TenantSetting::truncate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ \App\TenantSetting::truncate();
+
+ parent::tearDown();
+ }
+
/**
* Test email content
*/
@@ -58,5 +78,48 @@
$this->assertTrue(strpos($plain, $threshold->toDateString()) > 0);
$this->assertTrue(strpos($plain, "$appName Support") > 0);
$this->assertTrue(strpos($plain, "$appName Team") > 0);
+
+ // Test with user that is not the same tenant as in .env
+ $user = $this->getTestUser('user@sample-tenant.dev-local');
+ $tenant = $user->tenant;
+ $wallet = $user->wallets->first();
+ $wallet->balance = -100;
+ $wallet->save();
+
+ $threshold = WalletCheck::threshold($wallet, WalletCheck::THRESHOLD_DELETE);
+
+ $tenant->setSettings([
+ 'app.support_url' => 'https://test.org/support',
+ 'app.public_url' => 'https://test.org',
+ ]);
+
+ $mail = $this->fakeMail(new NegativeBalanceBeforeDelete($wallet, $user));
+
+ $html = $mail['html'];
+ $plain = $mail['plain'];
+
+ $walletUrl = 'https://test.org/wallet';
+ $walletLink = sprintf('<a href="%s">%s</a>', $walletUrl, $walletUrl);
+ $supportUrl = 'https://test.org/support';
+ $supportLink = sprintf('<a href="%s">%s</a>', $supportUrl, $supportUrl);
+
+ $this->assertMailSubject("{$tenant->title} Final Warning", $mail['message']);
+
+ $this->assertStringStartsWith('<!DOCTYPE html>', $html);
+ $this->assertTrue(strpos($html, $user->name(true)) > 0);
+ $this->assertTrue(strpos($html, $walletLink) > 0);
+ $this->assertTrue(strpos($html, $supportLink) > 0);
+ $this->assertTrue(strpos($html, "This is a final reminder to settle your {$tenant->title}") > 0);
+ $this->assertTrue(strpos($html, $threshold->toDateString()) > 0);
+ $this->assertTrue(strpos($html, "{$tenant->title} Support") > 0);
+ $this->assertTrue(strpos($html, "{$tenant->title} Team") > 0);
+
+ $this->assertStringStartsWith('Dear ' . $user->name(true), $plain);
+ $this->assertTrue(strpos($plain, $walletUrl) > 0);
+ $this->assertTrue(strpos($plain, $supportUrl) > 0);
+ $this->assertTrue(strpos($plain, "This is a final reminder to settle your {$tenant->title}") > 0);
+ $this->assertTrue(strpos($plain, $threshold->toDateString()) > 0);
+ $this->assertTrue(strpos($plain, "{$tenant->title} Support") > 0);
+ $this->assertTrue(strpos($plain, "{$tenant->title} Team") > 0);
}
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 3, 4:34 PM (21 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18823982
Default Alt Text
D2659.1775234089.diff (63 KB)
Attached To
Mode
D2659: Tenant settings
Attached
Detach File
Event Timeline