Merge branch 'develop' into 5.8-dev

This commit is contained in:
James Cole 2022-10-01 19:06:55 +02:00
commit 5f11272d49
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
25 changed files with 367 additions and 122 deletions

View File

@ -0,0 +1,93 @@
<?php
/*
* PurgeController.php
* Copyright (c) 2022 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V1\Controllers\Data;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Http\JsonResponse;
class PurgeController extends Controller
{
/**
* @return JsonResponse
*/
public function purge(): JsonResponse
{
$user = auth()->user();
// some manual code, too lazy to call all repositories.
//,transactions,withdrawals,deposits,transfers';
// budgets:
Budget::whereUserId($user->id)->onlyTrashed()->forceDelete();
// bills
Bill::whereUserId($user->id)->onlyTrashed()->forceDelete();
// piggies
$set = PiggyBank::leftJoin('accounts','accounts.id','piggy_banks.account_id')
->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*']);
/** @var PiggyBank $piggy */
foreach($set as $piggy) {
$piggy->forceDelete();
}
// rule group
RuleGroup::whereUserId($user->id)->onlyTrashed()->forceDelete();
// rules
Rule::whereUserId($user->id)->onlyTrashed()->forceDelete();
// recurring transactions
Recurrence::whereUserId($user->id)->onlyTrashed()->forceDelete();
// categories
Category::whereUserId($user->id)->onlyTrashed()->forceDelete();
// tags
Tag::whereUserId($user->id)->onlyTrashed()->forceDelete();
// accounts
Account::whereUserId($user->id)->onlyTrashed()->forceDelete();
// transaction groups
TransactionGroup::whereUserId($user->id)->onlyTrashed()->forceDelete();
// transaction journals
TransactionJournal::whereUserId($user->id)->onlyTrashed()->forceDelete();
return response()->json([], 204);
}
}

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/*
* InvitationCreated.php
* Copyright (c) 2022 james@firefly-iii.org

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/*
* NewVersionAvailable.php
* Copyright (c) 2022 james@firefly-iii.org

View File

@ -27,12 +27,14 @@ use Carbon\Carbon;
use Database\Seeders\ExchangeRateSeeder;
use Exception;
use FireflyIII\Events\ActuallyLoggedIn;
use FireflyIII\Events\Admin\InvitationCreated;
use FireflyIII\Events\DetectedNewIPAddress;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Events\UserChangedEmail;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Mail\ConfirmEmailChangeMail;
use FireflyIII\Mail\InvitationMail;
use FireflyIII\Mail\UndoEmailChangeMail;
use FireflyIII\Models\GroupMembership;
use FireflyIII\Models\UserGroup;
@ -77,6 +79,23 @@ class UserEventHandler
}
}
/**
* @param InvitationCreated $event
* @return void
*/
public function sendRegistrationInvite(InvitationCreated $event): void
{
$invitee = $event->invitee->email;
$admin = $event->invitee->user->email;
$url = route('invite', [$event->invitee->invite_code]);
try {
Mail::to($invitee)->send(new InvitationMail($invitee, $admin, $url));
} catch (Exception $e) { // @phpstan-ignore-line
Log::error($e->getMessage());
}
}
/**
* @param RegisteredUser $event
* @return bool

View File

@ -237,9 +237,6 @@ trait MetaCollection
return $this;
}
/**
* @param string $url
* @return GroupCollectorInterface
@ -269,9 +266,6 @@ trait MetaCollection
return $this;
}
/**
* @param string $url
* @return GroupCollectorInterface

View File

@ -411,9 +411,6 @@ interface GroupCollectorInterface
* @return GroupCollectorInterface
*/
public function externalIdDoesNotContain(string $externalId): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
@ -437,9 +434,6 @@ interface GroupCollectorInterface
* @return GroupCollectorInterface
*/
public function externalIdDoesNotStart(string $externalId): GroupCollectorInterface;
/**
* @param string $url
* @return GroupCollectorInterface
@ -1038,9 +1032,6 @@ interface GroupCollectorInterface
* @return GroupCollectorInterface
*/
public function excludeInternalReference(string $externalId): GroupCollectorInterface;
/**
* Limit the result to a set of specific transaction journals.
*

View File

@ -151,10 +151,14 @@ class BudgetLimitController extends Controller
// sanity check on amount:
if ((float) $amount === 0.0) {
$amount = '1';
if (null !== $limit) {
$this->blRepository->destroyBudgetLimit($limit);
}
// return empty=ish array:
return response()->json([]);
}
if ((int) $amount > 65536) {
$amount = '65536';
if ((int) $amount > 16777216) {
$amount = '16777216';
}
if (null !== $limit) {
@ -175,7 +179,7 @@ class BudgetLimitController extends Controller
if ($request->expectsJson()) {
$array = $limit->toArray();
// add some extra meta data:
// add some extra metadata:
$spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $currency);
$array['spent'] = $spentArr[$currency->id]['sum'] ?? '0';
$array['left_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, bcadd($array['spent'], $array['amount']));
@ -208,10 +212,19 @@ class BudgetLimitController extends Controller
// sanity check on amount:
if ((float) $amount === 0.0) {
$amount = '1';
$budgetId = $budgetLimit->budget_id;
$currency = $budgetLimit->transactionCurrency;
$this->blRepository->destroyBudgetLimit($budgetLimit);
$array = [
'budget_id' => $budgetId,
'left_formatted' => app('amount')->formatAnything($currency, '0'),
'left_per_day_formatted' => app('amount')->formatAnything($currency, '0'),
'transaction_currency_id' => $currency->id,
];
return response()->json($array);
}
if ((int) $amount > 65536) {
$amount = '65536';
if ((int) $amount > 16777216) { // 16 million
$amount = '16777216';
}
$limit = $this->blRepository->update($budgetLimit, ['amount' => $amount]);

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/*
* InviteUserFormRequest.php
* Copyright (c) 2022 james@firefly-iii.org

View File

@ -99,7 +99,7 @@ class RecurrenceFormRequest extends FormRequest
];
// fill in foreign currency data
if (null !== $this->float('foreign_amount')) {
if (null !== $this->convertFloat('foreign_amount')) {
$return['transactions'][0]['foreign_amount'] = $this->convertString('foreign_amount');
$return['transactions'][0]['foreign_currency_id'] = $this->convertInteger('foreign_currency_id');
}
@ -228,7 +228,7 @@ class RecurrenceFormRequest extends FormRequest
$rules['repetitions'] = 'required|numeric|between:0,254';
}
// if foreign amount, currency must be different.
if (null !== $this->float('foreign_amount')) {
if (null !== $this->convertFloat('foreign_amount')) {
$rules['foreign_currency_id'] = 'exists:transaction_currencies,id|different:transaction_currency_id';
}

View File

@ -0,0 +1,61 @@
<?php
/*
* InvitationMail.php
* Copyright (c) 2022 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class InvitationMail extends Mailable
{
use Queueable, SerializesModels;
public string $invitee;
public string $admin;
public string $url;
public string $host;
/**
* OAuthTokenCreatedMail constructor.
*
* @param string $ipAddress
*/
public function __construct(string $invitee, string $admin, string $url)
{
$this->invitee = $invitee;
$this->admin = $admin;
$this->url = $url;
$this->host = parse_url($url, PHP_URL_HOST);
}
/**
* Build the message.
*
* @return $this
*/
public function build(): self
{
return $this
->markdown('emails.invitation')
->subject((string) trans('email.invite_user_subject'));
}
}

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/*
* InvitedUser.php
* Copyright (c) 2022 james@firefly-iii.org

View File

@ -116,7 +116,7 @@ class EventServiceProvider extends ServiceProvider
],
InvitationCreated::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendInvitationNotification',
//'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationInvite',
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationInvite',
],
// is a Transaction Journal related event.

View File

@ -451,7 +451,7 @@ class UserRepository implements UserRepositoryInterface
public function validateInviteCode(string $code): bool
{
$now = Carbon::now();
$invitee = InvitedUser::where('invite_code', $code)->where('expires', '<=', $now)->where('redeemed', 0)->first();
$invitee = InvitedUser::where('invite_code', $code)->where('expires', '>', $now->format('Y-m-d H:i:s'))->where('redeemed', 0)->first();
return null !== $invitee;
}

View File

@ -219,7 +219,7 @@ trait ConvertsDataTypes
*
* @return float|null
*/
protected function float(string $field): ?float
protected function convertFloat(string $field): ?float
{
$res = $this->get($field);
if (null === $res) {

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/*
* AppendDescriptionToNotes.php
* Copyright (c) 2022 james@firefly-iii.org

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/*
* AppendNotesToDescription.php
* Copyright (c) 2022 james@firefly-iii.org

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/*
* MoveDescriptionToNotes.php
* Copyright (c) 2022 james@firefly-iii.org

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/*
* MoveNotesToDescription.php
* Copyright (c) 2022 james@firefly-iii.org

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

View File

@ -79,6 +79,7 @@ $(function () {
});
function updateBudgetedAmount(e) {
console.log('updateBudgetedAmount');
var input = $(e.currentTarget);
var budgetId = parseInt(input.data('id'));
var budgetLimitId = parseInt(input.data('limit'));
@ -95,7 +96,7 @@ function updateBudgetedAmount(e) {
}).done(function (data) {
input.prop('disabled', false);
input.data('limit', data.id);
// update amount left.
$('.left_span[data-limit="0"][data-id="' + budgetId + '"]').html(data.left_formatted);
if (data.left_per_day > 0) {
@ -113,6 +114,7 @@ function updateBudgetedAmount(e) {
amount: input.val(),
}).done(function (data) {
input.prop('disabled', false);
input.data('limit', data.id);
$('.left_span[data-limit="' + budgetLimitId + '"]').html(data.left_formatted);
if (data.left_per_day > 0) {
$('.left_span[data-limit="' + budgetLimitId + '"]').html(data.left_formatted + '(' + data.left_per_day_formatted + ')');

View File

@ -36,6 +36,10 @@ return [
// invite
'invitation_created_subject' => 'An invitation has been created',
'invitation_created_body' => 'Admin user ":email" created a user invitation which can be used by whoever is behind email address ":invitee". The invite will be valid for 48hrs.',
'invite_user_subject' => 'You\'ve been invited to create a Firefly III account.',
'invitation_introduction' => 'You\'ve been invited to create a Firefly III account on **:host**. Firefly III is a personal, self-hosted, private personal finance manager. All the cool kids are using it.',
'invitation_invited_by' => 'You\'ve been invited by ":admin" and this invitation was sent to ":invitee". That\'s you, right?',
'invitation_url' => 'The invitation is valid for 48 hours and can be redeemed by surfing to [Firefly III](:url). Enjoy!',
// new IP
'login_from_new_ip' => 'New login on Firefly III',

View File

@ -1336,8 +1336,14 @@ return [
'slack_url_label' => 'Slack "incoming webhook" URL',
// profile:
'delete_stuff_header' => 'Delete data',
'permanent_delete_stuff' => 'Be careful with these buttons. Deleting stuff is permanent.',
'purge_data_title' => 'Purge data from Firefly III',
'purge_data_expl' => '"Purging" means "deleting that which is already deleted". In normal circumstances, Firefly III deletes nothing permanently. It just hides it. This can be annoying when you import data from other sources, as removed transactions will still be recognized as possible duplicates. The button below deletes all of these previously "deleted" records FOREVER.',
'delete_stuff_header' => 'Delete and purge data',
'purge_all_data' => 'Purge all deleted records',
'purge_data' => 'Purge data',
'purged_all_records' => 'All deleted records have been purged.',
'delete_data_title' => 'Delete data from Firefly III',
'permanent_delete_stuff' => 'You can delete stuff from Firefly III. Using the buttons below means that your items will be removed from view and hidden. There is no undo-button for this, but the items may remain in the database where you can salvage them if necessary.',
'other_sessions_logged_out' => 'All your other sessions have been logged out.',
'delete_all_budgets' => 'Delete ALL your budgets',
'delete_all_categories' => 'Delete ALL your categories',
@ -2283,6 +2289,7 @@ return [
'admin_notification_check_new_version' => 'A new version is available',
'admin_notification_check_invite_created' => 'A user is invited to Firefly III',
'admin_notification_check_invite_redeemed' => 'A user invitation is redeemed',
'all_invited_users' => 'All invited users',
'save_notification_settings' => 'Save settings',
'notification_settings_saved' => 'The notification settings have been saved',

View File

@ -0,0 +1,8 @@
@component('mail::message')
{{ trans('email.invitation_introduction', ['host' => $host]) }}
{{ trans('email.invitation_invited_by', ['invitee' => $invitee, 'admin' => $admin]) }}
{{ trans('email.invitation_url', ['url' => $url]) }}
@endcomponent

View File

@ -13,7 +13,8 @@
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="{{ route('profile.index') }}#options" aria-controls="home" role="tab" data-toggle="tab">{{ 'options'|_ }}</a>
<a href="{{ route('profile.index') }}#options" aria-controls="home" role="tab"
data-toggle="tab">{{ 'options'|_ }}</a>
</li>
<li role="presentation">
<a href="#cmd" aria-controls="profile" role="tab" data-toggle="tab">{{ 'command_line_token'|_ }}</a>
@ -22,14 +23,16 @@
<a href="#oauth" aria-controls="messages" role="tab" data-toggle="tab">{{ 'oauth'|_ }}</a>
</li>
{% if true == isInternalAuth and true == isInternalIdentity %}
<li role="presentation">
<a href="#mfa" aria-controls="settings" role="tab" data-toggle="tab">{{ 'pref_two_factor_auth'|_ }}</a>
</li>
<li role="presentation">
<a href="#mfa" aria-controls="settings" role="tab"
data-toggle="tab">{{ 'pref_two_factor_auth'|_ }}</a>
</li>
{% endif %}
{% if true == isInternalAuth and true == isInternalIdentity %}
<li role="presentation">
<a href="#delete" aria-controls="settings" role="tab" data-toggle="tab">{{ 'delete_stuff_header'|_ }}</a>
</li>
<li role="presentation">
<a href="#delete" aria-controls="settings" role="tab"
data-toggle="tab">{{ 'delete_stuff_header'|_ }}</a>
</li>
{% endif %}
</ul>
<div class="tab-content">
@ -45,23 +48,24 @@
<div class="col-lg-6">
<ul>
{% if true == isInternalAuth and true == isInternalIdentity %}
<li>
<a href="{{ route('profile.change-email') }}">{{ 'change_your_email'|_ }}</a>
</li>
<li>
<a href="{{ route('profile.change-password') }}">{{ 'change_your_password'|_ }}</a>
</li>
<li>
<a href="{{ route('profile.change-email') }}">{{ 'change_your_email'|_ }}</a>
</li>
<li>
<a href="{{ route('profile.change-password') }}">{{ 'change_your_password'|_ }}</a>
</li>
{% endif %}
<li><a href="{{ route('logout') }}" class="logout-link">{{ 'logout'|_ }}</a></li>
<li><a href="{{ route('logout') }}" class="logout-link">{{ 'logout'|_ }}</a>
</li>
{% if true == isInternalAuth and true == isInternalIdentity %}
<li>
<a href="{{ route('profile.logout-others') }}">{{ 'logout_other_sessions'|_ }}</a>
</li>
<li><a class="text-danger"
href="{{ route('profile.delete-account') }}">{{ 'delete_account'|_ }}</a>
</li>
<li>
<a href="{{ route('profile.logout-others') }}">{{ 'logout_other_sessions'|_ }}</a>
</li>
<li><a class="text-danger"
href="{{ route('profile.delete-account') }}">{{ 'delete_account'|_ }}</a>
</li>
{% endif %}
</ul>
</div>
@ -102,153 +106,181 @@
</div>
{% if true == isInternalAuth and true == isInternalIdentity %}
<!-- MFA -->
<div role="tabpanel" class="tab-pane" id="mfa">
<div class="box box-default">
<div class="box-body">
<p class="text-info">{{ 'pref_two_factor_auth_help'|_ }}</p>
{% if enabled2FA == true %}
<p class="text-info">
{{ trans_choice('firefly.pref_two_factor_backup_code_count', mfaBackupCount) }}
</p>
<!-- MFA -->
<div role="tabpanel" class="tab-pane" id="mfa">
<div class="box box-default">
<div class="box-body">
<p class="text-info">{{ 'pref_two_factor_auth_help'|_ }}</p>
{% if enabled2FA == true %}
<p class="text-info">
{{ trans_choice('firefly.pref_two_factor_backup_code_count', mfaBackupCount) }}
</p>
<div class="btn-group">
<a class="btn btn-info" href="{{ route('profile.code') }}">
<span class="fa fa-recycle"></span>
{{ 'pref_two_factor_auth_reset_code'|_ }}</a>
</div>
<form method="post" action="{{ route('profile.delete-code') }}">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
<input class="btn btn-danger" style="margin-top:20px;" type="submit" name="submit" value="{{ 'pref_two_factor_auth_disable_2fa'|_ }}" />
</form>
<form method="post" action="{{ route('profile.new-backup-codes') }}">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
<input class="btn btn-default" style="margin-top:20px;" type="submit" name="submit" value="{{ 'pref_two_factor_new_backup_codes'|_ }}" />
</form>
{% else %}
<form action="{{ route('profile.enable2FA') }}" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-info"><span
class="fa fa-lock"></span> {{ 'pref_enable_two_factor_auth'|_ }}</button>
</form>
{% endif %}
<div class="btn-group">
<a class="btn btn-info" href="{{ route('profile.code') }}">
<span class="fa fa-recycle"></span>
{{ 'pref_two_factor_auth_reset_code'|_ }}</a>
</div>
<form method="post" action="{{ route('profile.delete-code') }}">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<input class="btn btn-danger" style="margin-top:20px;" type="submit"
name="submit" value="{{ 'pref_two_factor_auth_disable_2fa'|_ }}"/>
</form>
<form method="post" action="{{ route('profile.new-backup-codes') }}">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<input class="btn btn-default" style="margin-top:20px;" type="submit"
name="submit" value="{{ 'pref_two_factor_new_backup_codes'|_ }}"/>
</form>
{% else %}
<form action="{{ route('profile.enable2FA') }}" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-info"><span
class="fa fa-lock"></span> {{ 'pref_enable_two_factor_auth'|_ }}
</button>
</form>
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
<!-- delete stuff -->
<!-- purge stuff -->
<div role="tabpanel" class="tab-pane" id="delete">
<div class="box box-default">
<div class="box-header with-border">
<h3 class="box-title">{{ 'purge_data_title'|_ }}</h3>
</div>
<div class="box-body with-border">
<div class="col-lg-12">
<p class="text-info">
{{ 'purge_data_expl'|_ }}
</p>
<p>
<button type="button"
data-success="{{ trans('firefly.purged_all_records')|escape('html') }}"
data-type="purge" class="confirm btn btn-danger btn-sm"><span
class="fa fa-trash"></span> {{ 'purge_all_data'|_ }}</button>
</p>
</div>
</div>
</div>
<!-- delete stuff -->
<div class="box box-default">
<div class="box-header with-border">
<h3 class="box-title">{{ 'delete_data_title'|_ }}</h3>
</div>
<div class="box-body with-border">
<div class="col-lg-12">
<p class="text-info">
{{ 'permanent_delete_stuff'|_ }}
</p>
<h4>{{ 'financial_control'|_ }}</h4>
<p>
<div class="btn-group">
<button type="button"
data-success="{{ trans('firefly.deleted_all_budgets')|escape('html') }}"
data-type="budgets" class="confirm btn btn-danger btn-sm"><span
data-type="budgets" class="confirm btn btn-warning btn-sm"><span
class="fa fa-pie-chart"></span> {{ 'delete_all_budgets'|_ }}</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_bills')|escape('html') }}"
data-type="bills" class="confirm btn btn-danger btn-sm"><span
data-type="bills" class="confirm btn btn-warning btn-sm"><span
class="fa fa-calendar-o"></span> {{ 'delete_all_bills'|_ }}</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_piggy_banks')|escape('html') }}"
data-type="piggy_banks" class="confirm btn btn-danger btn-sm"><span
data-type="piggy_banks" class="confirm btn btn-warning btn-sm"><span
class="fa fa-bullseye"></span> {{ 'delete_all_piggy_banks'|_ }}</button>
</p>
</div>
<h4>{{ 'automation'|_ }}</h4>
<p>
<div class="btn-group">
<button type="button"
data-success="{{ trans('firefly.deleted_all_rules')|escape('html') }}"
data-type="rules" class="confirm btn btn-danger btn-sm"><span
data-type="rules" class="confirm btn btn-warning btn-sm"><span
class="fa fa-random"></span> {{ 'delete_all_rules'|_ }}</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_recurring')|escape('html') }}"
data-type="recurring" class="confirm btn btn-danger btn-sm"><span
class="fa fa-paint-brush"></span> {{ 'delete_all_recurring'|_ }}</button>
</p>
data-type="recurring" class="confirm btn btn-warning btn-sm"><span
class="fa fa-paint-brush"></span> {{ 'delete_all_recurring'|_ }}
</button>
</div>
<h4>{{ 'classification'|_ }}</h4>
<p>
<div class="btn-group">
<button type="button"
data-success="{{ trans('firefly.deleted_all_categories')|escape('html') }}"
data-type="categories" class="confirm btn btn-danger btn-sm"><span
data-type="categories" class="confirm btn btn-warning btn-sm"><span
class="fa fa-bookmark"></span> {{ 'delete_all_categories'|_ }}</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_tags')|escape('html') }}"
data-type="tags" class="confirm btn btn-danger btn-sm"><span
data-type="tags" class="confirm btn btn-warning btn-sm"><span
class="fa fa-tag"></span> {{ 'delete_all_tags'|_ }}</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_object_groups')|escape('html') }}"
data-type="object_groups" class="confirm btn btn-danger btn-sm"><span
class="fa fa-envelope-o"></span> {{ 'delete_all_object_groups'|_ }}</button>
</p>
data-type="object_groups" class="confirm btn btn-warning btn-sm"><span
class="fa fa-envelope-o"></span> {{ 'delete_all_object_groups'|_ }}
</button>
</div>
<h4>{{ 'accounts'|_ }}</h4>
<p>
<em class="text-danger">{{ 'also_delete_transactions'|_ }}</em>
</p>
<p>
<div class="btn-group">
<button type="button"
data-success="{{ trans('firefly.deleted_all_accounts')|escape('html') }}"
data-type="accounts" class="confirm btn btn-danger btn-sm"><span
data-type="accounts" class="confirm btn btn-warning btn-sm"><span
class="fa fa-credit-card"></span> {{ 'delete_all_accounts'|_ }}</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_asset_accounts')|escape('html') }}"
data-type="asset_accounts" class="confirm btn btn-danger btn-sm"><span
data-type="asset_accounts" class="confirm btn btn-warning btn-sm"><span
class="fa fa-money"></span> {{ 'delete_all_asset_accounts'|_ }}</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_expense_accounts')|escape('html') }}"
data-type="expense_accounts" class="confirm btn btn-danger btn-sm"><span
class="fa fa-shopping-cart"></span> {{ 'delete_all_expense_accounts'|_ }}</button>
</p>
<p>
data-type="expense_accounts" class="confirm btn btn-warning btn-sm"><span
class="fa fa-shopping-cart"></span> {{ 'delete_all_expense_accounts'|_ }}
</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_revenue_accounts')|escape('html') }}"
data-type="revenue_accounts" class="confirm btn btn-danger btn-sm"><span
class="fa fa-download"></span> {{ 'delete_all_revenue_accounts'|_ }}</button>
data-type="revenue_accounts" class="confirm btn btn-warning btn-sm"><span
class="fa fa-download"></span> {{ 'delete_all_revenue_accounts'|_ }}
</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_liabilities')|escape('html') }}"
data-type="liabilities" class="confirm btn btn-danger btn-sm"><span
data-type="liabilities" class="confirm btn btn-warning btn-sm"><span
class="fa fa-ticket"></span> {{ 'delete_all_liabilities'|_ }}</button>
</p>
</div>
<h4>{{ 'transactions'|_ }}</h4>
<p>
<div class="btn-group">
<button type="button"
data-success="{{ trans('firefly.deleted_all_transactions')|escape('html') }}"
data-type="transactions" class="confirm btn btn-danger btn-sm"><span
class="fa fa-exchange"></span> {{ 'delete_all_transactions'|_ }}</button>
data-type="transactions" class="confirm btn btn-warning btn-sm"><span
class="fa fa-exchange"></span> {{ 'delete_all_transactions'|_ }}
</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_withdrawals')|escape('html') }}"
data-type="withdrawals" class="confirm btn btn-danger btn-sm"><span
class="fa fa-long-arrow-left"></span> {{ 'delete_all_withdrawals'|_ }}</button>
data-type="withdrawals" class="confirm btn btn-warning btn-sm"><span
class="fa fa-long-arrow-left"></span> {{ 'delete_all_withdrawals'|_ }}
</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_deposits')|escape('html') }}"
data-type="deposits" class="confirm btn btn-danger btn-sm"><span
class="fa fa-long-arrow-right"></span> {{ 'delete_all_deposits'|_ }}</button>
</p>
<p>
data-type="deposits" class="confirm btn btn-warning btn-sm"><span
class="fa fa-long-arrow-right"></span> {{ 'delete_all_deposits'|_ }}
</button>
<button type="button"
data-success="{{ trans('firefly.deleted_all_transfers')|escape('html') }}"
data-type="transfers" class="confirm btn btn-danger btn-sm"><span
data-type="transfers" class="confirm btn btn-warning btn-sm"><span
class="fa fa-exchange"></span> {{ 'delete_all_transfers'|_ }}</button>
</p>
</div>
</div>
</div>
</div>
@ -267,6 +299,11 @@
$('.confirm').on('click', function (e) {
var link = $(e.currentTarget);
var classes = link.find('i').attr('class');
var url = deleteAPIRoute + '?objects=' + link.data('type');
// different URL for purge route:
if(link.data('type') === 'purge') {
url = '{{ route('api.v1.data.purge') }}';
}
// replace icon with loading thing
link.prop('disabled', true);
@ -275,7 +312,7 @@
// call API:
$.ajax({
method: 'DELETE',
url: deleteAPIRoute + '?objects=' + link.data('type'),
url: url,
}).done(
function () {
// enable button again:

View File

@ -181,6 +181,13 @@ Route::group(
Route::delete('', ['uses' => 'DestroyController@destroy', 'as' => 'destroy']);
}
);
Route::group(
['namespace' => 'FireflyIII\Api\V1\Controllers\Data', 'prefix' => 'v1/data/purge',
'as' => 'api.v1.data.',],
static function () {
Route::delete('', ['uses' => 'PurgeController@purge', 'as' => 'purge']);
}
);
// Bulk update API routes
Route::group(