Compare commits

...

17 Commits

Author SHA1 Message Date
Čarodej
b8e97b62c1 added index and button into DunningEmailToCoverAccountUsageNotification 2022-06-30 10:49:18 +02:00
Čarodej
7b4cb694f3 return null in default in restriction engine 2022-06-28 16:33:47 +02:00
Čarodej
8bc046fc73 build 2022-06-28 08:25:39 +02:00
Čarodej
ec5ed24eaa language strings update 2022-06-28 08:23:41 +02:00
Čarodej
aaea435eb0 restriction text implementation for the frontend 2022-06-14 10:31:55 +02:00
Čarodej
ebf1b16aa5 MeteredBillingRestrictionsEngine update with the new rule for dunning 2022-06-12 14:53:58 +02:00
Čarodej
119780317d MeteredBillingRestrictionsEngine update with the new rule for dunning 2022-06-10 11:19:45 +02:00
Čarodej
9bc8a9b27c dunning implementation of the notifications 2022-06-09 18:08:49 +02:00
Čarodej
f3bfe9a0cc dump fix 2022-06-08 08:53:59 +02:00
Čarodej
8a943747eb subscription config 2022-06-06 18:31:03 +02:00
Čarodej
59e3f55ac3 Added Fraud Prevention Mechanism Rules form 2022-06-06 10:00:44 +02:00
Čarodej
16db8d3512 setup wizard fix 2022-06-06 07:57:37 +02:00
Čarodej
d521b237a3 app title fix 2022-06-02 08:40:56 +02:00
Čarodej
3077ec18d1 version change 2022-06-02 07:38:29 +02:00
Čarodej
8b5b9c3310 color fix 2022-06-02 07:38:03 +02:00
Čarodej
c4d7fa1e9b composer update 2022-06-01 20:01:05 +02:00
Čarodej
96afda9f7c back to php 8.0 2022-06-01 19:47:27 +02:00
32 changed files with 495 additions and 68 deletions

View File

@@ -1,6 +1,6 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:DZMmPFCbaykO5HNb9ltj1PKX/WKGylZf0s/ghUWUCGs=
APP_KEY=base64:9wVs/t3WU5njVGevkgDgGY8aKKkzsxkwykf3Pr0aQHc=
APP_DEBUG=true
APP_URL=http://localhost
APP_DEMO=false

View File

@@ -108,7 +108,7 @@ return [
'want_to_delete_card_description' => 'We will no longer settle your payments automatically and you will have to fund your account for the next payments.',
'credit_card_deleted' => 'Your credit card was deleted.',
'billed_annually' => 'Billed Annually',
'restricted_account_warning' => 'Your functionality is restricted. Please review your billing settings.',
'restricted_account_warning' => 'Your functionality is restricted.',
'subscription_type' => 'Subscription Type',
'subscription_type_note' => 'Please do not change in production environment.',
'select_subscription_type' => 'Select your subscription type',
@@ -267,6 +267,22 @@ return [
'synchronizing_plans' => 'Synchronizing Plans...',
'plans_are_synchronizing' => 'Your plans are synchronizing with the payment gateways',
'plans_was_synchronized' => 'Plans was successfully synchronized',
'limit_usage_in_new_accounts_1_subject' => 'Please make first payment for your account to fund your usage',
'limit_usage_in_new_accounts_1_line' => 'We are happy you are using our service. To continue to using our service, please make first payment for your account balance to fund your usage.',
'limit_usage_in_new_accounts_2_subject' => '📆 Reminder: Please make first payment for your account to fund your usage',
'limit_usage_in_new_accounts_2_line' => 'We are happy you are using our service. To continue to using our service, please make first payment for your account balance to fund your usage.',
'limit_usage_in_new_accounts_3_subject' => '‼️ Uh-oh! Your functionality was restricted. Please make payment to continue using your account',
'limit_usage_in_new_accounts_3_line' => 'We are sorry for the inconvenience with using our service. To continue to using our service, please make first payment for your account balance to fund your usage and your functionality will be allowed as soon as possible.',
'usage_bigger_than_balance_1_subject' => "⚠️ You don't have sufficient funds in your account, please increase your account balance",
'usage_bigger_than_balance_1_line' => 'We are happy you are using our service. To continue to using our service, please increase your funds for your account balance to cover your usage.',
'usage_bigger_than_balance_2_subject' => "📆 Reminder: You don't have sufficient funds in your account, please increase your account balance",
'usage_bigger_than_balance_2_line' => 'We are happy you are using our service. To continue to using our service, please increase your funds for your account balance to cover your usage.',
'usage_bigger_than_balance_3_subject' => '‼️ Uh-oh! Your functionality was restricted. Please increase your funds for your account balance to cover your usage',
'usage_bigger_than_balance_3_line' => 'We are sorry for the inconvenience with using our service. To continue to using our service, please increase your funds for your account balance to cover your usage and your functionality will be allowed as soon as possible.',
'dunning_notification_description' => 'Please resolve your billing as soon as possible. Your functions can be restricted.',
'allow_limit_usage_in_new_accounts' => 'Allow limiting max usage before users will be forced to increase balance in first month of account existence',
'allow_limit_usage_bigger_than_balance' => 'Force users to increase balance when usage is bigger than their current balance',
'limit_usage_description_for_restrictions' => 'If user does not increase his balance or store his credit card, after third notification user account functionality will be restricted.',
],
'regular' => [
'type' => 'Type',

View File

@@ -23,6 +23,7 @@ return [
],
'notifications' => [
'DunningEmailToCoverAccountUsageNotification' => \Domain\Subscriptions\Notifications\DunningEmailToCoverAccountUsageNotification::class,
'ChargeFromCreditCardFailedAgainNotification' => \Domain\Subscriptions\Notifications\ChargeFromCreditCardFailedAgainNotification::class,
'ChargeFromCreditCardFailedNotification' => \Domain\Subscriptions\Notifications\ChargeFromCreditCardFailedNotification::class,
'SubscriptionWasCreatedNotification' => \Domain\Subscriptions\Notifications\SubscriptionWasCreatedNotification::class,
@@ -32,8 +33,18 @@ return [
'BonusCreditAddedNotification' => \Domain\Subscriptions\Notifications\BonusCreditAddedNotification::class,
],
'metered_billing' => [
'metered_billing' => [
'settlement_period' => 30,
'fraud_prevention_mechanism' => [
'usage_bigger_than_balance' => [
'active' => true,
],
'limit_usage_in_new_accounts' => [
'active' => true,
'amount' => 5,
],
],
],
'paystack' => [
@@ -48,4 +59,5 @@ return [
],
'is_demo' => env('APP_DEMO', false),
'is_local' => env('APP_ENV', 'production') === 'local',
];

View File

@@ -1,7 +1,7 @@
<?php
return [
'version' => '2.2',
'version' => '2.2.0.3',
'is_demo' => env('APP_DEMO', false),

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/main.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
"/chunks/request.js": "/chunks/request.js?id=386fae37c11630a4",
"/chunks/request-upload.js": "/chunks/request-upload.js?id=b631a474ab4b733b",
"/chunks/setup-wizard.js": "/chunks/setup-wizard.js?id=6122cca2fca4f02b",
"/chunks/status-check.js": "/chunks/status-check.js?id=792733921b00a3a2",
"/chunks/status-check.js": "/chunks/status-check.js?id=8ce1ff7720194e7e",
"/chunks/purchase-code.js": "/chunks/purchase-code.js?id=ce78be58a4b9683b",
"/chunks/database.js": "/chunks/database.js?id=0fb6901a348563e4",
"/chunks/environment.js": "/chunks/environment.js?id=99910849f57466b0",
@@ -15,7 +15,7 @@
"/chunks/shared/authenticate.js": "/chunks/shared/authenticate.js?id=4d66b8a277a638a7",
"/chunks/not-found.js": "/chunks/not-found.js?id=901e26fdc77de6e0",
"/chunks/temporary-unavailable.js": "/chunks/temporary-unavailable.js?id=c3a4f158b971145f",
"/chunks/admin.js": "/chunks/admin.js?id=87bda46c27a6dde9",
"/chunks/admin.js": "/chunks/admin.js?id=5d611417aa87f58f",
"/chunks/dashboard.js": "/chunks/dashboard.js?id=849bbd9454296cc0",
"/chunks/invoices.js": "/chunks/invoices.js?id=c40b8b7925003659",
"/chunks/subscriptions.js": "/chunks/subscriptions.js?id=cdca1b82ffe52ff5",
@@ -38,7 +38,7 @@
"/chunks/plan-delete.js": "/chunks/plan-delete.js?id=b552da027e7ccdc8",
"/chunks/payments.js": "/chunks/payments.js?id=22e84a36acc89129",
"/chunks/payments/billings.js": "/chunks/payments/billings.js?id=8cf2287d221825c1",
"/chunks/payments/settings.js": "/chunks/payments/settings.js?id=313058a869f63cdc",
"/chunks/payments/settings.js": "/chunks/payments/settings.js?id=cebdae0e4d02d3d1",
"/chunks/app-settings.js": "/chunks/app-settings.js?id=5de958be12ca920f",
"/chunks/app-appearance.js": "/chunks/app-appearance.js?id=bc104e3407af9abb",
"/chunks/app-index.js": "/chunks/app-index.js?id=96b505173e1be922",
@@ -58,12 +58,12 @@
"/chunks/sign-up.js": "/chunks/sign-up.js?id=caeb50ac27194d33",
"/chunks/forgotten-password.js": "/chunks/forgotten-password.js?id=b06174390d32669c",
"/chunks/create-new-password.js": "/chunks/create-new-password.js?id=3fe56e872c74d485",
"/chunks/settings.js": "/chunks/settings.js?id=b11db80b0d994f04",
"/chunks/settings.js": "/chunks/settings.js?id=cb4fc111071ec323",
"/chunks/profile.js": "/chunks/profile.js?id=87ac69edc17d9245",
"/chunks/settings-password.js": "/chunks/settings-password.js?id=99e9984bfcd5289b",
"/chunks/settings-storage.js": "/chunks/settings-storage.js?id=a9e2543c5362e459",
"/chunks/billing.js": "/chunks/billing.js?id=c4160a2491437905",
"/chunks/platform.js": "/chunks/platform.js?id=66a55d4175e81c6b",
"/chunks/platform.js": "/chunks/platform.js?id=62d94e384c593749",
"/chunks/files.js": "/chunks/files.js?id=5251b1cdad4ad5ad",
"/chunks/recent-uploads.js": "/chunks/recent-uploads.js?id=b1f5ab7af810aa91",
"/chunks/my-shared-items.js": "/chunks/my-shared-items.js?id=44dd934482c86e2e",

View File

@@ -19,7 +19,7 @@
class="vue-feather text-theme shrink-0"
/>
<alert-triangle-icon
v-if="['billing-alert', 'insufficient-balance'].includes(notification.data.attributes.category)"
v-if="['billing-alert', 'insufficient-balance', 'payment-alert'].includes(notification.data.attributes.category)"
size="22"
class="vue-feather text-theme shrink-0"
/>

View File

@@ -1,7 +1,7 @@
<template>
<div v-if="$store.getters.isLimitedUser" class="bg-red-500 py-1 text-center">
<div v-if="$store.getters.userLimitationReason" class="bg-gradient-to-r from-red-600 to-red-500 py-2.5 px-1 text-center leading-none">
<router-link :to="{ name: 'Billing' }" class="text-xs font-bold text-white">
{{ $t('restricted_account_warning') }}
{{ $t('restricted_account_warning') + ' ' + $store.getters.userLimitationReason }}
</router-link>
</div>
</template>

View File

@@ -201,10 +201,7 @@ const mutations = {
}
const getters = {
isLimitedUser: (state) =>
state.user &&
state.user.data.relationships.failedPayments &&
state.user.data.relationships.failedPayments.data.length === 3,
userLimitationReason: (state) => state.user && state.user.data.meta.restrictions.reason,
permission: (state) => state.permission,
user: (state) => state.user,
}

View File

@@ -75,6 +75,49 @@
</AppInputButton>
</div>
<!--Fraud Prevention Mechanism Rules-->
<div v-if="config.subscriptionType === 'metered' && allowedPayments" class="card shadow-card">
<FormLabel icon="shield">
{{ $t('Fraud Prevention Mechanism Rules') }}
</FormLabel>
<AppInputSwitch
:title="$t('allow_limit_usage_in_new_accounts')"
:description="$t('limit_usage_description_for_restrictions')"
>
<SwitchInput
@input="$updateText('/admin/settings', 'limit_usage_in_new_accounts', settings.limitUsageInNewAccounts)"
v-model="settings.limitUsageInNewAccounts"
:state="settings.limitUsageInNewAccounts"
/>
</AppInputSwitch>
<AppInputText
v-if="settings.limitUsageInNewAccounts"
class="-mt-3"
>
<input
@input="$updateText('/admin/settings', 'limit_usage_in_new_accounts_amount', settings.limitUsageInNewAccountsAmount)"
v-model="settings.limitUsageInNewAccountsAmount"
:placeholder="$t('Max Usage Amount...')"
type="number"
class="focus-border-theme input-dark"
/>
</AppInputText>
<AppInputSwitch
:title="$t('allow_limit_usage_bigger_than_balance')"
:description="$t('limit_usage_description_for_restrictions')"
:is-last="true"
>
<SwitchInput
@input="$updateText('/admin/settings', 'usage_bigger_than_balance', settings.usageBiggerThanBalance)"
v-model="settings.usageBiggerThanBalance"
:state="settings.usageBiggerThanBalance"
/>
</AppInputSwitch>
</div>
<!--Stripe method configuration-->
<div v-if="allowedPayments" class="card shadow-card">
<img :src="$getPaymentLogo('stripe')" alt="Stripe" class="mb-8 h-8" />
@@ -508,10 +551,13 @@ export default {
InfoBox,
},
computed: {
...mapGetters(['config']),
...mapGetters([
'config'
]),
},
data() {
return {
settings: undefined,
allowedRegistrationBonus: true,
registrationBonusAmount: undefined,
@@ -651,6 +697,23 @@ export default {
})
}
},
mounted() {
axios
.get('/api/admin/settings', {
params: {
column: 'limit_usage_in_new_accounts|limit_usage_in_new_accounts_amount|usage_bigger_than_balance',
},
})
.then((response) => {
this.isLoading = false
this.settings = {
limitUsageInNewAccounts: parseInt(response.data.limit_usage_in_new_accounts),
limitUsageInNewAccountsAmount: parseInt(response.data.limit_usage_in_new_accounts_amount),
usageBiggerThanBalance: parseInt(response.data.usage_bigger_than_balance),
}
})
},
created() {
// Set payment description
this.stripe.paymentDescription = this.config.stripe_payment_description

View File

@@ -110,7 +110,7 @@ export default {
DragUI,
},
computed: {
...mapGetters(['isVisibleSidebar', 'isLimitedUser', 'config', 'currentFolder']),
...mapGetters(['isVisibleSidebar', 'config', 'currentFolder']),
},
data() {
return {

View File

@@ -256,7 +256,7 @@ export default {
axios
.get('/api/ping')
.then((response) => {
this.apiRunning = response.data === 'pong'
this.apiRunning = response.data.message === 'pong'
})
.catch(() => {
this.apiRunning = false

View File

@@ -9,7 +9,7 @@
<meta name="description" content="{{ $config->app->description ?? __t('app_description') }}">
<title>
{{ $config->app->title ?? 'VueFileManager' }} | {{ $config->app->description ?? __t('app_description') }}
{{ $config->app->name ?? 'VueFileManager' }} | {{ $config->app->description ?? __t('app_description') }}
</title>
{{--StyleSheet--}}
@@ -18,14 +18,14 @@
{{--OG items--}}
<meta property="og:url" content="{{ url('/') }}">
<meta property="og:title" content="{{ $config->app->title ?? 'VueFileManager' }} | {{ $config->app->description ?? __t('app_description') }}">
<meta property="og:title" content="{{ $config->app->name ?? 'VueFileManager' }} | {{ $config->app->description ?? __t('app_description') }}">
<meta property="og:description" content="{{ $config->app->description ?? __t('app_description') }}">
<meta property="og:image" content="{{ isset($config->logos->og_image) ? url($config->logos->og_image) : '' }}">
{{-- Apple Mobile Web App--}}
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="white">
<meta name="apple-mobile-web-app-title" content="{{ $config->app->title ?? 'VueFileManager' }}">
<meta name="apple-mobile-web-app-title" content="{{ $config->app->name ?? 'VueFileManager' }}">
{{--Icons--}}
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ isset($config->logos->touch_icon) ? url($config->logos->touch_icon) : '' }}">

View File

@@ -1,7 +1,7 @@
<style>
@php
$color = $settings->app_color ?? '#00BC7E';
$color = $config->theme->color ?? '#00BC7E';
@endphp
{{-- Tailwind Helpers --}}

View File

@@ -24,6 +24,49 @@ class AppServiceProvider extends ServiceProvider
{
Schema::defaultStringLength(191);
// Set subscription config
$this->setSubscriptionConfig();
// Set app locale
$this->setLocale();
// Get all migrations with all directories
$this->setMigrations();
}
private function setMigrations(): void
{
$mainPath = database_path('migrations');
$directories = glob($mainPath . '/*', GLOB_ONLYDIR);
$this->loadMigrationsFrom(
array_merge([$mainPath], $directories)
);
}
private function setSubscriptionConfig(): void
{
if (app()->runningUnitTests()) {
return;
}
$settings = getAllSettings();
config([
'subscription.metered_billing.fraud_prevention_mechanism' => [
'usage_bigger_than_balance' => [
'active' => isset($settings->usage_bigger_than_balance) ? intval($settings->usage_bigger_than_balance) : true,
],
'limit_usage_in_new_accounts' => [
'active' => isset($settings->limit_usage_in_new_accounts) ? intval($settings->limit_usage_in_new_accounts) : true,
'amount' => isset($settings->limit_usage_in_new_accounts_amount) ? intval($settings->limit_usage_in_new_accounts_amount) : 20,
],
]
]);
}
private function setLocale(): void
{
try {
$app_locale = get_settings('language') ?? 'en';
} catch (\PDOException $e) {
@@ -35,21 +78,5 @@ class AppServiceProvider extends ServiceProvider
// Set locale for carbon dates
setlocale(LC_TIME, $app_locale . '_' . mb_strtoupper($app_locale));
// Get all migrations with all directories
$this->loadMigrationsFrom(
$this->get_migration_paths()
);
}
/**
* @return array
*/
private function get_migration_paths(): array
{
$mainPath = database_path('migrations');
$directories = glob($mainPath . '/*', GLOB_ONLYDIR);
return array_merge([$mainPath], $directories);
}
}

View File

@@ -3,6 +3,7 @@ namespace App\Users\Models;
use ByteUnits\Metric;
use Illuminate\Support\Str;
use BadMethodCallException;
use Domain\Files\Models\File;
use Domain\Folders\Models\Folder;
use Laravel\Sanctum\HasApiTokens;
@@ -196,10 +197,14 @@ class User extends Authenticatable implements MustVerifyEmail
public function __call($method, $parameters)
{
if (str_starts_with($method, 'can')) {
return resolve(RestrictionsManager::class)
->driver()
->$method($this, ...$parameters);
try {
if (str_starts_with($method, 'can') || str_starts_with($method, 'get')) {
return resolve(RestrictionsManager::class)
->driver()
->$method($this, ...$parameters);
}
} catch (BadMethodCallException $e) {
return parent::__call($method, $parameters);
}
return parent::__call($method, $parameters);

View File

@@ -69,6 +69,7 @@ class UserResource extends JsonResource
'canCreateFolder' => $this->canCreateFolder(),
'canCreateTeamFolder' => $this->canCreateTeamFolder(),
'canInviteTeamMembers' => $this->canInviteTeamMembers(),
'reason' => $this->getRestrictionReason(),
],
$this->mergeWhen($isFixedSubscription, fn () => [
'limitations' => $this->limitations->summary(),

View File

@@ -47,4 +47,9 @@ class DefaultRestrictionsEngine implements RestrictionsEngine
{
return true;
}
public function getRestrictionReason(User $user): string|null
{
return null;
}
}

View File

@@ -43,4 +43,9 @@ class FixedBillingRestrictionsEngine implements RestrictionsEngine
{
return true;
}
public function getRestrictionReason(User $user): string|null
{
return null;
}
}

View File

@@ -8,26 +8,46 @@ class MeteredBillingRestrictionsEngine implements RestrictionsEngine
{
public function canUpload(User $user, int $fileSize = 0): bool
{
// Check the count of the dunning emails
if ($this->getDunningSequenceCount($user) === 3) {
return false;
}
// Disable upload when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3);
return $this->checkFailedPayments($user);
}
public function canDownload(User $user): bool
{
// Check the count of the dunning emails
if ($this->getDunningSequenceCount($user) === 3) {
return false;
}
// Disable download when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3);
return $this->checkFailedPayments($user);
}
public function canCreateFolder(User $user): bool
{
// Check the count of the dunning emails
if ($this->getDunningSequenceCount($user) === 3) {
return false;
}
// Disable create folder when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3);
return $this->checkFailedPayments($user);
}
public function canCreateTeamFolder(User $user): bool
{
// Check the count of the dunning emails
if ($this->getDunningSequenceCount($user) === 3) {
return false;
}
// Disable create folder when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3);
return $this->checkFailedPayments($user);
}
public function canInviteTeamMembers(User $user, array $newInvites = []): bool
@@ -37,7 +57,38 @@ class MeteredBillingRestrictionsEngine implements RestrictionsEngine
public function canVisitShared(User $user): bool
{
// Check the count of the dunning emails
if ($this->getDunningSequenceCount($user) === 3) {
return false;
}
// Disable share visit when user has more than 3 failed payments
return ! ($user->failedPayments()->count() >= 3);
return $this->checkFailedPayments($user);
}
public function getRestrictionReason(User $user): string|null
{
if ($this->getDunningSequenceCount($user) === 3) {
return match ($user->dunning->type) {
'limit_usage_in_new_accounts' => 'Please make your first payment to cover your usage.',
'usage_bigger_than_balance' => 'Please increase your account balance higher than your monthly usage.',
};
}
if (! $this->checkFailedPayments($user)) {
return 'Please update your credit card to pay your usage.';
}
return null;
}
private function getDunningSequenceCount(User $user): int
{
return cache()->remember("dunning-count.$user->id", 3600, fn () => $user?->dunning->sequence ?? 0);
}
private function checkFailedPayments(User $user): bool
{
return cache()->remember("failed-payments-count.$user->id", 3600, fn () => !($user->failedPayments()->count() >= 3));
}
}

View File

@@ -16,4 +16,6 @@ interface RestrictionsEngine
public function canInviteTeamMembers(User $user, array $newInvites = []): bool;
public function canVisitShared(User $user): bool;
public function getRestrictionReason(User $user): string|null;
}

View File

@@ -6,18 +6,16 @@ use DB;
use PDOException;
use Domain\Pages\Models\Page;
use Monolog\Handler\MissingExtensionException;
use Support\Status\Actions\GetServerSetupStatusAction;
use VueFileManager\Subscription\Domain\Plans\Models\Plan;
use Domain\Settings\Controllers\GetServerStatusController;
use VueFileManager\Subscription\Domain\Transactions\Models\Transaction;
use VueFileManager\Subscription\Domain\Subscriptions\Models\Subscription;
class GetConfigAction
{
public function __construct(
public GetServerStatusController $getServerSetupStatus,
)
{
}
public GetServerSetupStatusAction $getServerSetupStatus,
) {}
public function __invoke(): array
{

View File

@@ -0,0 +1,89 @@
<?php
namespace Domain\Subscriptions\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use VueFileManager\Subscription\Domain\DunningEmails\Models\Dunning;
class DunningEmailToCoverAccountUsageNotification extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(
private Dunning $dunning
) {
}
public function via(): array
{
return ['mail', 'database', 'broadcast'];
}
public function toMail(): MailMessage
{
$message = $this->dunningMessages();
$index = $this->dunning->sequence - 1;
return (new MailMessage)
->subject($message[$this->dunning->type][$index]['subject'])
->greeting(__('Hi there'))
->line($message[$this->dunning->type][$index]['line'])
->action(__t('show_billing'), url('/user/settings/billing'))
->salutation(__('Regards'));
}
public function toArray(): array
{
$message = $this->dunningMessages();
$index = $this->dunning->sequence - 1;
return [
'category' => 'payment-alert',
'title' => $message[$this->dunning->type][$index]['subject'],
'description' => __t('dunning_notification_description'),
'action' => [
'type' => 'route',
'params' => [
'route' => __t('billing'),
'button' => __t('show_billing'),
],
],
];
}
private function dunningMessages(): array
{
return [
'limit_usage_in_new_accounts' => [
[
'subject' => __t('limit_usage_in_new_accounts_1_subject'),
'line' => __t('limit_usage_in_new_accounts_1_line'),
],
[
'subject' => __t('limit_usage_in_new_accounts_2_subject'),
'line' => __t('limit_usage_in_new_accounts_2_line'),
],
[
'subject' => __t('limit_usage_in_new_accounts_3_subject'),
'line' => __t('limit_usage_in_new_accounts_3_line'),
],
],
'usage_bigger_than_balance' => [
[
'subject' => __t("usage_bigger_than_balance_1_subject"),
'line' => __t('usage_bigger_than_balance_1_line'),
],
[
'subject' => __t('usage_bigger_than_balance_2_subject'),
'line' => __t('usage_bigger_than_balance_2_line'),
],
[
'subject' => __t('usage_bigger_than_balance_3_subject'),
'line' => __t('usage_bigger_than_balance_3_line'),
],
],
];
}
}

View File

@@ -3,14 +3,14 @@ namespace Support\Status\Actions;
class GetServerSetupStatusAction
{
public function __invoke()
public function __invoke(): array
{
// Required parameters
$upload_max_filesize = 128;
$post_max_size = 128;
$memory_limit = 512;
$max_execution_time = 180;
$php_version = '8.0';
$php_version = '8.1';
// Writable
$storageDirectory = dirname(storage_path('/storage'));

View File

@@ -1,4 +1,5 @@
<?php
namespace Tests\App\Restrictions;
use Illuminate\Http\UploadedFile;
@@ -9,6 +10,7 @@ use App\Users\Models\User;
use Domain\Files\Models\File;
use Domain\Sharing\Models\Share;
use Domain\Settings\Models\Setting;
use VueFileManager\Subscription\Domain\DunningEmails\Models\Dunning;
class MeteredBillingRestrictionsTest extends TestCase
{
@@ -17,7 +19,7 @@ class MeteredBillingRestrictionsTest extends TestCase
parent::setUp();
Setting::updateOrCreate([
'name' => 'subscription_type',
'name' => 'subscription_type',
], [
'value' => 'metered',
]);
@@ -32,6 +34,13 @@ class MeteredBillingRestrictionsTest extends TestCase
->hasFailedpayments(2)
->create();
Dunning::factory()
->createOneQuietly([
'type' => 'limit_usage_in_new_accounts',
'user_id' => $user->id,
'sequence' => 2,
]);
$this->assertEquals(true, $user->canUpload());
}
@@ -47,6 +56,24 @@ class MeteredBillingRestrictionsTest extends TestCase
$this->assertEquals(false, $user->canUpload());
}
/**
* @test
*/
public function it_cant_upload_because_user_has_3_dunning_mails()
{
$user = User::factory()
->create();
Dunning::factory()
->createOneQuietly([
'type' => 'limit_usage_in_new_accounts',
'user_id' => $user->id,
'sequence' => 3,
]);
$this->assertEquals(false, $user->canUpload());
}
/**
* @test
*/
@@ -117,6 +144,47 @@ class MeteredBillingRestrictionsTest extends TestCase
$this->assertDatabaseCount('folders', 0);
}
/**
* @test
*/
public function it_cant_create_new_folder_because_user_has_3_dunning_mails()
{
$user = User::factory()
->create();
Dunning::factory()
->createOneQuietly([
'type' => 'limit_usage_in_new_accounts',
'user_id' => $user->id,
'sequence' => 3,
]);
// Create basic folder
$this
->actingAs($user)
->postJson('/api/create-folder', [
'name' => 'New Folder',
])
->assertStatus(401);
// Create team folder
$this
->actingAs($user)
->postJson('/api/teams/folders', [
'name' => 'New Folder',
'invitations' => [
[
'email' => 'john@doe.com',
'permission' => 'can-edit',
'type' => 'invitation',
],
],
])
->assertStatus(401);
$this->assertDatabaseCount('folders', 0);
}
/**
* @test
*/
@@ -139,6 +207,34 @@ class MeteredBillingRestrictionsTest extends TestCase
->assertStatus(401);
}
/**
* @test
*/
public function it_cant_get_private_file_because_user_has_3_dunning_mails()
{
$user = User::factory()
->create();
Dunning::factory()
->createOneQuietly([
'type' => 'limit_usage_in_new_accounts',
'user_id' => $user->id,
'sequence' => 3,
]);
$file = File::factory()
->create([
'user_id' => $user->id,
'basename' => 'fake-file.pdf',
'name' => 'fake-file.pdf',
]);
$this
->actingAs($user)
->get("file/$file->name")
->assertStatus(401);
}
/**
* @test
*/
@@ -160,11 +256,10 @@ class MeteredBillingRestrictionsTest extends TestCase
'name' => 'fake-file.pdf',
]);
// 404 but, ok, because there is not stored temporary file in test
$this
->actingAs($user)
->get("file/$file->name")
->assertStatus(404);
->get("file/$file->basename")
->assertStatus(200);
}
/**
@@ -196,6 +291,41 @@ class MeteredBillingRestrictionsTest extends TestCase
->assertStatus(401);
}
/**
* @test
*/
public function it_cant_get_shared_file_because_user_has_3_dunning_mails()
{
$user = User::factory()
->create();
Dunning::factory()
->createOneQuietly([
'type' => 'limit_usage_in_new_accounts',
'user_id' => $user->id,
'sequence' => 3,
]);
$file = File::factory()
->create([
'user_id' => $user->id,
'basename' => 'fake-file.pdf',
'name' => 'fake-file.pdf',
]);
$share = Share::factory()
->create([
'item_id' => $file->id,
'user_id' => $user->id,
'type' => 'file',
'is_protected' => false,
]);
$this
->get("file/$file->name/shared/$share->token")
->assertStatus(401);
}
/**
* @test
*/
@@ -234,7 +364,7 @@ class MeteredBillingRestrictionsTest extends TestCase
/**
* @test
*/
public function it_cant_get_share_page()
public function it_cant_get_share_page_because_user_has_3_failed_payments()
{
$user = User::factory()
->hasFailedpayments(3)
@@ -250,4 +380,30 @@ class MeteredBillingRestrictionsTest extends TestCase
$this->get("/share/$share->token")
->assertRedirect('/temporary-unavailable');
}
/**
* @test
*/
public function it_cant_get_share_page_because_user_has_3_dunning_mails()
{
$user = User::factory()
->create();
Dunning::factory()
->createOneQuietly([
'type' => 'limit_usage_in_new_accounts',
'user_id' => $user->id,
'sequence' => 3,
]);
$share = Share::factory()
->create([
'user_id' => $user->id,
'type' => 'folder',
'is_protected' => false,
]);
$this->get("/share/$share->token")
->assertRedirect('/temporary-unavailable');
}
}

View File

@@ -307,7 +307,7 @@ class AdminTest extends TestCase
Sanctum::actingAs($admin);
// Delete user
$this->deleteJson("/api/admin/users/$user->id/delete", [
$this->deleteJson("/api/admin/users/$user->id", [
'name' => $user->settings->name,
])
->assertStatus(200);