refactor: replace custom CRM with Monica fork
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
This commit is contained in:
28
app/Http/Controllers/Settings/AuditLogController.php
Normal file
28
app/Http/Controllers/Settings/AuditLogController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Helpers\AccountHelper;
|
||||
use App\Helpers\AuditLogHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class AuditLogController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the page listing all the audit logs.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$logs = auth()->user()->account->auditLogs()
|
||||
->with('author')
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(15);
|
||||
|
||||
$accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account);
|
||||
|
||||
return view('settings.auditlog.index')
|
||||
->withLogsCollection(AuditLogHelper::getCollectionOfAudits($logs))
|
||||
->withAccountHasLimitations($accountHasLimitations)
|
||||
->withLogsPagination($logs);
|
||||
}
|
||||
}
|
||||
124
app/Http/Controllers/Settings/ExportController.php
Normal file
124
app/Http/Controllers/Settings/ExportController.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Jobs\ExportAccount;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Helpers\AccountHelper;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Models\Account\ExportJob;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ExportController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the export view.
|
||||
*
|
||||
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$exports = ExportJob::where([
|
||||
'account_id' => auth()->user()->account_id,
|
||||
'user_id' => auth()->user()->id,
|
||||
])
|
||||
->orderByDesc('created_at')
|
||||
->get();
|
||||
|
||||
return view('settings.export')
|
||||
->withAccountHasLimitations(AccountHelper::hasLimitations(auth()->user()->account))
|
||||
->withExports($exports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the data of the account in SQL format.
|
||||
*
|
||||
* @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response|null
|
||||
*/
|
||||
public function storeSql()
|
||||
{
|
||||
$job = $this->newExport(ExportJob::SQL);
|
||||
ExportAccount::dispatch($job);
|
||||
|
||||
return redirect()->route('settings.export.index')
|
||||
->withStatus(trans('settings.export_submitted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the data of the account in SQL format.
|
||||
*
|
||||
* @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response|null
|
||||
*/
|
||||
public function storeJson()
|
||||
{
|
||||
$job = $this->newExport(ExportJob::JSON);
|
||||
ExportAccount::dispatch($job);
|
||||
|
||||
return redirect()->route('settings.export.index')
|
||||
->withStatus(trans('settings.export_submitted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ExportJob.
|
||||
*
|
||||
* @param string $type
|
||||
* @return ExportJob
|
||||
*/
|
||||
private function newExport(string $type): ExportJob
|
||||
{
|
||||
$exports = ExportJob::where([
|
||||
'account_id' => auth()->user()->account_id,
|
||||
'user_id' => auth()->user()->id,
|
||||
])
|
||||
->orderBy('created_at')
|
||||
->get();
|
||||
|
||||
if ($exports->count() >= config('monica.export_size')) {
|
||||
$job = $exports->first();
|
||||
try {
|
||||
if ($job->filename !== null) {
|
||||
StorageHelper::disk($job->location)
|
||||
->delete($job->filename);
|
||||
}
|
||||
} finally {
|
||||
$job->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return ExportJob::create([
|
||||
'account_id' => auth()->user()->account_id,
|
||||
'user_id' => auth()->user()->id,
|
||||
'type' => $type,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the generated file.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $uuid
|
||||
* @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response|null
|
||||
*/
|
||||
public function download(Request $request, string $uuid)
|
||||
{
|
||||
$job = ExportJob::where([
|
||||
'account_id' => auth()->user()->account_id,
|
||||
'user_id' => auth()->user()->id,
|
||||
'uuid' => $uuid,
|
||||
])->firstOrFail();
|
||||
|
||||
if ($job->status !== ExportJob::EXPORT_DONE) {
|
||||
return redirect()->route('settings.export.index')
|
||||
->withErrors(trans('settings.export_not_done'));
|
||||
}
|
||||
$disk = StorageHelper::disk($job->location);
|
||||
|
||||
return $disk->response($job->filename,
|
||||
"monica.{$job->type}",
|
||||
[
|
||||
'Content-Type' => "application/{$job->type}; charset=utf-8",
|
||||
'Content-Disposition' => "attachment; filename=monica.{$job->type}",
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
178
app/Http/Controllers/Settings/GendersController.php
Normal file
178
app/Http/Controllers/Settings/GendersController.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Helpers\GenderHelper;
|
||||
use App\Models\Contact\Gender;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Helpers\CollectionHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\JsonRespondController;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Http\Requests\Settings\GendersRequest;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class GendersController extends Controller
|
||||
{
|
||||
use JsonRespondController;
|
||||
|
||||
/**
|
||||
* Get all the gender types.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$gendersData = collect([]);
|
||||
$genders = auth()->user()->account->genders;
|
||||
|
||||
foreach ($genders as $gender) {
|
||||
$gendersData->push($this->formatData($gender));
|
||||
}
|
||||
|
||||
return CollectionHelper::sortByCollator($gendersData, 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the gender sex types.
|
||||
*/
|
||||
public function types()
|
||||
{
|
||||
$gendersData = collect([]);
|
||||
|
||||
$types = [
|
||||
Gender::MALE,
|
||||
Gender::FEMALE,
|
||||
Gender::OTHER,
|
||||
Gender::UNKNOWN,
|
||||
Gender::NONE,
|
||||
];
|
||||
|
||||
foreach ($types as $type) {
|
||||
$gendersData->push([
|
||||
'id' => $type,
|
||||
'name' => trans('settings.personalization_genders_'.strtolower($type)),
|
||||
]);
|
||||
}
|
||||
|
||||
return CollectionHelper::sortByCollator($gendersData, 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the gender.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
Validator::make($request->all(), [
|
||||
'name' => 'required|max:255',
|
||||
'type' => ['required', Rule::in(Gender::LIST)],
|
||||
])->validate();
|
||||
|
||||
$gender = auth()->user()->account->genders()->create(
|
||||
$request->only([
|
||||
'name',
|
||||
'type',
|
||||
])
|
||||
+ [
|
||||
'account_id' => auth()->user()->account_id,
|
||||
]
|
||||
);
|
||||
|
||||
if ($request->input('isDefault')) {
|
||||
$this->updateDefault($gender);
|
||||
}
|
||||
|
||||
return $this->formatData($gender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the given gender.
|
||||
*/
|
||||
public function update(GendersRequest $request, Gender $gender)
|
||||
{
|
||||
$gender->update(
|
||||
$request->only([
|
||||
'name',
|
||||
'type',
|
||||
])
|
||||
);
|
||||
if ($request->input('isDefault')) {
|
||||
$this->updateDefault($gender);
|
||||
$gender->refresh();
|
||||
} elseif ($gender->isDefault()) {
|
||||
// Case of this gender was the default one previously
|
||||
$account = auth()->user()->account;
|
||||
$account->default_gender_id = null;
|
||||
$account->save();
|
||||
$gender->refresh();
|
||||
}
|
||||
|
||||
return $this->formatData($gender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a gender type.
|
||||
*/
|
||||
public function destroyAndReplaceGender(Gender $gender, $genderId)
|
||||
{
|
||||
$account = auth()->user()->account;
|
||||
try {
|
||||
$genderToReplaceWith = Gender::where('account_id', $account->id)
|
||||
->findOrFail($genderId);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return response()->json([
|
||||
'message' => trans('settings.personalization_genders_modal_error'),
|
||||
], 403);
|
||||
}
|
||||
|
||||
// We get the new gender to associate the contacts with.
|
||||
GenderHelper::replace($account, $gender, $genderToReplaceWith);
|
||||
|
||||
if ($gender->isDefault()) {
|
||||
$account->default_gender_id = $genderToReplaceWith->id;
|
||||
$account->save();
|
||||
}
|
||||
|
||||
$gender->delete();
|
||||
|
||||
return $this->respondObjectDeleted($gender->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a gender type.
|
||||
*/
|
||||
public function destroy(Gender $gender)
|
||||
{
|
||||
$gender->delete();
|
||||
|
||||
return $this->respondObjectDeleted($gender->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the given gender to the default gender.
|
||||
*/
|
||||
public function updateDefault(Gender $gender)
|
||||
{
|
||||
$account = auth()->user()->account;
|
||||
$account->default_gender_id = $gender->id;
|
||||
$account->save();
|
||||
|
||||
return $this->formatData($gender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format data for output.
|
||||
*
|
||||
* @param Gender $gender
|
||||
* @return array
|
||||
*/
|
||||
private function formatData($gender)
|
||||
{
|
||||
return [
|
||||
'id' => $gender->id,
|
||||
'name' => $gender->name,
|
||||
'type' => $gender->type,
|
||||
'isDefault' => $gender->isDefault(),
|
||||
'numberOfContacts' => $gender->contacts->count(),
|
||||
];
|
||||
}
|
||||
}
|
||||
45
app/Http/Controllers/Settings/ModulesController.php
Normal file
45
app/Http/Controllers/Settings/ModulesController.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Models\User\Module;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\JsonRespondController;
|
||||
|
||||
class ModulesController extends Controller
|
||||
{
|
||||
use JsonRespondController;
|
||||
|
||||
/**
|
||||
* Get all the reminder rules.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$modules = auth()->user()->account->modules;
|
||||
|
||||
return $modules->map(function ($module) {
|
||||
return $this->format($module);
|
||||
});
|
||||
}
|
||||
|
||||
public function toggle(Request $request, Module $module)
|
||||
{
|
||||
$module->active = ! $module->active;
|
||||
$module->save();
|
||||
|
||||
return $this->respond([
|
||||
'data' => $this->format($module),
|
||||
]);
|
||||
}
|
||||
|
||||
private function format(Module $module)
|
||||
{
|
||||
return [
|
||||
'id' => $module->id,
|
||||
'key' => $module->key,
|
||||
'name' => trans($module->translation_key),
|
||||
'active' => $module->active,
|
||||
];
|
||||
}
|
||||
}
|
||||
157
app/Http/Controllers/Settings/MultiFAController.php
Normal file
157
app/Http/Controllers/Settings/MultiFAController.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Models\User\User;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\JsonRespondController;
|
||||
use Illuminate\Foundation\Auth\RedirectsUsers;
|
||||
use PragmaRX\Google2FALaravel\Facade as Google2FA;
|
||||
use PragmaRX\Google2FALaravel\Support\Authenticator;
|
||||
|
||||
class MultiFAController extends Controller
|
||||
{
|
||||
use RedirectsUsers, JsonRespondController;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/settings/security';
|
||||
|
||||
/**
|
||||
* Session var name to store secret code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $SESSION_TFA_SECRET = '2FA_secret';
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function enableTwoFactor(Request $request)
|
||||
{
|
||||
//generate new secret
|
||||
$secret = $this->generateSecret();
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
//generate image for QR barcode
|
||||
$imageDataUri = Google2FA::getQRCodeInline(
|
||||
$request->getHttpHost(),
|
||||
$user->email,
|
||||
$secret,
|
||||
200
|
||||
);
|
||||
|
||||
$request->session()->put($this->SESSION_TFA_SECRET, $secret);
|
||||
|
||||
return response()->json(['image' => $imageDataUri, 'secret' => $secret]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function validateTwoFactor(Request $request)
|
||||
{
|
||||
//get user
|
||||
$user = $request->user();
|
||||
|
||||
if (! is_null($user->google2fa_secret)) {
|
||||
return response()->json(['error' => trans('settings.2fa_enable_error_already_set')]);
|
||||
}
|
||||
|
||||
$this->validate($request, [
|
||||
'one_time_password' => 'required',
|
||||
]);
|
||||
|
||||
//retrieve secret
|
||||
$secret = $request->session()->pull($this->SESSION_TFA_SECRET);
|
||||
|
||||
$authenticator = app(Authenticator::class)->boot($request);
|
||||
|
||||
if ($authenticator->verifyGoogle2FA($secret, $request['one_time_password'])) {
|
||||
//encrypt and then save secret
|
||||
$user->google2fa_secret = $secret;
|
||||
$user->save();
|
||||
|
||||
$authenticator->login();
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
$authenticator->logout();
|
||||
|
||||
return response()->json(['success' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function disableTwoFactor(Request $request)
|
||||
{
|
||||
return view('settings.security.2fa-disable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function deactivateTwoFactor(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'one_time_password' => 'required',
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
if ($this->validateTwoFactorLogin($request, $user, $request['one_time_password'])) {
|
||||
//make secret column blank
|
||||
$user->google2fa_secret = null;
|
||||
$user->save();
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
return response()->json(['success' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate 2nd factor for user with 2FA code or recovery code.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param User $user
|
||||
* @param string $oneTimePassword
|
||||
* @return bool
|
||||
*/
|
||||
private function validateTwoFactorLogin(Request $request, User $user, string $oneTimePassword): bool
|
||||
{
|
||||
//retrieve secret
|
||||
$secret = $user->google2fa_secret;
|
||||
|
||||
$authenticator = app(Authenticator::class)->boot($request);
|
||||
|
||||
// try provided token as a 2FA code, or as a recovery code
|
||||
if ($authenticator->verifyGoogle2FA($secret, $oneTimePassword)
|
||||
|| $user->recoveryChallenge($oneTimePassword)) {
|
||||
$authenticator->logout();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a secret key in Base32 format.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generateSecret()
|
||||
{
|
||||
return Google2FA::generateSecretKey(32);
|
||||
}
|
||||
}
|
||||
112
app/Http/Controllers/Settings/PersonalizationController.php
Normal file
112
app/Http/Controllers/Settings/PersonalizationController.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Helpers\AccountHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\JsonRespondController;
|
||||
use App\Models\Contact\ContactFieldType;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class PersonalizationController extends Controller
|
||||
{
|
||||
use JsonRespondController;
|
||||
|
||||
/**
|
||||
* Display the personalization page.
|
||||
*
|
||||
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account);
|
||||
|
||||
return view('settings.personalization.index')
|
||||
->withAccountHasLimitations($accountHasLimitations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the contact field types.
|
||||
*/
|
||||
public function getContactFieldTypes()
|
||||
{
|
||||
return auth()->user()->account->contactFieldTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return string
|
||||
*/
|
||||
public function storeContactFieldType(Request $request)
|
||||
{
|
||||
Validator::make($request->all(), [
|
||||
'name' => 'required|max:255',
|
||||
'icon' => 'max:255|nullable',
|
||||
'protocol' => 'max:255|nullable',
|
||||
])->validate();
|
||||
|
||||
return auth()->user()->account->contactFieldTypes()->create(
|
||||
$request->only([
|
||||
'name',
|
||||
'protocol',
|
||||
])
|
||||
+ [
|
||||
'fontawesome_icon' => $request->input('icon'),
|
||||
'account_id' => auth()->user()->account_id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a newly created resource in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param ContactFieldType $contactFieldType
|
||||
* @return ContactFieldType
|
||||
*/
|
||||
public function editContactFieldType(Request $request, ContactFieldType $contactFieldType): ContactFieldType
|
||||
{
|
||||
Validator::make($request->all(), [
|
||||
'name' => 'required|max:255',
|
||||
'icon' => 'max:255|nullable',
|
||||
'protocol' => 'max:255|nullable',
|
||||
])->validate();
|
||||
|
||||
$contactFieldType->update(
|
||||
$request->only([
|
||||
'name',
|
||||
'protocol',
|
||||
])
|
||||
+ [
|
||||
'fontawesome_icon' => $request->input('icon'),
|
||||
]
|
||||
);
|
||||
|
||||
return $contactFieldType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the contact field type.
|
||||
*/
|
||||
public function destroyContactFieldType(Request $request, ContactFieldType $contactFieldType)
|
||||
{
|
||||
if (! $contactFieldType->delible) {
|
||||
return $this->respondUnauthorized();
|
||||
}
|
||||
|
||||
// find all the contact fields that have this contact field types
|
||||
$contactFields = auth()->user()->account->contactFields
|
||||
->where('contact_field_type_id', $contactFieldType->id);
|
||||
|
||||
foreach ($contactFields as $contactField) {
|
||||
$contactField->delete();
|
||||
}
|
||||
|
||||
$contactFieldType->delete();
|
||||
|
||||
return $this->respondObjectDeleted($contactFieldType->id);
|
||||
}
|
||||
}
|
||||
103
app/Http/Controllers/Settings/RecoveryCodesController.php
Normal file
103
app/Http/Controllers/Settings/RecoveryCodesController.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use PragmaRX\Random\Random;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User\RecoveryCode;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\JsonRespondController;
|
||||
|
||||
class RecoveryCodesController extends Controller
|
||||
{
|
||||
use JsonRespondController;
|
||||
|
||||
/**
|
||||
* Generate recovery codes.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Support\Collection<array-key, array{id: int, recovery: string, used: bool}>
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// Remove previous codes
|
||||
auth()->user()->recoveryCodes()
|
||||
->each(function ($code) {
|
||||
$code->delete();
|
||||
});
|
||||
|
||||
// Generate new codes
|
||||
$this->generate();
|
||||
|
||||
$codes = auth()->user()->recoveryCodes()->get();
|
||||
|
||||
return $this->response($codes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of recovery codes.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Support\Collection<array-key, array{id: int, recovery: string, used: bool}>
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$codes = auth()->user()->recoveryCodes()->get();
|
||||
|
||||
if (count($codes) == 0) {
|
||||
$this->generate();
|
||||
$codes = auth()->user()->recoveryCodes()->get();
|
||||
}
|
||||
|
||||
return $this->response($codes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format codes collection for response.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection<array-key, \App\Models\User\RecoveryCode> $codes
|
||||
* @return \Illuminate\Support\Collection<array-key, array{id: int, recovery: string, used: bool}>
|
||||
*/
|
||||
private function response($codes)
|
||||
{
|
||||
return $codes->map(function (RecoveryCode $code): array {
|
||||
return [
|
||||
'id' => $code->id,
|
||||
'recovery' => $code->recovery,
|
||||
'used' => (bool) $code->used,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new recovery codes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function generate()
|
||||
{
|
||||
// Generate new codes
|
||||
$random = new Random();
|
||||
$random->uppercase(true);
|
||||
|
||||
$codes = [];
|
||||
|
||||
for ($i = 1; $i <= (int) config('auth.recovery.count'); $i++) {
|
||||
$blocks = [];
|
||||
|
||||
for ($j = 1; $j <= (int) config('auth.recovery.blocks'); $j++) {
|
||||
$blocks[] = $random->size(config('auth.recovery.chars'))->get();
|
||||
}
|
||||
|
||||
$codes[] = implode('-', $blocks);
|
||||
}
|
||||
|
||||
foreach ($codes as $code) {
|
||||
RecoveryCode::create([
|
||||
'account_id' => auth()->user()->account_id,
|
||||
'user_id' => auth()->user()->id,
|
||||
'recovery' => $code,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
app/Http/Controllers/Settings/ReminderRulesController.php
Normal file
44
app/Http/Controllers/Settings/ReminderRulesController.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Contact\ReminderRule;
|
||||
use App\Traits\JsonRespondController;
|
||||
|
||||
class ReminderRulesController extends Controller
|
||||
{
|
||||
use JsonRespondController;
|
||||
|
||||
/**
|
||||
* Get all the reminder rules.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$reminderRules = auth()->user()->account->reminderRules;
|
||||
|
||||
return $reminderRules->map(function ($reminderRule) {
|
||||
return $this->format($reminderRule);
|
||||
});
|
||||
}
|
||||
|
||||
public function toggle(Request $request, ReminderRule $reminderRule)
|
||||
{
|
||||
$reminderRule->active = ! $reminderRule->active;
|
||||
$reminderRule->save();
|
||||
|
||||
return $this->respond([
|
||||
'data' => $this->format($reminderRule),
|
||||
]);
|
||||
}
|
||||
|
||||
private function format(ReminderRule $reminderRule)
|
||||
{
|
||||
return [
|
||||
'id' => $reminderRule->id,
|
||||
'number_of_days_before' => $reminderRule->number_of_days_before,
|
||||
'active' => $reminderRule->active,
|
||||
];
|
||||
}
|
||||
}
|
||||
43
app/Http/Controllers/Settings/StorageController.php
Normal file
43
app/Http/Controllers/Settings/StorageController.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Models\Account\Photo;
|
||||
use App\Helpers\AccountHelper;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Models\Contact\Document;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class StorageController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get all the information about the account in terms of storage.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$documents = Document::where('account_id', auth()->user()->account_id)->get();
|
||||
$photos = Photo::where('account_id', auth()->user()->account_id)->get();
|
||||
/** @var \Illuminate\Support\Collection<array-key, \Illuminate\Database\Eloquent\Model> */
|
||||
$documents = collect($documents);
|
||||
$elements = $documents->concat($photos)->sortByDesc('created_at');
|
||||
|
||||
// size is in bytes in the database
|
||||
$currentAccountSize = StorageHelper::getAccountStorageSize(auth()->user()->account);
|
||||
|
||||
if ($currentAccountSize != 0) {
|
||||
$currentAccountSize = round($currentAccountSize / 1000000);
|
||||
}
|
||||
|
||||
// correspondingPercent
|
||||
$percentUsage = round($currentAccountSize * 100 / config('monica.max_storage_size'));
|
||||
|
||||
$accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account);
|
||||
|
||||
return view('settings.storage.index')
|
||||
->withAccountHasLimitations($accountHasLimitations)
|
||||
->withElements($elements)
|
||||
->withCurrentAccountSize($currentAccountSize)
|
||||
->withAccountLimit(config('monica.max_storage_size'))
|
||||
->withPercentUsage($percentUsage);
|
||||
}
|
||||
}
|
||||
367
app/Http/Controllers/Settings/SubscriptionsController.php
Normal file
367
app/Http/Controllers/Settings/SubscriptionsController.php
Normal file
@@ -0,0 +1,367 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use App\Traits\StripeCall;
|
||||
use App\Helpers\DateHelper;
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Cashier\Cashier;
|
||||
use Laravel\Cashier\Payment;
|
||||
use App\Helpers\AccountHelper;
|
||||
use App\Helpers\InstanceHelper;
|
||||
use App\Exceptions\StripeException;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Stripe\Exception\ApiErrorException;
|
||||
use Laravel\Cashier\Exceptions\IncompletePayment;
|
||||
use App\Services\Account\Settings\ArchiveAllContacts;
|
||||
|
||||
class SubscriptionsController extends Controller
|
||||
{
|
||||
use StripeCall;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return View|Factory|RedirectResponse
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if (! config('monica.requires_subscription')) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
$account = auth()->user()->account;
|
||||
|
||||
$subscription = $account->getSubscribedPlan();
|
||||
if (! $account->isSubscribed() && (! $subscription || $subscription->ended())) {
|
||||
return view('settings.subscriptions.blank', [
|
||||
'numberOfCustomers' => InstanceHelper::getNumberOfPaidSubscribers(),
|
||||
]);
|
||||
}
|
||||
|
||||
$hasInvoices = $account->hasStripeId() && $account->hasInvoices();
|
||||
$invoices = null;
|
||||
if ($hasInvoices) {
|
||||
$invoices = $account->invoices();
|
||||
}
|
||||
|
||||
try {
|
||||
$planInformation = $this->stripeCall(function () use ($subscription): ?array {
|
||||
return InstanceHelper::getPlanInformationFromSubscription($subscription);
|
||||
});
|
||||
} catch (StripeException $e) {
|
||||
$planInformation = null;
|
||||
}
|
||||
|
||||
return view('settings.subscriptions.account', [
|
||||
'planInformation' => $planInformation,
|
||||
'subscription' => $subscription,
|
||||
'hasInvoices' => $hasInvoices,
|
||||
'invoices' => $invoices,
|
||||
'accountHasLimitations' => AccountHelper::hasLimitations($account),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the upgrade view page.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return View|Factory|RedirectResponse
|
||||
*/
|
||||
public function upgrade(Request $request)
|
||||
{
|
||||
if (! config('monica.requires_subscription')) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
if (auth()->user()->account->isSubscribed()) {
|
||||
return redirect()->route('settings.subscriptions.index');
|
||||
}
|
||||
|
||||
$plan = $request->query('plan');
|
||||
if ($plan !== 'monthly' && $plan !== 'annual') {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$planInformation = InstanceHelper::getPlanInformationFromConfig($plan);
|
||||
|
||||
if ($planInformation === null) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return view('settings.subscriptions.upgrade', [
|
||||
'planInformation' => $planInformation,
|
||||
'nextTheoriticalBillingDate' => DateHelper::getFullDate(DateHelper::getNextTheoriticalBillingDate($plan)),
|
||||
'intent' => auth()->user()->account->createSetupIntent(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the update view page.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return View|Factory|RedirectResponse
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
if (! config('monica.requires_subscription')) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
$account = auth()->user()->account;
|
||||
|
||||
$subscription = $account->getSubscribedPlan();
|
||||
if (! $account->isSubscribed() && (! $subscription || $subscription->ended())) {
|
||||
return view('settings.subscriptions.blank', [
|
||||
'numberOfCustomers' => InstanceHelper::getNumberOfPaidSubscribers(),
|
||||
]);
|
||||
}
|
||||
|
||||
$planInformation = InstanceHelper::getPlanInformationFromSubscription($subscription);
|
||||
|
||||
if ($planInformation === null) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$plans = collect();
|
||||
foreach (['monthly', 'annual'] as $plan) {
|
||||
$plans->push(InstanceHelper::getPlanInformationFromConfig($plan));
|
||||
}
|
||||
|
||||
$legacyPlan = null;
|
||||
if (! $plans->contains(function ($value) use ($planInformation) {
|
||||
return $value['id'] === $planInformation['id'];
|
||||
})) {
|
||||
$legacyPlan = $planInformation;
|
||||
}
|
||||
|
||||
return view('settings.subscriptions.update', [
|
||||
'planInformation' => $planInformation,
|
||||
'plans' => $plans,
|
||||
'legacyPlan' => $legacyPlan,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the update process.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return View|Factory|RedirectResponse
|
||||
*/
|
||||
public function processUpdate(Request $request)
|
||||
{
|
||||
$account = auth()->user()->account;
|
||||
|
||||
$subscription = $account->getSubscribedPlan();
|
||||
if (! $account->isSubscribed() && ! $subscription) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
try {
|
||||
$account->updateSubscription($request->input('frequency'), $subscription);
|
||||
} catch (StripeException $e) {
|
||||
return back()
|
||||
->withInput()
|
||||
->withErrors($e->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('settings.subscriptions.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the confirm view page.
|
||||
*
|
||||
* @return View|Factory|RedirectResponse
|
||||
*
|
||||
* @throws ApiErrorException
|
||||
*/
|
||||
public function confirmPayment($id)
|
||||
{
|
||||
try {
|
||||
$payment = $this->stripeCall(function () use ($id): \Stripe\PaymentIntent {
|
||||
return Cashier::stripe()->paymentIntents->retrieve($id);
|
||||
});
|
||||
} catch (StripeException $e) {
|
||||
return back()->withErrors($e->getMessage());
|
||||
}
|
||||
|
||||
return view('settings.subscriptions.confirm', [
|
||||
'payment' => new Payment($payment),
|
||||
'redirect' => request('redirect'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the upgrade success page.
|
||||
*
|
||||
* @return View|Factory|RedirectResponse
|
||||
*/
|
||||
public function upgradeSuccess()
|
||||
{
|
||||
if (! config('monica.requires_subscription')) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
return view('settings.subscriptions.success');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the downgrade success page.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return View|Factory|RedirectResponse
|
||||
*/
|
||||
public function downgradeSuccess(Request $request)
|
||||
{
|
||||
if (! config('monica.requires_subscription')) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
return view('settings.subscriptions.downgrade-success');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the archive all your contacts page.
|
||||
*
|
||||
* @return View|Factory|RedirectResponse
|
||||
*/
|
||||
public function archive()
|
||||
{
|
||||
return view('settings.subscriptions.archive');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the Archive process.
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function processArchive()
|
||||
{
|
||||
app(ArchiveAllContacts::class)->execute([
|
||||
'account_id' => auth()->user()->account_id,
|
||||
]);
|
||||
|
||||
return redirect()->route('settings.subscriptions.downgrade');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the downgrade view page.
|
||||
*
|
||||
* @return View|Factory|RedirectResponse
|
||||
*/
|
||||
public function downgrade()
|
||||
{
|
||||
$account = auth()->user()->account;
|
||||
|
||||
if (! config('monica.requires_subscription')) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
$subscription = $account->getSubscribedPlan();
|
||||
if (! $account->isSubscribed() && ! $subscription) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
return view('settings.subscriptions.downgrade-checklist')
|
||||
->with('numberOfActiveContacts', $account->allContacts()->active()->count())
|
||||
->with('numberOfPendingInvitations', $account->invitations()->count())
|
||||
->with('numberOfUsers', $account->users()->count())
|
||||
->with('accountHasLimitations', AccountHelper::hasLimitations($account))
|
||||
->with('hasReachedContactLimit', ! AccountHelper::isBelowContactLimit($account))
|
||||
->with('canDowngrade', AccountHelper::canDowngrade($account));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the downgrade process.
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function processDowngrade()
|
||||
{
|
||||
$account = auth()->user()->account;
|
||||
|
||||
if (! AccountHelper::canDowngrade($account)) {
|
||||
return redirect()->route('settings.subscriptions.downgrade');
|
||||
}
|
||||
|
||||
$subscription = $account->getSubscribedPlan();
|
||||
if (! $account->isSubscribed() && ! $subscription) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
try {
|
||||
$account->subscriptionCancel();
|
||||
} catch (StripeException $e) {
|
||||
return back()
|
||||
->withInput()
|
||||
->withErrors($e->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('settings.subscriptions.downgrade.success');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the upgrade payment.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function processPayment(Request $request)
|
||||
{
|
||||
if (! config('monica.requires_subscription')) {
|
||||
return redirect()->route('settings.index');
|
||||
}
|
||||
|
||||
try {
|
||||
auth()->user()->account
|
||||
->subscribe($request->input('payment_method'), $request->input('plan'));
|
||||
} catch (IncompletePayment $e) {
|
||||
return redirect()->route(
|
||||
'settings.subscriptions.confirm',
|
||||
[$e->payment->asStripePaymentIntent()->id, 'redirect' => route('settings.subscriptions.upgrade.success')]
|
||||
);
|
||||
} catch (StripeException $e) {
|
||||
return back()
|
||||
->withInput()
|
||||
->withErrors($e->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('settings.subscriptions.upgrade.success');
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the invoice as PDF.
|
||||
*
|
||||
* @param mixed $invoiceId
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function downloadInvoice($invoiceId)
|
||||
{
|
||||
return auth()->user()->account->downloadInvoice($invoiceId, [
|
||||
'vendor' => 'Monica',
|
||||
'product' => trans('settings.subscriptions_pdf_title', ['name' => config('monica.paid_plan_monthly_friendly_name')]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the invoice as PDF.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|null
|
||||
*/
|
||||
public function forceCompletePaymentOnTesting(Request $request): ?RedirectResponse
|
||||
{
|
||||
if (App::environment('production')) {
|
||||
return null;
|
||||
}
|
||||
$subscription = auth()->user()->account->getSubscribedPlan();
|
||||
$subscription->stripe_status = 'active';
|
||||
$subscription->save();
|
||||
|
||||
return redirect()->route('settings.subscriptions.index');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user