Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2527564
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/app/Tenant.php b/src/app/Tenant.php
index 6a05c730..197c5daa 100644
--- a/src/app/Tenant.php
+++ b/src/app/Tenant.php
@@ -1,95 +1,94 @@
<?php
namespace App;
use App\Traits\SettingsTrait;
use Illuminate\Database\Eloquent\Model;
/**
* The eloquent definition of a Tenant.
*
* @property int $id
* @property string $title
*/
class Tenant extends Model
{
use SettingsTrait;
protected $fillable = [
'id',
'title',
];
- 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
// - pgp.enable
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
*/
public function discounts()
{
return $this->hasMany('App\Discount');
}
/**
* SignupInvitations assigned to this tenant.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function signupInvitations()
{
return $this->hasMany('App\SignupInvitation');
}
/*
* Returns the wallet of the tanant (reseller's wallet).
*
* @return ?\App\Wallet A wallet object
*/
public function wallet(): ?Wallet
{
$user = \App\User::where('role', 'reseller')->where('tenant_id', $this->id)->first();
return $user ? $user->wallets->first() : null;
}
}
diff --git a/src/app/Traits/UuidIntKeyTrait.php b/src/app/Traits/UuidIntKeyTrait.php
index 0bf0811d..df6c562d 100644
--- a/src/app/Traits/UuidIntKeyTrait.php
+++ b/src/app/Traits/UuidIntKeyTrait.php
@@ -1,51 +1,41 @@
<?php
namespace App\Traits;
trait UuidIntKeyTrait
{
/**
* Boot function from Laravel.
*/
protected static function bootUuidIntKeyTrait()
{
static::creating(function ($model) {
if (empty($model->{$model->getKeyName()})) {
$allegedly_unique = \App\Utils::uuidInt();
// Verify if unique
if (in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses($model))) {
while ($model->withTrashed()->find($allegedly_unique)) {
$allegedly_unique = \App\Utils::uuidInt();
}
} else {
while ($model->find($allegedly_unique)) {
$allegedly_unique = \App\Utils::uuidInt();
}
}
$model->{$model->getKeyName()} = $allegedly_unique;
}
});
}
/**
* Get if the key is incrementing.
*
* @return bool
*/
public function getIncrementing()
{
return false;
}
-
- /**
- * Get the key type.
- *
- * @return string
- */
- public function getKeyType()
- {
- return 'bigint';
- }
}
diff --git a/src/resources/js/locale.js b/src/resources/js/locale.js
index afe2b18e..fedff4c9 100644
--- a/src/resources/js/locale.js
+++ b/src/resources/js/locale.js
@@ -1,66 +1,66 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
// We do pre-load English localization as this is possible
// the only one that is complete and used as a fallback.
import messages from '../build/js/en.json'
Vue.use(VueI18n)
export const i18n = new VueI18n({
locale: 'en',
fallbackLocale: 'en',
messages: { en: messages },
silentFallbackWarn: true
})
let currentLanguage
const loadedLanguages = ['en'] // our default language that is preloaded
const setI18nLanguage = (lang) => {
i18n.locale = lang
document.querySelector('html').setAttribute('lang', lang)
// Set language for API requests
// Note, it's kinda redundant as we support the cookie
window.axios.defaults.headers.common['Accept-Language'] = lang
// Save the selected language in a cookie, so it can be used server-side
// after page reload. Make the cookie valid for 10 years
const age = 10 * 60 * 60 * 24 * 365
- document.cookie = 'language=' + lang + '; max-age=' + age
+ document.cookie = 'language=' + lang + '; max-age=' + age + '; path=/; secure'
return lang
}
export const getLang = () => {
if (!currentLanguage) {
currentLanguage = document.querySelector('html').getAttribute('lang') || 'en'
}
return currentLanguage
}
export const setLang = lang => {
currentLanguage = lang
loadLangAsync()
}
export function loadLangAsync() {
const lang = getLang()
// If the language was already loaded
if (loadedLanguages.includes(lang)) {
return Promise.resolve(setI18nLanguage(lang))
}
// If the language hasn't been loaded yet
return import(/* webpackChunkName: "locale/[request]" */ `../build/js/${lang}.json`)
.then(messages => {
i18n.setLocaleMessage(lang, messages.default)
loadedLanguages.push(lang)
return setI18nLanguage(lang)
})
}
diff --git a/src/tests/Feature/Console/Wallet/ChargeTest.php b/src/tests/Feature/Console/Wallet/ChargeTest.php
index 628114eb..28991135 100644
--- a/src/tests/Feature/Console/Wallet/ChargeTest.php
+++ b/src/tests/Feature/Console/Wallet/ChargeTest.php
@@ -1,147 +1,147 @@
<?php
namespace Tests\Feature\Console\Wallet;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
class ChargeTest extends TestCase
{
/**
* {@inheritDoc}
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('wallet-charge@kolabnow.com');
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$this->deleteTestUser('wallet-charge@kolabnow.com');
parent::tearDown();
}
/**
* Test command run for a specified wallet
*/
public function testHandleSingle(): void
{
$user = $this->getTestUser('wallet-charge@kolabnow.com');
$wallet = $user->wallets()->first();
$wallet->balance = 0;
$wallet->save();
Queue::fake();
// Non-existing wallet ID
$this->artisan('wallet:charge 123')
->assertExitCode(1)
->expectsOutput("Wallet not found.");
Queue::assertNothingPushed();
// The wallet has no entitlements, expect no charge and no check
$this->artisan('wallet:charge ' . $wallet->id)
->assertExitCode(0);
Queue::assertNothingPushed();
// The wallet has no entitlements, but has negative balance
$wallet->balance = -100;
$wallet->save();
$this->artisan('wallet:charge ' . $wallet->id)
->assertExitCode(0);
Queue::assertPushed(\App\Jobs\WalletCharge::class, 0);
Queue::assertPushed(\App\Jobs\WalletCheck::class, 1);
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
Queue::fake();
// The wallet has entitlements to charge, and negative balance
- $sku = \App\Sku::where('title', 'mailbox')->first();
+ $sku = \App\Sku::withObjectTenantContext($user)->where('title', 'mailbox')->first();
$entitlement = \App\Entitlement::create([
'wallet_id' => $wallet->id,
'sku_id' => $sku->id,
'cost' => 100,
'entitleable_id' => $user->id,
'entitleable_type' => \App\User::class,
]);
\App\Entitlement::where('id', $entitlement->id)->update([
- 'created_at' => \Carbon\Carbon::now()->subMonths(1),
- 'updated_at' => \Carbon\Carbon::now()->subMonths(1),
+ 'created_at' => \Carbon\Carbon::now()->subMonthsNoOverflow(1),
+ 'updated_at' => \Carbon\Carbon::now()->subMonthsNoOverflow(1),
]);
\App\User::where('id', $user->id)->update([
- 'created_at' => \Carbon\Carbon::now()->subMonths(1),
- 'updated_at' => \Carbon\Carbon::now()->subMonths(1),
+ 'created_at' => \Carbon\Carbon::now()->subMonthsNoOverflow(1),
+ 'updated_at' => \Carbon\Carbon::now()->subMonthsNoOverflow(1),
]);
$this->assertSame(100, $wallet->fresh()->chargeEntitlements(false));
$this->artisan('wallet:charge ' . $wallet->id)
->assertExitCode(0);
Queue::assertPushed(\App\Jobs\WalletCharge::class, 1);
Queue::assertPushed(\App\Jobs\WalletCharge::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
Queue::assertPushed(\App\Jobs\WalletCheck::class, 1);
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
}
/**
* Test command run for all wallets
*/
public function testHandleAll(): void
{
$user = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
$wallet->balance = 0;
$wallet->save();
// backdate john's entitlements and set balance=0 for all wallets
$this->backdateEntitlements($user->entitlements, \Carbon\Carbon::now()->subWeeks(5));
\App\Wallet::where('balance', '<', '0')->update(['balance' => 0]);
$user2 = $this->getTestUser('wallet-charge@kolabnow.com');
$wallet2 = $user2->wallets()->first();
$wallet2->balance = -100;
$wallet2->save();
Queue::fake();
// Non-existing wallet ID
$this->artisan('wallet:charge')->assertExitCode(0);
Queue::assertPushed(\App\Jobs\WalletCheck::class, 2);
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet2) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet2->id;
});
Queue::assertPushed(\App\Jobs\WalletCharge::class, 1);
Queue::assertPushed(\App\Jobs\WalletCharge::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jan 31, 1:10 AM (3 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426102
Default Alt Text
(11 KB)
Attached To
Mode
R2 kolab
Attached
Detach File
Event Timeline
Log In to Comment