543 lines
18 KiB
PHP
543 lines
18 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\User\User;
|
|
use App\Helpers\DateHelper;
|
|
use App\Models\Contact\Tag;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Http\Request;
|
|
use App\Helpers\LocaleHelper;
|
|
use App\Helpers\AccountHelper;
|
|
use App\Helpers\TimezoneHelper;
|
|
use App\Models\Contact\Contact;
|
|
use App\Jobs\AddContactFromVCard;
|
|
use App\Models\Account\ImportJob;
|
|
use App\Models\Account\Invitation;
|
|
use App\Models\Account\AccountLink;
|
|
use App\Services\User\EmailChange;
|
|
use App\Exceptions\StripeException;
|
|
use App\Http\Requests\ImportsRequest;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use App\Notifications\InvitationMail;
|
|
use App\Notifications\AccountLinkMail;
|
|
use App\Http\Requests\SettingsRequest;
|
|
use App\Http\Requests\AccountLinkRequest;
|
|
use App\Services\Contact\Tag\UpdateTag;
|
|
use LaravelWebauthn\Models\WebauthnKey;
|
|
use App\Http\Requests\InvitationRequest;
|
|
use App\Services\Contact\Tag\DestroyTag;
|
|
use App\Services\Account\Settings\ResetAccount;
|
|
use App\Services\Account\Settings\DestroyAccount;
|
|
use PragmaRX\Google2FALaravel\Facade as Google2FA;
|
|
use App\Http\Resources\Contact\ContactShort as ContactResource;
|
|
use App\Http\Resources\Settings\WebauthnKey\WebauthnKey as WebauthnKeyResource;
|
|
|
|
class SettingsController extends Controller
|
|
{
|
|
/**
|
|
* Instantiate a new controller instance.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$this->middleware('limitations')->only(['inviteUser', 'storeImport']);
|
|
}
|
|
|
|
/**
|
|
* Display a listing of the resource.
|
|
*
|
|
* @return \Illuminate\View\View
|
|
*/
|
|
public function index()
|
|
{
|
|
$meContact = null;
|
|
|
|
$search = auth()->user()->first_name.' '.
|
|
auth()->user()->last_name.' '.
|
|
auth()->user()->email;
|
|
$existingContacts = Contact::search($search, auth()->user()->account_id, 'id')
|
|
->real()
|
|
->whereNotIn('id', [auth()->user()->me_contact_id])
|
|
->paginate(20);
|
|
|
|
if (auth()->user()->me_contact_id) {
|
|
$meContact = Contact::where('account_id', auth()->user()->account_id)
|
|
->find(auth()->user()->me_contact_id);
|
|
if ($meContact) {
|
|
$existingContacts->prepend($meContact);
|
|
}
|
|
}
|
|
|
|
$accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account);
|
|
|
|
return view('settings.index')
|
|
->withAccountHasLimitations($accountHasLimitations)
|
|
->withMeContact($meContact ? new ContactResource($meContact) : null)
|
|
->withExistingContacts(ContactResource::collection($existingContacts))
|
|
->withNamesOrder(User::NAMES_ORDER)
|
|
->withLocales(LocaleHelper::getLocaleList()->sortByCollator('name-orig'))
|
|
->withHours(DateHelper::getListOfHours())
|
|
->withSelectedTimezone(TimezoneHelper::adjustEquivalentTimezone(DateHelper::getTimezone()))
|
|
->withTimezones(collect(TimezoneHelper::getListOfTimezones())->map(function (array $timezone): array {
|
|
return ['id' => $timezone['timezone'], 'name'=>$timezone['name']];
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Save user settings.
|
|
*
|
|
* @param SettingsRequest $request
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function save(SettingsRequest $request)
|
|
{
|
|
$user = $request->user();
|
|
|
|
$user->update(
|
|
$request->only([
|
|
'first_name',
|
|
'last_name',
|
|
'timezone',
|
|
'locale',
|
|
'currency_id',
|
|
'name_order',
|
|
'fluid_container',
|
|
'temperature_scale',
|
|
])
|
|
);
|
|
|
|
if ($user->email !== $request->input('email')) {
|
|
app(EmailChange::class)->execute([
|
|
'account_id' => $user->account_id,
|
|
'email' => $request->input('email'),
|
|
'user_id' => $user->id,
|
|
]);
|
|
}
|
|
|
|
if (! AccountHelper::hasLimitations($user->account) && $request->input('me_contact_id')) {
|
|
$user->me_contact_id = $request->input('me_contact_id');
|
|
$user->save();
|
|
}
|
|
|
|
$user->account->default_time_reminder_is_sent = $request->input('reminder_time');
|
|
$user->account->save();
|
|
|
|
return redirect()->route('settings.index')
|
|
->with('status', trans('settings.settings_success', [], $request['locale']));
|
|
}
|
|
|
|
/**
|
|
* Delete user account.
|
|
*
|
|
* @param Request $request
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function delete(Request $request)
|
|
{
|
|
$account = auth()->user()->account;
|
|
|
|
try {
|
|
app(DestroyAccount::class)->execute([
|
|
'account_id' => $account->id,
|
|
]);
|
|
} catch (StripeException $e) {
|
|
return redirect()->route('settings.index')
|
|
->withErrors($e->getMessage());
|
|
}
|
|
|
|
auth('')->logout();
|
|
|
|
return redirect()->route('loginRedirect');
|
|
}
|
|
|
|
/**
|
|
* Reset user account.
|
|
*
|
|
* @param Request $request
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function reset(Request $request)
|
|
{
|
|
$user = $request->user();
|
|
$account = $user->account;
|
|
|
|
ResetAccount::dispatch([
|
|
'account_id' => $account->id,
|
|
]);
|
|
|
|
return redirect()->route('settings.index')
|
|
->with('status', trans('settings.reset_success'));
|
|
}
|
|
|
|
/**
|
|
* Display the import view.
|
|
*
|
|
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
|
*/
|
|
public function import()
|
|
{
|
|
$accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account);
|
|
|
|
if (auth()->user()->account->importjobs->count() == 0) {
|
|
return view('settings.imports.blank')
|
|
->withAccountHasLimitations($accountHasLimitations);
|
|
}
|
|
|
|
return view('settings.imports.index')
|
|
->withAccountHasLimitations($accountHasLimitations);
|
|
}
|
|
|
|
/**
|
|
* Display the Import people's view.
|
|
*
|
|
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function upload()
|
|
{
|
|
if (AccountHelper::hasLimitations(auth()->user()->account)) {
|
|
return redirect()->route('settings.subscriptions.index');
|
|
}
|
|
|
|
return view('settings.imports.upload');
|
|
}
|
|
|
|
public function storeImport(ImportsRequest $request)
|
|
{
|
|
$filename = $request->file('vcard')->store('imports', config('filesystems.default'));
|
|
|
|
$importJob = auth()->user()->account->importjobs()->create([
|
|
'user_id' => auth()->user()->id,
|
|
'type' => 'vcard',
|
|
'filename' => $filename,
|
|
]);
|
|
|
|
AddContactFromVCard::dispatch($importJob, $request->input('behaviour'));
|
|
|
|
return redirect()->route('settings.import');
|
|
}
|
|
|
|
/**
|
|
* Display the import report view.
|
|
*
|
|
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
|
*/
|
|
public function report($importJobId)
|
|
{
|
|
$importJob = ImportJob::where('account_id', auth()->user()->account_id)
|
|
->findOrFail($importJobId);
|
|
|
|
return view('settings.imports.report', compact('importJob'));
|
|
}
|
|
|
|
/**
|
|
* Display the users view.
|
|
*
|
|
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
|
*/
|
|
public function users()
|
|
{
|
|
$users = auth()->user()->account->users;
|
|
$accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account);
|
|
|
|
if ($users->count() == 1 && auth()->user()->account->invitations()->count() == 0) {
|
|
return view('settings.users.blank')
|
|
->withAccountHasLimitations($accountHasLimitations);
|
|
}
|
|
|
|
return view('settings.users.index', compact('users'))
|
|
->withAccountHasLimitations($accountHasLimitations);
|
|
}
|
|
|
|
/**
|
|
* Show the form for creating a new resource.
|
|
*
|
|
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function addUser()
|
|
{
|
|
if (AccountHelper::hasLimitations(auth()->user()->account)) {
|
|
return redirect()->route('settings.subscriptions.index');
|
|
}
|
|
|
|
return view('settings.users.add');
|
|
}
|
|
|
|
/**
|
|
* Store a newly created resource in storage.
|
|
*
|
|
* @param InvitationRequest $request
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function inviteUser(InvitationRequest $request)
|
|
{
|
|
// Make sure the confirmation to invite has not been bypassed
|
|
if (! $request->input('confirmation')) {
|
|
return redirect()->back()->withErrors(trans('settings.users_error_please_confirm'))->withInput();
|
|
}
|
|
|
|
// Is the email address already taken?
|
|
$users = User::where('email', $request->only(['email']))->count();
|
|
if ($users > 0) {
|
|
return redirect()->back()->withErrors(trans('settings.users_error_email_already_taken'))->withInput();
|
|
}
|
|
|
|
// Has this user already been invited?
|
|
$invitations = Invitation::where('email', $request->only(['email']))->count();
|
|
if ($invitations > 0) {
|
|
return redirect()->back()->withErrors(trans('settings.users_error_already_invited'))->withInput();
|
|
}
|
|
|
|
$invitation = auth()->user()->account->invitations()->create(
|
|
$request->only([
|
|
'email',
|
|
])
|
|
+ [
|
|
'invited_by_user_id' => auth()->user()->id,
|
|
'account_id' => auth()->user()->account_id,
|
|
'invitation_key' => Str::random(100),
|
|
]
|
|
);
|
|
|
|
$invitation->notify((new InvitationMail())->locale(auth()->user()->locale));
|
|
|
|
auth()->user()->account->update([
|
|
'number_of_invitations_sent' => auth()->user()->account->number_of_invitations_sent + 1,
|
|
]);
|
|
|
|
return redirect()->route('settings.users.index')
|
|
->with('status', trans('settings.settings_success'));
|
|
}
|
|
|
|
/**
|
|
* Remove the specified resource from storage.
|
|
*
|
|
* @param Invitation $invitation
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function destroyInvitation(Invitation $invitation)
|
|
{
|
|
$invitation->delete();
|
|
|
|
return redirect()->route('settings.users.index')
|
|
->with('success', trans('settings.users_invitation_deleted_confirmation_message'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for linking an existing user account.
|
|
*
|
|
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function linkAccount()
|
|
{
|
|
if (AccountHelper::hasLimitations(auth()->user()->account)) {
|
|
return redirect()->route('settings.subscriptions.index');
|
|
}
|
|
|
|
// Get invitations sent by current user's account
|
|
$sentInvitations = auth()->user()->account->accountLinks()
|
|
->with(['user', 'invitedBy'])
|
|
->get();
|
|
|
|
// Get invitations received by current user
|
|
$receivedInvitations = AccountLink::where('user_id', auth()->user()->id)
|
|
->with(['account', 'invitedBy'])
|
|
->get();
|
|
|
|
return view('settings.users.link', compact('sentInvitations', 'receivedInvitations'));
|
|
}
|
|
|
|
/**
|
|
* Send an account link invitation to an existing user.
|
|
*
|
|
* @param AccountLinkRequest $request
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function inviteExistingUser(AccountLinkRequest $request)
|
|
{
|
|
// Make sure the confirmation to invite has not been bypassed
|
|
if (! $request->input('confirmation')) {
|
|
return redirect()->back()->withErrors(trans('settings.users_error_please_confirm'))->withInput();
|
|
}
|
|
|
|
// Find the user by email
|
|
$user = User::where('email', $request->input('email'))->first();
|
|
|
|
if (! $user) {
|
|
return redirect()->back()->withErrors(trans('settings.users_error_email_not_found'))->withInput();
|
|
}
|
|
|
|
// Check if user is trying to link their own account
|
|
if ($user->id === auth()->user()->id) {
|
|
return redirect()->back()->withErrors(trans('settings.users_error_cannot_link_own_account'))->withInput();
|
|
}
|
|
|
|
// Check if user is already part of this account
|
|
if ($user->account_id === auth()->user()->account_id) {
|
|
return redirect()->back()->withErrors(trans('settings.users_error_already_in_account'))->withInput();
|
|
}
|
|
|
|
// Has this user already been invited to link?
|
|
$existingLink = AccountLink::where('user_id', $user->id)
|
|
->where('account_id', auth()->user()->account_id)
|
|
->count();
|
|
if ($existingLink > 0) {
|
|
return redirect()->back()->withErrors(trans('settings.users_error_already_link_invited'))->withInput();
|
|
}
|
|
|
|
$accountLink = auth()->user()->account->accountLinks()->create([
|
|
'user_id' => $user->id,
|
|
'invited_by_user_id' => auth()->user()->id,
|
|
'account_id' => auth()->user()->account_id,
|
|
'invitation_key' => Str::random(100),
|
|
]);
|
|
|
|
$accountLink->notify((new AccountLinkMail())->locale(auth()->user()->locale));
|
|
|
|
return redirect()->route('settings.users.index')
|
|
->with('status', trans('settings.account_link_sent_success'));
|
|
}
|
|
|
|
/**
|
|
* Remove the specified account link invitation.
|
|
*
|
|
* @param AccountLink $accountLink
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function destroyAccountLink(AccountLink $accountLink)
|
|
{
|
|
$accountLink->delete();
|
|
|
|
return redirect()->route('settings.users.index')
|
|
->with('success', trans('settings.account_link_deleted_confirmation_message'));
|
|
}
|
|
|
|
/**
|
|
* Delete additional user account.
|
|
*
|
|
* @param int $userID
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function deleteAdditionalUser($userID)
|
|
{
|
|
$user = User::where('account_id', auth()->user()->account_id)
|
|
->findOrFail($userID);
|
|
|
|
// make sure you don't delete yourself from this screen
|
|
if ($user->id == auth()->user()->id) {
|
|
return redirect()->route('loginRedirect');
|
|
}
|
|
|
|
$user->delete();
|
|
|
|
return redirect()->route('settings.users.index')
|
|
->with('success', trans('settings.users_list_delete_success'));
|
|
}
|
|
|
|
/**
|
|
* Display the list of tags for this account.
|
|
*/
|
|
public function tags()
|
|
{
|
|
return view('settings.tags')
|
|
->withAccountHasLimitations(AccountHelper::hasLimitations(auth()->user()->account));
|
|
}
|
|
|
|
/**
|
|
* Destroy the tag.
|
|
*
|
|
* @param int $tagId
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function deleteTag($tagId)
|
|
{
|
|
app(DestroyTag::class)->execute([
|
|
'tag_id' => $tagId,
|
|
'account_id' => auth()->user()->account_id,
|
|
]);
|
|
|
|
return redirect()->route('settings.tags.index')
|
|
->with('success', trans('settings.tags_list_delete_success'));
|
|
}
|
|
|
|
/**
|
|
* Edit a tag name.
|
|
*
|
|
* @param Tag $tag
|
|
* @param Request $request
|
|
*
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function editTag(Tag $tag, Request $request): RedirectResponse
|
|
{
|
|
app(UpdateTag::class)->execute([
|
|
'tag_id' => $tag->id,
|
|
'account_id' => auth()->user()->account_id,
|
|
'name' => $request->input('name'),
|
|
]);
|
|
|
|
return back()
|
|
->with('success', trans('settings.tags_list_edit_success'));
|
|
}
|
|
|
|
public function api()
|
|
{
|
|
return view('settings.api.index')
|
|
->withAccountHasLimitations(AccountHelper::hasLimitations(auth()->user()->account));
|
|
}
|
|
|
|
public function dav()
|
|
{
|
|
$davroute = route('sabre.dav');
|
|
$email = auth()->user()->email;
|
|
|
|
return view('settings.dav.index')
|
|
->withDavRoute($davroute)
|
|
->withCardDavRoute("{$davroute}/addressbooks/{$email}/contacts")
|
|
->withCalDavBirthdaysRoute("{$davroute}/calendars/{$email}/birthdays")
|
|
->withCalDavTasksRoute("{$davroute}/calendars/{$email}/tasks")
|
|
->withAccountHasLimitations(AccountHelper::hasLimitations(auth()->user()->account));
|
|
}
|
|
|
|
public function security()
|
|
{
|
|
$webauthnKeys = WebauthnKey::where('user_id', auth()->id())->get();
|
|
|
|
return view('settings.security.index')
|
|
->with('is2FAActivated', Google2FA::isActivated())
|
|
->withWebauthnKeys(WebauthnKeyResource::collection($webauthnKeys))
|
|
->withAccountHasLimitations(AccountHelper::hasLimitations(auth()->user()->account));
|
|
}
|
|
|
|
/**
|
|
* Update the default view when viewing a contact.
|
|
* The default view can be either the life events feed or the general data
|
|
* about the contact (notes, reminders, ...).
|
|
* Possible values: life-events | notes.
|
|
*
|
|
* @param Request $request
|
|
* @return string
|
|
*/
|
|
public function updateDefaultProfileView(Request $request)
|
|
{
|
|
$allowedValues = ['life-events', 'notes', 'photos'];
|
|
/** @var string */
|
|
$view = $request->input('name');
|
|
|
|
if (! in_array($view, $allowedValues)) {
|
|
return 'not allowed';
|
|
}
|
|
|
|
auth()->user()->profile_active_tab = $view;
|
|
|
|
if ($view == 'life-events') {
|
|
auth()->user()->profile_new_life_event_badge_seen = true;
|
|
}
|
|
|
|
auth()->user()->save();
|
|
|
|
return $view;
|
|
}
|
|
}
|