mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-03-16 02:20:03 +00:00
Compare commits
42 Commits
develop-20
...
v6.5.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aee804940b | ||
|
|
b53a756e5a | ||
|
|
ff5d83eba5 | ||
|
|
4c10c4a26f | ||
|
|
f75817b44d | ||
|
|
9ad5dfd45b | ||
|
|
9e3b8e6232 | ||
|
|
6c3b4a77b5 | ||
|
|
34b5d9fcf6 | ||
|
|
12bbc91dca | ||
|
|
a77ff6a51f | ||
|
|
62eb054c7f | ||
|
|
13536a2f65 | ||
|
|
19dece287e | ||
|
|
897e1f773e | ||
|
|
5788e18d6d | ||
|
|
c8c4816fe8 | ||
|
|
aa57252b11 | ||
|
|
58e4c26a87 | ||
|
|
ac323f11c4 | ||
|
|
a907f9b2f7 | ||
|
|
8abd2a6604 | ||
|
|
0780390ff6 | ||
|
|
b9d1ed28a5 | ||
|
|
b8ebcdf1a8 | ||
|
|
ac8dbbff6c | ||
|
|
fea89c5231 | ||
|
|
654f2ee489 | ||
|
|
5c18624cf9 | ||
|
|
27ba8e842a | ||
|
|
e504ee204a | ||
|
|
a3a332643c | ||
|
|
464a89f305 | ||
|
|
062c2323e3 | ||
|
|
a37f995872 | ||
|
|
a5b6315cb8 | ||
|
|
ff568653c8 | ||
|
|
21447a9b2f | ||
|
|
238bfc819e | ||
|
|
3fab4668fc | ||
|
|
190050d6cf | ||
|
|
45d623e0c1 |
23
.github/security.md
vendored
23
.github/security.md
vendored
@@ -3,13 +3,12 @@
|
||||
Firefly III is an application to manage your personal finances. As such, the developer has adopted this security
|
||||
disclosure and response policy to ensure that critical issues are responsibly handled.
|
||||
|
||||
## Supported Versions
|
||||
## Supported versions
|
||||
|
||||
Only the latest Firefly III release is maintained. Applicable fixes, including security fixes, will not backported to
|
||||
older release branches. Please refer to [releases.md](https://github.com/firefly-iii/firefly-iii/blob/main/releases.md)
|
||||
for details.
|
||||
older release branches. Please refer to [releases.md](https://github.com/firefly-iii/firefly-iii/blob/main/releases.md) for details.
|
||||
|
||||
## Reporting a Vulnerability - Private Disclosure Process
|
||||
## Reporting a vulnerability - private disclosure process
|
||||
|
||||
Security is of the highest importance and all security vulnerabilities or suspected security vulnerabilities should be
|
||||
reported to Firefly III privately, to minimize attacks against current users of Firefly III before they are fixed.
|
||||
@@ -28,7 +27,7 @@ within 3 business days, including a detailed plan to investigate the issue and a
|
||||
the meantime. Do not report non-security-impacting bugs through this channel.
|
||||
Use [GitHub issues](https://github.com/firefly-iii/firefly-iii/issues/new/choose) instead.
|
||||
|
||||
### Proposed Email Content
|
||||
### Proposed email content
|
||||
|
||||
Provide a descriptive subject line and in the body of the email include the following information:
|
||||
|
||||
@@ -47,7 +46,7 @@ Provide a descriptive subject line and in the body of the email include the foll
|
||||
* When you know of or suspect a potential vulnerability on another project that is used by Firefly III. For example
|
||||
Firefly III has a dependency on Docker, MySQL, etc.
|
||||
|
||||
## Patch, Release, and Disclosure
|
||||
## Patch, release, and disclosure
|
||||
|
||||
The Firefly III developer will respond to vulnerability reports as follows:
|
||||
|
||||
@@ -75,7 +74,7 @@ The Firefly III developer will respond to vulnerability reports as follows:
|
||||
8. Once the fix is confirmed, the developer will patch the vulnerability in the next patch or minor release. Upon
|
||||
release of the patched version of Firefly III, we will follow the **Public Disclosure Process**.
|
||||
|
||||
### Public Disclosure Process
|
||||
### Public disclosure process
|
||||
|
||||
The developer publishes a public [advisory](https://github.com/firefly-iii/firefly-iii/security/advisories) to the
|
||||
Firefly III community via GitHub. In most cases, additional communication via Mastodon, Gitter and other channels will
|
||||
@@ -97,6 +96,16 @@ III to provide a hardened Firefly III environment. We will not act on any securi
|
||||
safe defaults. Over time, we will work towards improved safe-by-default configuration, taking into account backwards
|
||||
compatibility.
|
||||
|
||||
## Security scanning through automated means
|
||||
|
||||
There is some additional guidance for security vulnerabilities or suspected security vulnerabilities that have been
|
||||
found with the full or partial support of AI coding agents, large language models and other code-scanning tools. These reports are often not applicable, not actually a vulnerability, or just plain wrong. This takes time away from responding to
|
||||
*actual* security vulnerabilities or suspected security vulnerabilities. If you use automated means to search for security vulnerabilities in the Firefly III code base, please take care to:
|
||||
|
||||
1. manually validate the results before you submit a report,
|
||||
2. explain how the vulnerability can actually be abused by a nefarious third party, and
|
||||
3. try to limit the verbosity of your report.
|
||||
|
||||
## Credits
|
||||
|
||||
This security policy is based on [Harbor](https://github.com/goharbor/harbor)'s security policy.
|
||||
|
||||
@@ -185,6 +185,8 @@ final class DestroyController extends Controller
|
||||
/** @var BudgetRepositoryInterface $budgetRepository */
|
||||
$budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
$budgetRepository->destroyAll();
|
||||
|
||||
$abRepository->cleanup();
|
||||
}
|
||||
|
||||
private function destroyCategories(): void
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* TriggerController.php
|
||||
* Copyright (c) 2025 james@firefly-iii.org
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -22,6 +20,8 @@ declare(strict_types=1);
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Models\Recurrence;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
|
||||
@@ -98,7 +98,7 @@ final class TriggerController extends Controller
|
||||
$enrichment->setUser($rule->user);
|
||||
$transactions = $enrichment->enrich($transactions);
|
||||
|
||||
$paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page'));
|
||||
$paginator = new LengthAwarePaginator($transactions, $count, 31_337, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.rules.test', [$rule->id]).$this->buildParams());
|
||||
|
||||
// resulting list is presented as JSON thing.
|
||||
|
||||
@@ -105,7 +105,7 @@ final class TriggerController extends Controller
|
||||
$enrichment->setUser($group->user);
|
||||
$transactions = $enrichment->enrich($transactions);
|
||||
|
||||
$paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page'));
|
||||
$paginator = new LengthAwarePaginator($transactions, $count, 31_337, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.rule-groups.test', [$group->id]).$this->buildParams());
|
||||
|
||||
// resulting list is presented as JSON thing.
|
||||
|
||||
@@ -32,6 +32,7 @@ use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class DestroyController
|
||||
@@ -71,6 +72,12 @@ final class DestroyController extends Controller
|
||||
if (false === $linkType->editable) {
|
||||
throw new FireflyException('200020: Link type cannot be changed.');
|
||||
}
|
||||
if (false === auth()->user()->hasRole('owner')) {
|
||||
Log::channel('audit')->warning('Non-owner user tries to delete a link type.');
|
||||
|
||||
response()->json([], 401);
|
||||
}
|
||||
|
||||
$this->repository->destroy($linkType);
|
||||
Preferences::mark();
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ final class BasicController extends Controller
|
||||
$this->accountRepository->setUser($user);
|
||||
$this->abRepository->setUser($user);
|
||||
$this->opsRepository->setUser($user);
|
||||
$this->abRepository->cleanup();
|
||||
|
||||
return $next($request);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* BatchController.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
@@ -22,6 +20,8 @@ declare(strict_types=1);
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\System;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* AutocompleteApiRequest.php
|
||||
* Copyright (c) 2025 james@firefly-iii.org
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -22,6 +20,8 @@ declare(strict_types=1);
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests\Autocomplete;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\AggregateFormRequest;
|
||||
|
||||
@@ -28,7 +28,7 @@ use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use FireflyIII\Validation\FireflyValidator;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -69,9 +69,9 @@ class ChartRequest extends FormRequest
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
public function withValidator(FireflyValidator $validator): void
|
||||
{
|
||||
$validator->after(static function (Validator $validator): void {
|
||||
$validator->after(static function (FireflyValidator $validator): void {
|
||||
// validate transaction query data.
|
||||
$data = $validator->getData();
|
||||
if (!array_key_exists('accounts', $data)) {
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace FireflyIII\Api\V1\Requests\Data\Bulk;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use FireflyIII\Validation\FireflyValidator;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -61,9 +61,9 @@ class MoveTransactionsRequest extends FormRequest
|
||||
* Configure the validator instance with special rules for after the basic validation rules.
|
||||
* TODO this is duplicate.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
public function withValidator(FireflyValidator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
$validator->after(function (FireflyValidator $validator): void {
|
||||
// validate start before end only if both are there.
|
||||
$data = $validator->getData();
|
||||
if (array_key_exists('original_account', $data) && array_key_exists('destination_account', $data)) {
|
||||
@@ -75,7 +75,7 @@ class MoveTransactionsRequest extends FormRequest
|
||||
}
|
||||
}
|
||||
|
||||
private function validateMove(Validator $validator): void
|
||||
private function validateMove(FireflyValidator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
|
||||
@@ -33,7 +33,7 @@ use FireflyIII\Rules\UniqueIban;
|
||||
use FireflyIII\Support\Request\AppendsLocationData;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use FireflyIII\Validation\FireflyValidator;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -121,9 +121,9 @@ class UpdateRequest extends FormRequest
|
||||
/**
|
||||
* Configure the validator instance with special rules for after the basic validation rules.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
public function withValidator(FireflyValidator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
$validator->after(function (FireflyValidator $validator): void {
|
||||
// validate start before end only if both are there.
|
||||
$data = $validator->getData();
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class StoreRequest extends FormRequest
|
||||
public function getAll(): array
|
||||
{
|
||||
$active = true;
|
||||
$order = 31337;
|
||||
$order = 31_337;
|
||||
if (null !== $this->get('active')) {
|
||||
$active = $this->boolean('active');
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V1\Requests\Models\TransactionCurrency;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\ApiRequest;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use FireflyIII\Validation\FireflyValidator;
|
||||
|
||||
class CurrencyCodeRequest extends ApiRequest
|
||||
{
|
||||
@@ -34,10 +34,10 @@ class CurrencyCodeRequest extends ApiRequest
|
||||
return ['code' => sprintf('exists:transaction_currencies,code|%s', $this->required)];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
public function withValidator(FireflyValidator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if (!$validator->valid()) {
|
||||
$validator->after(function (FireflyValidator $validator): void {
|
||||
if (0 === count($validator->valid())) {
|
||||
return;
|
||||
}
|
||||
$code = $this->convertString('code', '');
|
||||
|
||||
@@ -29,6 +29,8 @@ use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
@@ -92,6 +94,18 @@ class RemovesLinksToDeletedObjects extends Command
|
||||
if (count($deletedCategories) > 0) {
|
||||
$this->cleanupCategories($deletedCategories);
|
||||
}
|
||||
|
||||
// count and clean up available budgets in currencies with no budget limits.
|
||||
// this is not entirely the place for it but OK.
|
||||
/** @var AvailableBudgetRepositoryInterface $repository */
|
||||
$repository = app(AvailableBudgetRepositoryInterface::class);
|
||||
|
||||
/** @var User $user */
|
||||
foreach (User::get() as $user) {
|
||||
$repository->setUser($user);
|
||||
$repository->cleanup();
|
||||
}
|
||||
|
||||
$this->friendlyNeutral('Validated links to deleted objects.');
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,10 @@ class RollbacksSingleMigration extends Command
|
||||
}
|
||||
|
||||
if ($res) {
|
||||
DB::table('migrations')->where('id', (int) $entry->id)->delete();
|
||||
DB::table('migrations')
|
||||
->where('id', (int) $entry->id)
|
||||
->delete()
|
||||
;
|
||||
$this->friendlyInfo(sprintf('Database migration #%d ("%s") is deleted.', $entry->id, $entry->migration));
|
||||
$this->friendlyLine('');
|
||||
$this->friendlyLine('Try running "php artisan migrate" now.');
|
||||
|
||||
79
app/Console/Commands/SendTestEmail.php
Normal file
79
app/Console/Commands/SendTestEmail.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* SendTestEmail.php
|
||||
* Copyright (c) 2026 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/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use FireflyIII\Events\Test\OwnerTestsNotificationChannel;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SendTestEmail extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
use VerifiesAccessToken;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:send-test-email
|
||||
{--user=1 : The user ID.}
|
||||
{--token= : The user\'s access token.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Send test email';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$user = $this->getUser();
|
||||
if (!$user->hasRole('owner')) {
|
||||
$this->friendlyError((string) trans('firefly.must_be_owner'));
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
/** @var int $lastNotification */
|
||||
$lastNotification = FireflyConfig::get('last_test_notification', 123)->data;
|
||||
if ((time() - $lastNotification) < 120) {
|
||||
$this->friendlyError((string) trans('firefly.test_rate_limited'));
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$owner = new OwnerNotifiable();
|
||||
event(new OwnerTestsNotificationChannel('email', $owner));
|
||||
FireflyConfig::set('last_test_notification', time());
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,10 @@ class RepairsPostgresSequences extends Command
|
||||
$this->friendlyLine(sprintf('Checking the next id sequence for table "%s".', $tableToCheck));
|
||||
|
||||
$highestId = DB::table($tableToCheck)->select(DB::raw('MAX(id)'))->first();
|
||||
$nextId = DB::table($tableToCheck)->select(DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))->first();
|
||||
$nextId = DB::table($tableToCheck)
|
||||
->select(DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))
|
||||
->first()
|
||||
;
|
||||
if (null === $nextId) {
|
||||
$this->friendlyInfo(sprintf('nextval is NULL for table "%s", go to next table.', $tableToCheck));
|
||||
|
||||
@@ -117,7 +120,10 @@ class RepairsPostgresSequences extends Command
|
||||
if ($nextId->nextval < $highestId->max) {
|
||||
DB::select(sprintf('SELECT setval(\'%s_id_seq\', %d)', $tableToCheck, $highestId->max));
|
||||
$highestId = DB::table($tableToCheck)->select(DB::raw('MAX(id)'))->first();
|
||||
$nextId = DB::table($tableToCheck)->select(DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))->first();
|
||||
$nextId = DB::table($tableToCheck)
|
||||
->select(DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))
|
||||
->first()
|
||||
;
|
||||
if ($nextId->nextval > $highestId->max) {
|
||||
$this->friendlyInfo(sprintf('Table "%s" autoincrement corrected.', $tableToCheck));
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ class AccountFactory
|
||||
'user_group_id' => $this->user->user_group_id,
|
||||
'account_type_id' => $type->id,
|
||||
'name' => $data['name'],
|
||||
'order' => 25000,
|
||||
'order' => 25_000,
|
||||
'virtual_balance' => $virtualBalance,
|
||||
'active' => $active,
|
||||
'iban' => $data['iban'],
|
||||
|
||||
@@ -159,7 +159,7 @@ class PiggyBankFactory
|
||||
}
|
||||
|
||||
// no amount set, use previous amount
|
||||
$toBeLinked[$account->id] = ['current_amount' => $toBeLinked[$account->id]['current_amount']];
|
||||
$toBeLinked[$account->id] = ['current_amount' => $toBeLinked[$account->id]['current_amount'] ?? '0'];
|
||||
Log::debug(sprintf('[b] Will link account #%d with amount %s', $account->id, $toBeLinked[$account->id]['current_amount']));
|
||||
|
||||
// create event:
|
||||
@@ -246,7 +246,7 @@ class PiggyBankFactory
|
||||
$piggyBankData['target_date_tz'] = $piggyBankData['target_date']?->format('e');
|
||||
$piggyBankData['account_id'] = null;
|
||||
$piggyBankData['transaction_currency_id'] = $this->getCurrency($data)->id;
|
||||
$piggyBankData['order'] = 131337;
|
||||
$piggyBankData['order'] = 131_337;
|
||||
|
||||
try {
|
||||
/** @var PiggyBank $piggyBank */
|
||||
|
||||
@@ -124,8 +124,16 @@ final class NotificationController extends Controller
|
||||
return redirect(route('settings.notification.index'));
|
||||
}
|
||||
|
||||
$all = $request->all();
|
||||
$channel = $all['test_submit'] ?? '';
|
||||
/** @var int $lastNotification */
|
||||
$lastNotification = FireflyConfig::get('last_test_notification', 123)->data;
|
||||
if ((time() - $lastNotification) < 120) {
|
||||
session()->flash('error', (string) trans('firefly.test_rate_limited'));
|
||||
|
||||
return redirect(route('settings.notification.index'));
|
||||
}
|
||||
|
||||
$all = $request->all();
|
||||
$channel = $all['test_submit'] ?? '';
|
||||
|
||||
switch ($channel) {
|
||||
default:
|
||||
@@ -142,6 +150,7 @@ final class NotificationController extends Controller
|
||||
event(new OwnerTestsNotificationChannel($channel, $owner));
|
||||
session()->flash('success', (string) trans('firefly.notification_test_executed', ['channel' => $channel]));
|
||||
}
|
||||
FireflyConfig::set('last_test_notification', time());
|
||||
|
||||
return redirect(route('settings.notification.index'));
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
|
||||
@@ -31,7 +31,6 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Http\Controllers\CreateStuff;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
@@ -172,7 +172,7 @@ final class BudgetLimitController extends Controller
|
||||
// return empty array:
|
||||
return response()->json([]);
|
||||
}
|
||||
if ((int) $amount > 268435456) { // intentional cast to integer
|
||||
if ((int) $amount > 268_435_456) { // intentional cast to integer
|
||||
$amount = '268435456';
|
||||
}
|
||||
if (-1 === bccomp($amount, '0')) {
|
||||
@@ -232,7 +232,7 @@ final class BudgetLimitController extends Controller
|
||||
if ('' === $amount) {
|
||||
$amount = '0';
|
||||
}
|
||||
if ((int) $amount > 268435456) { // 268 million, intentional integer
|
||||
if ((int) $amount > 268_435_456) { // 268 million, intentional integer
|
||||
$amount = '268435456';
|
||||
}
|
||||
// sanity check on amount:
|
||||
@@ -254,8 +254,8 @@ final class BudgetLimitController extends Controller
|
||||
$amount = bcmul($amount, '-1');
|
||||
}
|
||||
$notes = (string) $request->get('notes');
|
||||
if (strlen($notes) > 32768) {
|
||||
$notes = substr($notes, 0, 32768);
|
||||
if (strlen($notes) > 32_768) {
|
||||
$notes = substr($notes, 0, 32_768);
|
||||
}
|
||||
|
||||
$limit = $this->blRepository->update($budgetLimit, ['amount' => $amount, 'notes' => $notes]);
|
||||
|
||||
@@ -99,6 +99,7 @@ abstract class Controller extends BaseController
|
||||
$logoutUrl = config('firefly.custom_logout_url');
|
||||
|
||||
// overrule v2 layout back to v1.
|
||||
|
||||
if ('true' === request()->get('force_default_layout') && 'v2' === config('view.layout')) {
|
||||
// config('view.layout','v1');
|
||||
Config::set('view.layout', 'v1');
|
||||
|
||||
@@ -160,7 +160,7 @@ final class DebugController extends Controller
|
||||
}
|
||||
if ('' !== $logContent) {
|
||||
// last few lines
|
||||
$logContent = 'Truncated from this point <----|'.substr($logContent, -16384);
|
||||
$logContent = 'Truncated from this point <----|'.substr($logContent, -16_384);
|
||||
}
|
||||
|
||||
return view('debug', ['table' => $table, 'now' => $now, 'logContent' => $logContent]);
|
||||
|
||||
@@ -60,6 +60,7 @@ final class BudgetController extends Controller
|
||||
$this->abRepository = app(AvailableBudgetRepositoryInterface::class);
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$this->repository->cleanupBudgets();
|
||||
$this->abRepository->cleanup();
|
||||
|
||||
return $next($request);
|
||||
});
|
||||
|
||||
@@ -26,13 +26,11 @@ namespace FireflyIII\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Exceptions\Handler;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Contracts\Auth\Factory as Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
|
||||
/**
|
||||
* Class Authenticate
|
||||
@@ -100,7 +98,6 @@ class Authenticate
|
||||
|
||||
throw new FireflyException('This point is generally unreachable.');
|
||||
|
||||
//
|
||||
// exit('five');
|
||||
// foreach ($guards as $guard) {
|
||||
// exit('six');
|
||||
|
||||
@@ -205,7 +205,12 @@ class InterestingMessage
|
||||
|
||||
// send message about newly created transaction group.
|
||||
/** @var null|TransactionGroup $group */
|
||||
$group = auth()->user()->transactionGroups()->with(['transactionJournals', 'transactionJournals.transactionType'])->find((int) $transactionGroupId);
|
||||
$group = auth()
|
||||
->user()
|
||||
->transactionGroups()
|
||||
->with(['transactionJournals', 'transactionJournals.transactionType'])
|
||||
->find((int) $transactionGroupId)
|
||||
;
|
||||
|
||||
if (null === $group) {
|
||||
return;
|
||||
|
||||
@@ -58,8 +58,10 @@ class SecureHeaders
|
||||
"default-src 'none'",
|
||||
"object-src 'none'",
|
||||
sprintf("script-src 'unsafe-eval' 'strict-dynamic' 'nonce-%1s'", $nonce),
|
||||
"style-src 'unsafe-inline' 'self'",
|
||||
// sprintf("style-src 'self' 'nonce-%1s'", $nonce), // safe variant
|
||||
"style-src 'self' 'unsafe-inline'", // unsafe variant
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"font-src 'self' data:",
|
||||
sprintf("connect-src 'self' %s", $trackingScriptSrc),
|
||||
sprintf("img-src 'self' data: 'nonce-%1s' ", $nonce),
|
||||
@@ -71,9 +73,11 @@ class SecureHeaders
|
||||
$csp = [
|
||||
"default-src 'none'",
|
||||
"object-src 'none'",
|
||||
sprintf("script-src 'unsafe-eval' 'strict-dynamic' 'nonce-%1s' https://firefly.sd.internal/_debugbar/assets", $nonce),
|
||||
"style-src 'unsafe-inline' 'self' https://10.0.0.15:5173/",
|
||||
sprintf("script-src 'unsafe-eval' 'strict-dynamic' 'nonce-%1s'", $nonce),
|
||||
// sprintf("style-src 'self' 'nonce-%1s' https://10.0.0.15:5173/", $nonce), // safe variant
|
||||
"style-src 'self' 'unsafe-inline' https://10.0.0.15:5173/", // unsafe variant
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"font-src 'self' data: https://10.0.0.15:5173/",
|
||||
sprintf("connect-src 'self' %s https://10.0.0.15:5173/ wss://10.0.0.15:5173/", $trackingScriptSrc),
|
||||
sprintf("img-src 'self' data: 'nonce-%1s'", $nonce),
|
||||
@@ -89,7 +93,7 @@ class SecureHeaders
|
||||
$customUrl = $logoutUrl;
|
||||
}
|
||||
|
||||
if (null !== $route && 'oauth/authorize' !== $route->uri) {
|
||||
if ('' !== $customUrl && null !== $route && 'oauth/authorize' !== $route->uri) {
|
||||
$csp[] = sprintf("form-action 'self' %s", $customUrl);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class ChecksForNewVersion implements ShouldQueue
|
||||
$now = Carbon::now()->getTimestamp();
|
||||
$diff = $now - $lastCheckTime->data;
|
||||
Log::debug(sprintf('Last check time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff));
|
||||
if ($diff < 604800) {
|
||||
if ($diff < 604_800) {
|
||||
Log::debug(sprintf('Checked for updates less than a week ago (on %s).', Carbon::createFromTimestamp($lastCheckTime->data)->format('Y-m-d H:i:s')));
|
||||
|
||||
return;
|
||||
@@ -121,7 +121,7 @@ class ChecksForNewVersion implements ShouldQueue
|
||||
$now = Carbon::now()->getTimestamp();
|
||||
$diff = $now - $lastCheckTime->data;
|
||||
Log::debug(sprintf('Last warning time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff));
|
||||
if ($diff < (604800 * 4)) {
|
||||
if ($diff < (604_800 * 4)) {
|
||||
Log::debug(sprintf(
|
||||
'Warned about updates less than four weeks ago (on %s).',
|
||||
Carbon::createFromTimestamp($lastCheckTime->data)->format('Y-m-d H:i:s')
|
||||
|
||||
@@ -31,6 +31,9 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property User $user
|
||||
*/
|
||||
class GroupMembership extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -73,7 +73,11 @@ class Preference extends Model
|
||||
|
||||
// try again with ID, but this time don't care about the preferred user_group_id
|
||||
if (null === $preference) {
|
||||
$preference = $user->preferences()->where('id', (int) $value)->first();
|
||||
$preference = $user
|
||||
->preferences()
|
||||
->where('id', (int) $value)
|
||||
->first()
|
||||
;
|
||||
}
|
||||
if (null !== $preference) {
|
||||
/** @var Preference $preference */
|
||||
|
||||
@@ -32,6 +32,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use function Safe\json_decode;
|
||||
use function Safe\json_encode;
|
||||
|
||||
/**
|
||||
* @property TransactionJournal $transactionJournal
|
||||
*/
|
||||
class TransactionJournalMeta extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,8 @@ use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -37,6 +39,7 @@ use Illuminate\Support\Collection;
|
||||
* @method getUser()
|
||||
* @method checkUserGroupAccess(UserRoleEnum $role)
|
||||
* @method setUserGroupById(int $userGroupId)
|
||||
* @method setUser(null|Authenticatable|User $user)
|
||||
*/
|
||||
interface AttachmentRepositoryInterface
|
||||
{
|
||||
|
||||
@@ -69,6 +69,21 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface, U
|
||||
}
|
||||
$exists[$key] = true;
|
||||
}
|
||||
|
||||
// grab budget limit currencies.
|
||||
$currencies = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
|
||||
->where('budgets.user_id', $this->user->id)
|
||||
->distinct()
|
||||
->get(['budget_limits.transaction_currency_id'])
|
||||
->pluck('transaction_currency_id')
|
||||
->toArray()
|
||||
;
|
||||
// delete available budgets without these currencies.
|
||||
$this->user
|
||||
->availableBudgets()
|
||||
->whereNotIn('transaction_currency_id', $currencies)
|
||||
->delete()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -256,8 +256,14 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
|
||||
foreach ($budgets as $budget) {
|
||||
DB::table('budget_transaction')->where('budget_id', $budget->id)->delete();
|
||||
DB::table('budget_transaction_journal')->where('budget_id', $budget->id)->delete();
|
||||
RecurrenceTransactionMeta::where('name', 'budget_id')->where('value', (string) $budget->id)->delete();
|
||||
RuleAction::where('action_type', 'set_budget')->where('action_value', (string) $budget->id)->delete();
|
||||
RecurrenceTransactionMeta::where('name', 'budget_id')
|
||||
->where('value', (string) $budget->id)
|
||||
->delete()
|
||||
;
|
||||
RuleAction::where('action_type', 'set_budget')
|
||||
->where('action_value', (string) $budget->id)
|
||||
->delete()
|
||||
;
|
||||
$budget->delete();
|
||||
}
|
||||
Log::channel('audit')->info('Delete all budgets through destroyAll');
|
||||
|
||||
@@ -86,7 +86,10 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
|
||||
}
|
||||
|
||||
// is being used in accounts:
|
||||
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string) $currency->id))->count();
|
||||
$meta = AccountMeta::where('name', 'currency_id')
|
||||
->where('data', json_encode((string) $currency->id))
|
||||
->count()
|
||||
;
|
||||
if ($meta > 0) {
|
||||
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
|
||||
|
||||
@@ -94,7 +97,10 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
|
||||
}
|
||||
|
||||
// second search using integer check.
|
||||
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((int) $currency->id))->count();
|
||||
$meta = AccountMeta::where('name', 'currency_id')
|
||||
->where('data', json_encode((int) $currency->id))
|
||||
->count()
|
||||
;
|
||||
if ($meta > 0) {
|
||||
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
|
||||
|
||||
|
||||
@@ -116,8 +116,15 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface, UserGroupInterf
|
||||
$links = $linkType->transactionJournalLinks()->get(['source_id', 'destination_id']);
|
||||
$sources = $links->pluck('source_id')->toArray();
|
||||
$destinations = $links->pluck('destination_id')->toArray();
|
||||
$joined = array_unique(array_merge($sources, $destinations));
|
||||
|
||||
return array_unique(array_merge($sources, $destinations));
|
||||
return $this->user
|
||||
->transactionJournals()
|
||||
->whereIn('id', $joined)
|
||||
->get(['transaction_journals.id'])
|
||||
->pluck('id')
|
||||
->toArray()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,10 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Repositories\PeriodStatistic;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\PeriodStatistic;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@@ -38,7 +41,7 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U
|
||||
{
|
||||
use UserGroupTrait;
|
||||
|
||||
public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection
|
||||
public function allInRangeForModel(Account|Category|Tag $model, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
return $model->primaryPeriodStatistics()->where('start', '>=', $start)->where('end', '<=', $end)->get();
|
||||
}
|
||||
|
||||
@@ -25,13 +25,16 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Repositories\PeriodStatistic;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\PeriodStatistic;
|
||||
use FireflyIII\Models\Tag;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
interface PeriodStatisticRepositoryInterface
|
||||
{
|
||||
public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection;
|
||||
public function allInRangeForModel(Account|Category|Tag $model, Carbon $start, Carbon $end): Collection;
|
||||
|
||||
public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection;
|
||||
|
||||
@@ -41,6 +44,8 @@ interface PeriodStatisticRepositoryInterface
|
||||
|
||||
public function deleteStatisticsForPrefix(string $prefix, Collection $dates): void;
|
||||
|
||||
public function deleteStatisticsForType(string $class, Collection $objects, Collection $dates): void;
|
||||
|
||||
public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection;
|
||||
|
||||
public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection;
|
||||
|
||||
@@ -327,7 +327,7 @@ class RuleRepository implements RuleRepositoryInterface, UserGroupInterface
|
||||
$rule->userGroup()->associate($this->user->userGroup);
|
||||
|
||||
$rule->rule_group_id = $ruleGroup->id;
|
||||
$rule->order = 31337;
|
||||
$rule->order = 31_337;
|
||||
$rule->active = array_key_exists('active', $data) ? $data['active'] : true;
|
||||
$rule->strict = array_key_exists('strict', $data) ? $data['strict'] : false;
|
||||
$rule->stop_processing = array_key_exists('stop_processing', $data) ? $data['stop_processing'] : false;
|
||||
|
||||
@@ -382,7 +382,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
'user_group_id' => $this->user->user_group_id,
|
||||
'title' => $data['title'],
|
||||
'description' => $data['description'],
|
||||
'order' => 31337,
|
||||
'order' => 31_337,
|
||||
'active' => array_key_exists('active', $data) ? $data['active'] : true,
|
||||
]);
|
||||
$newRuleGroup->save();
|
||||
|
||||
@@ -41,7 +41,10 @@ class UserGroupAccount implements BinderInterface
|
||||
if (auth()->check()) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$account = Account::where('id', (int) $value)->where('user_group_id', $user->user_group_id)->first();
|
||||
$account = Account::where('id', (int) $value)
|
||||
->where('user_group_id', $user->user_group_id)
|
||||
->first()
|
||||
;
|
||||
if (null !== $account) {
|
||||
return $account;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ class UserGroupBill implements BinderInterface
|
||||
if (auth()->check()) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$currency = Bill::where('id', (int) $value)->where('user_group_id', $user->user_group_id)->first();
|
||||
$currency = Bill::where('id', (int) $value)
|
||||
->where('user_group_id', $user->user_group_id)
|
||||
->first()
|
||||
;
|
||||
if (null !== $currency) {
|
||||
return $currency;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,10 @@ class UserGroupExchangeRate implements BinderInterface
|
||||
if (auth()->check()) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$rate = CurrencyExchangeRate::where('id', (int) $value)->where('user_group_id', $user->user_group_id)->first();
|
||||
$rate = CurrencyExchangeRate::where('id', (int) $value)
|
||||
->where('user_group_id', $user->user_group_id)
|
||||
->first()
|
||||
;
|
||||
if (null !== $rate) {
|
||||
return $rate;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,10 @@ class UserGroupTransaction implements BinderInterface
|
||||
if (auth()->check()) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$group = TransactionGroup::where('id', (int) $value)->where('user_group_id', $user->user_group_id)->first();
|
||||
$group = TransactionGroup::where('id', (int) $value)
|
||||
->where('user_group_id', $user->user_group_id)
|
||||
->first()
|
||||
;
|
||||
if (null !== $group) {
|
||||
return $group;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ abstract class AbstractCronjob
|
||||
public bool $jobFired = false;
|
||||
public bool $jobSucceeded = false;
|
||||
public ?string $message = null;
|
||||
public int $timeBetweenRuns = 43200;
|
||||
public int $timeBetweenRuns = 43_200;
|
||||
protected Carbon $date;
|
||||
protected bool $force = false;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class AutoBudgetCronjob extends AbstractCronjob
|
||||
Log::info('Auto budget cron-job has never fired before.');
|
||||
}
|
||||
// less than half a day ago:
|
||||
if ($lastTime > 0 && $diff <= 43200) {
|
||||
if ($lastTime > 0 && $diff <= 43_200) {
|
||||
Log::info(sprintf('It has been %s since the auto budget cron-job has fired.', $diffForHumans));
|
||||
if (false === $this->force) {
|
||||
Log::info('The auto budget cron-job will not fire now.');
|
||||
@@ -58,7 +58,7 @@ class AutoBudgetCronjob extends AbstractCronjob
|
||||
Log::info('Execution of the auto budget cron-job has been FORCED.');
|
||||
}
|
||||
|
||||
if ($lastTime > 0 && $diff > 43200) {
|
||||
if ($lastTime > 0 && $diff > 43_200) {
|
||||
Log::info(sprintf('It has been %s since the auto budget cron-job has fired. It will fire now!', $diffForHumans));
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class BillWarningCronjob extends AbstractCronjob
|
||||
Log::info('The bill notification cron-job has never fired before.');
|
||||
}
|
||||
// less than half a day ago:
|
||||
if ($lastTime > 0 && $diff <= 43200) {
|
||||
if ($lastTime > 0 && $diff <= 43_200) {
|
||||
Log::info(sprintf('It has been %s since the bill notification cron-job has fired.', $diffForHumans));
|
||||
if (false === $this->force) {
|
||||
Log::info('The cron-job will not fire now.');
|
||||
@@ -69,7 +69,7 @@ class BillWarningCronjob extends AbstractCronjob
|
||||
Log::info('Execution of the bill notification cron-job has been FORCED.');
|
||||
}
|
||||
|
||||
if ($lastTime > 0 && $diff > 43200) {
|
||||
if ($lastTime > 0 && $diff > 43_200) {
|
||||
Log::info(sprintf('It has been %s since the bill notification cron-job has fired. It will fire now!', $diffForHumans));
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class ExchangeRatesCronjob extends AbstractCronjob
|
||||
Log::info('Exchange rates cron-job has never fired before.');
|
||||
}
|
||||
// less than half a day ago:
|
||||
if ($lastTime > 0 && $diff <= 43200) {
|
||||
if ($lastTime > 0 && $diff <= 43_200) {
|
||||
Log::info(sprintf('It has been %s since the exchange rates cron-job has fired.', $diffForHumans));
|
||||
if (false === $this->force) {
|
||||
Log::info('The exchange rates cron-job will not fire now.');
|
||||
@@ -59,7 +59,7 @@ class ExchangeRatesCronjob extends AbstractCronjob
|
||||
Log::info('Execution of the exchange rates cron-job has been FORCED.');
|
||||
}
|
||||
|
||||
if ($lastTime > 0 && $diff > 43200) {
|
||||
if ($lastTime > 0 && $diff > 43_200) {
|
||||
Log::info(sprintf('It has been %s since the exchange rates cron-job has fired. It will fire now!', $diffForHumans));
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class RecurringCronjob extends AbstractCronjob
|
||||
Log::info('Recurring transactions cron-job has never fired before.');
|
||||
}
|
||||
// less than half a day ago:
|
||||
if ($lastTime > 0 && $diff <= 43200) {
|
||||
if ($lastTime > 0 && $diff <= 43_200) {
|
||||
Log::info(sprintf('It has been "%s" since the recurring transactions cron-job has fired.', $diffForHumans));
|
||||
if (false === $this->force) {
|
||||
Log::info('The cron-job will not fire now.');
|
||||
@@ -68,7 +68,7 @@ class RecurringCronjob extends AbstractCronjob
|
||||
Log::info('Execution of the recurring transaction cron-job has been FORCED.');
|
||||
}
|
||||
|
||||
if ($lastTime > 0 && $diff > 43200) {
|
||||
if ($lastTime > 0 && $diff > 43_200) {
|
||||
Log::info(sprintf('It has been "%s" since the recurring transactions cron-job has fired. It will fire now!', $diffForHumans));
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class UpdateCheckCronjob extends AbstractCronjob
|
||||
$now = Carbon::now()->getTimestamp();
|
||||
$diff = $now - $lastCheckTime->data;
|
||||
Log::debug(sprintf('Last check time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff));
|
||||
if ($diff < 604800 && false === $this->force) {
|
||||
if ($diff < 604_800 && false === $this->force) {
|
||||
// get stuff from job:
|
||||
$this->jobFired = false;
|
||||
$this->jobErrored = false;
|
||||
|
||||
@@ -221,7 +221,7 @@ trait GetConfigurationData
|
||||
|
||||
return;
|
||||
}
|
||||
if (($now - $lastTime) > 129600) {
|
||||
if (($now - $lastTime) > 129_600) {
|
||||
request()->session()->flash('warning', trans('firefly.recurring_cron_long_ago'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,8 @@ trait UserNavigation
|
||||
|
||||
return redirect(route('index'));
|
||||
}
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $transaction->transactionJournal;
|
||||
|
||||
/** @var null|Transaction $other */
|
||||
|
||||
@@ -111,6 +111,7 @@ class AvailableBudgetCalculator
|
||||
$viewRange = Preferences::getForUser($this->user, 'viewRange', '1M')->data;
|
||||
$viewRange = !is_string($viewRange) ? '1M' : $viewRange;
|
||||
$this->viewRange = $this->correctViewRange($viewRange);
|
||||
$this->abRepository->cleanup();
|
||||
}
|
||||
|
||||
private function correctViewRange(string $viewRange): string
|
||||
|
||||
@@ -27,8 +27,10 @@ namespace FireflyIII\Support\Request;
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidDateException;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -36,6 +38,8 @@ use function Safe\preg_replace;
|
||||
|
||||
/**
|
||||
* Trait ConvertsDataTypes
|
||||
*
|
||||
* @method UserGroup validateUserGroup(Request $request)
|
||||
*/
|
||||
trait ConvertsDataTypes
|
||||
{
|
||||
|
||||
@@ -48,7 +48,10 @@ class AppendNotes implements ActionInterface
|
||||
public function actOnArray(array $journal): bool
|
||||
{
|
||||
$this->refreshNotes($journal);
|
||||
$dbNote = Note::where('noteable_id', (int) $journal['transaction_journal_id'])->where('noteable_type', TransactionJournal::class)->first(['notes.*']);
|
||||
$dbNote = Note::where('noteable_id', (int) $journal['transaction_journal_id'])
|
||||
->where('noteable_type', TransactionJournal::class)
|
||||
->first(['notes.*'])
|
||||
;
|
||||
if (null === $dbNote) {
|
||||
$dbNote = new Note();
|
||||
$dbNote->noteable_id = (int) $journal['transaction_journal_id'];
|
||||
|
||||
@@ -44,7 +44,10 @@ class PrependNotes implements ActionInterface
|
||||
|
||||
public function actOnArray(array $journal): bool
|
||||
{
|
||||
$dbNote = Note::where('noteable_id', (int) $journal['transaction_journal_id'])->where('noteable_type', TransactionJournal::class)->first(['notes.*']);
|
||||
$dbNote = Note::where('noteable_id', (int) $journal['transaction_journal_id'])
|
||||
->where('noteable_type', TransactionJournal::class)
|
||||
->first(['notes.*'])
|
||||
;
|
||||
if (null === $dbNote) {
|
||||
$dbNote = new Note();
|
||||
$dbNote->noteable_id = (int) $journal['transaction_journal_id'];
|
||||
|
||||
@@ -255,7 +255,7 @@ class SearchRuleEngine implements RuleEngineInterface
|
||||
$searchEngine = app(SearchInterface::class);
|
||||
$searchEngine->setUser($this->user);
|
||||
$searchEngine->setPage(1);
|
||||
$searchEngine->setLimit(31337);
|
||||
$searchEngine->setLimit(31_337);
|
||||
|
||||
foreach ($searchArray as $type => $value) {
|
||||
$searchEngine->parseQuery(sprintf('%s:%s', $type, $value));
|
||||
@@ -345,7 +345,7 @@ class SearchRuleEngine implements RuleEngineInterface
|
||||
$searchEngine = app(SearchInterface::class);
|
||||
$searchEngine->setUser($this->user);
|
||||
$searchEngine->setPage(1);
|
||||
$searchEngine->setLimit(31337);
|
||||
$searchEngine->setLimit(31_337);
|
||||
$searchEngine->setDate($date);
|
||||
Log::debug('Search array', $searchArray);
|
||||
|
||||
|
||||
@@ -106,9 +106,6 @@ class User extends Authenticatable
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to accounts.
|
||||
*/
|
||||
public function accounts(): HasMany
|
||||
{
|
||||
return $this->hasMany(Account::class);
|
||||
|
||||
@@ -72,7 +72,7 @@ trait ValidatesAutoBudgetRequest
|
||||
$validator->errors()->add('auto_budget_amount', (string) trans('validation.require_currency_info'));
|
||||
}
|
||||
// too big amount
|
||||
if ((int) $amount > 268435456) {
|
||||
if ((int) $amount > 268_435_456) {
|
||||
$validator->errors()->add('auto_budget_amount', (string) trans('validation.amount_required_for_auto_budget'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +355,11 @@ trait RecurrenceValidation
|
||||
}
|
||||
if (array_key_exists('id', $transaction)) { // don't matter if $idsMandatory
|
||||
Log::debug('Array has ID.');
|
||||
$idCount = $recurrence->recurrenceTransactions()->where('recurrences_transactions.id', (int) $transaction['id'])->count();
|
||||
$idCount = $recurrence
|
||||
->recurrenceTransactions()
|
||||
->where('recurrences_transactions.id', (int) $transaction['id'])
|
||||
->count()
|
||||
;
|
||||
if (0 === $idCount) {
|
||||
Log::debug('ID does not exist or no match. Count another unmatched ID.');
|
||||
++$unmatchedIds;
|
||||
|
||||
33
changelog.md
33
changelog.md
@@ -3,37 +3,36 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## x.x.x - 20xx-xx-xx
|
||||
## v6.5.5 - 2026-03-15
|
||||
|
||||
<!-- summary: If you can read this I forgot to update the summary! -->
|
||||
<!-- summary: This release takes note of some security issues, and fixes interesting bugs. -->
|
||||
|
||||
### Added
|
||||
|
||||
- Initial release.
|
||||
- Add the ability for Fosstodon posts to read a summary of the changelog.
|
||||
|
||||
### Changed
|
||||
|
||||
- Initial release.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Initial release.
|
||||
|
||||
### Removed
|
||||
|
||||
- Initial release.
|
||||
- Lots of code cleanup and small quality issues fixed.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Initial release.
|
||||
- [Issue 11803](https://github.com/firefly-iii/firefly-iii/issues/11803) (Monthly Left budget not correct) reported by @fabienfitoussi
|
||||
- [Issue 11641](https://github.com/firefly-iii/firefly-iii/issues/11641) (Annual budget “Remaining” resets in subsequent months) reported by @maxwell5555
|
||||
- [Discussion 11879](https://github.com/orgs/firefly-iii/discussions/11879) (Searching for accounts should include inactive accounts?) started by @b-ryan
|
||||
- [Issue 11916](https://github.com/firefly-iii/firefly-iii/issues/11916) (Balance is not recalculated when multiple transactions are selected and then deleted) reported by @elp3dr0
|
||||
- [Discussion 11936](https://github.com/orgs/firefly-iii/discussions/11936) (Links in emails don't link to correct domain) started by @SamLMB
|
||||
- [Issue 11944](https://github.com/firefly-iii/firefly-iii/issues/11944) (Stale available_budgets rows prevent disabling a currency after switching default) reported by @k-leveller
|
||||
|
||||
### Security
|
||||
|
||||
- Enum thing in rule engine, reported by basically all users with preview access to the new Claude code security scanner
|
||||
- Credits go to Igor for finding some interesting issues in Firefly III. They have been fixed.
|
||||
|
||||
### API
|
||||
> [!NOTE]
|
||||
> As AI-code scanning tools like Claude and Co-Pilot get more advanced, many (new) issues are being reported through (semi-)automated means. I have updated [the security policy](https://github.com/firefly-iii/firefly-iii/security/policy) to reflect my stance on this. The following security related issues no longer need reporting:
|
||||
|
||||
- Initial release.
|
||||
- It is possible to point webhooks to private or internal IPs.
|
||||
- You can see all transaction link types.
|
||||
- `unsafe-inline` is allowed for CSS, which means you can overrule the layout if you manage to get CSS on the page.
|
||||
|
||||
## v6.5.4 - 2026-03-06
|
||||
|
||||
|
||||
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => (bool)envDefaultWhenEmpty(env('USE_RUNNING_BALANCE'), true), // this is only the default value, is not used.
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2026-03-13',
|
||||
'build_time' => 1773370359,
|
||||
'version' => '6.5.5',
|
||||
'build_time' => 1773558152,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -4135,9 +4135,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.10.7",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.7.tgz",
|
||||
"integrity": "sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw==",
|
||||
"version": "2.10.8",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz",
|
||||
"integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -4587,9 +4587,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001778",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz",
|
||||
"integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==",
|
||||
"version": "1.0.30001779",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz",
|
||||
"integrity": "sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
||||
@@ -22,6 +22,267 @@
|
||||
.no-margin-pagination {padding-bottom:0;padding-top:0;}
|
||||
.no-margin-pagination ul.pagination {margin:0 !important;}
|
||||
|
||||
/* date range */
|
||||
.date-range-holder {color:#fff;padding: 15px;display: block;line-height: 20px;}
|
||||
.dropdown-holder {cursor:default;color:#fff;padding: 15px;display: block;line-height: 20px;}
|
||||
|
||||
/** new classes because of CSP */
|
||||
.wide-chart {width:100%;height:400px;}
|
||||
.low-chart {width:100%;height:200px;}
|
||||
.medium-chart {width:100%;height:250px;}
|
||||
.center-chart {width:100%;margin:0 auto;}
|
||||
.map-size {width:100%;height:300px;}
|
||||
|
||||
/* install box border */
|
||||
.install-box-border {border:1px #ddd solid;}
|
||||
.install-box-title {font-family: monospace;font-size:16pt;}
|
||||
|
||||
/* some borders and lines */
|
||||
.top-light-border {border-top:1px #aaa solid;}
|
||||
.top-dark-border {border-top:1px #777 solid;}
|
||||
.bottom-light-border {border-bottom:1px #aaa solid;}
|
||||
|
||||
/* misc */
|
||||
.calendar-display {max-width: 400px;margin: 0 auto;}
|
||||
.empty-high-block {height:400px;}
|
||||
.fw-normal {font-weight: normal;}
|
||||
.position-relative {position: relative;}
|
||||
.pointer {cursor:pointer;}
|
||||
.big-line {line-height:1.7em;}
|
||||
.bold-label {font-weight: normal;font-size:0.9em;}
|
||||
|
||||
/** tables **/
|
||||
th.five {width:5%;}
|
||||
th.ten {width:10%;}
|
||||
th.fifteen td.fifteen {width:15%;}
|
||||
td.twenty th.twenty {width:25%;}
|
||||
td.onetwenty {width:120px;}
|
||||
td.quarter th.quarter {width:25%;}
|
||||
td.thirty th.thirty {width:30%;}
|
||||
td.third th.third {width:33%;}
|
||||
td.forty th.forty {width:40%;}
|
||||
td.half th.half {width:50%;}
|
||||
td.sixty-six th.sixty-six {width:66%;}
|
||||
td.twenty-px {width:20px;}
|
||||
td.forty-px {width:40px;}
|
||||
td.sixty-px {width:60px;}
|
||||
td.hundred-px {width:100px;}
|
||||
|
||||
/** width for progress bar **/
|
||||
.width-0 {width:0;}
|
||||
.width-1 {width:1%;}
|
||||
.width-2 {width:2%;}
|
||||
.width-3 {width:3%;}
|
||||
.width-4 {width:4%;}
|
||||
.width-5 {width:5%;}
|
||||
.width-6 {width:6%;}
|
||||
.width-7 {width:7%;}
|
||||
.width-8 {width:8%;}
|
||||
.width-9 {width:9%;}
|
||||
.width-10 {width:10%;}
|
||||
.width-11 {width:11%;}
|
||||
.width-12 {width:12%;}
|
||||
.width-13 {width:13%;}
|
||||
.width-14 {width:14%;}
|
||||
.width-15 {width:15%;}
|
||||
.width-16 {width:16%;}
|
||||
.width-17 {width:17%;}
|
||||
.width-18 {width:18%;}
|
||||
.width-19 {width:19%;}
|
||||
.width-20 {width:20%;}
|
||||
.width-21 {width:21%;}
|
||||
.width-22 {width:22%;}
|
||||
.width-23 {width:23%;}
|
||||
.width-24 {width:24%;}
|
||||
.width-25 {width:25%;}
|
||||
.width-26 {width:26%;}
|
||||
.width-27 {width:27%;}
|
||||
.width-28 {width:28%;}
|
||||
.width-29 {width:29%;}
|
||||
.width-30 {width:30%;}
|
||||
.width-31 {width:31%;}
|
||||
.width-32 {width:32%;}
|
||||
.width-33 {width:33%;}
|
||||
.width-34 {width:34%;}
|
||||
.width-35 {width:35%;}
|
||||
.width-36 {width:36%;}
|
||||
.width-37 {width:37%;}
|
||||
.width-38 {width:38%;}
|
||||
.width-39 {width:39%;}
|
||||
.width-40 {width:40%;}
|
||||
.width-41 {width:41%;}
|
||||
.width-42 {width:42%;}
|
||||
.width-43 {width:43%;}
|
||||
.width-44 {width:44%;}
|
||||
.width-45 {width:45%;}
|
||||
.width-46 {width:46%;}
|
||||
.width-47 {width:47%;}
|
||||
.width-48 {width:48%;}
|
||||
.width-49 {width:49%;}
|
||||
.width-50 {width:50%;}
|
||||
.width-51 {width:51%;}
|
||||
.width-52 {width:52%;}
|
||||
.width-53 {width:53%;}
|
||||
.width-54 {width:54%;}
|
||||
.width-55 {width:55%;}
|
||||
.width-56 {width:56%;}
|
||||
.width-57 {width:57%;}
|
||||
.width-58 {width:58%;}
|
||||
.width-59 {width:59%;}
|
||||
.width-60 {width:60%;}
|
||||
.width-61 {width:61%;}
|
||||
.width-62 {width:62%;}
|
||||
.width-63 {width:63%;}
|
||||
.width-64 {width:64%;}
|
||||
.width-65 {width:65%;}
|
||||
.width-66 {width:66%;}
|
||||
.width-67 {width:67%;}
|
||||
.width-68 {width:68%;}
|
||||
.width-69 {width:69%;}
|
||||
.width-70 {width:70%;}
|
||||
.width-71 {width:71%;}
|
||||
.width-72 {width:72%;}
|
||||
.width-73 {width:73%;}
|
||||
.width-74 {width:74%;}
|
||||
.width-75 {width:75%;}
|
||||
.width-76 {width:76%;}
|
||||
.width-77 {width:77%;}
|
||||
.width-78 {width:78%;}
|
||||
.width-79 {width:79%;}
|
||||
.width-80 {width:80%;}
|
||||
.width-81 {width:81%;}
|
||||
.width-82 {width:82%;}
|
||||
.width-83 {width:83%;}
|
||||
.width-84 {width:84%;}
|
||||
.width-85 {width:85%;}
|
||||
.width-86 {width:86%;}
|
||||
.width-87 {width:87%;}
|
||||
.width-88 {width:88%;}
|
||||
.width-89 {width:89%;}
|
||||
.width-90 {width:90%;}
|
||||
.width-91 {width:91%;}
|
||||
.width-92 {width:92%;}
|
||||
.width-93 {width:93%;}
|
||||
.width-94 {width:94%;}
|
||||
.width-95 {width:95%;}
|
||||
.width-96 {width:96%;}
|
||||
.width-97 {width:97%;}
|
||||
.width-98 {width:98%;}
|
||||
.width-99 {width:99%;}
|
||||
.width-100 {width:100%;}
|
||||
|
||||
|
||||
/* spacing and padding */
|
||||
.m-0 { margin:0!important; }
|
||||
.m-1 { margin:.25rem!important; }
|
||||
.m-2 { margin:.5rem !important; }
|
||||
.m-3 { margin:1rem !important; }
|
||||
.m-4 { margin:1.5rem !important; }
|
||||
.m-5 { margin:3rem!important; }
|
||||
|
||||
.mt-0 { margin-top:0!important; }
|
||||
.mr-0 { margin-right:0!important; }
|
||||
.mb-0 { margin-bottom:0!important; }
|
||||
.ml-0 { margin-left:0!important; }
|
||||
.mx-0 { margin-left:0 !important;margin-right:0!important; }
|
||||
.my-0 { margin-top:0 !important;margin-bottom:0!important; }
|
||||
|
||||
.mt-1 { margin-top:.25rem!important; }
|
||||
.mr-1 { margin-right:.25rem!important; }
|
||||
.mb-1 { margin-bottom:.25rem!important; }
|
||||
.ml-1 { margin-left:.25rem!important; }
|
||||
.mx-1 { margin-left:.25rem!important;margin-right:.25rem!important; }
|
||||
.my-1 { margin-top:.25rem!important;margin-bottom:.25rem!important; }
|
||||
|
||||
.mt-2 { margin-top:.5rem!important; }
|
||||
.mr-2 { margin-right:.5rem!important; }
|
||||
.mb-2 { margin-bottom:.5rem!important; }
|
||||
.ml-2 { margin-left:.5rem!important; }
|
||||
.mx-2 { margin-right:.5rem!important;margin-left:.5rem!important; }
|
||||
.my-2 { margin-top:.5rem!important;margin-bottom:.5rem!important; }
|
||||
|
||||
.mt-3 { margin-top:1rem!important; }
|
||||
.mr-3 { margin-right:1rem!important; }
|
||||
.mb-3 { margin-bottom:1rem!important; }
|
||||
.ml-3 { margin-left:1rem!important; }
|
||||
.mx-3 { margin-right:1rem!important;margin-left:1rem!important; }
|
||||
.my-3 { margin-bottom:1rem!important;margin-top:1rem!important; }
|
||||
|
||||
.mt-4 { margin-top:1.5rem!important; }
|
||||
.mr-4 { margin-right:1.5rem!important; }
|
||||
.mb-4 { margin-bottom:1.5rem!important; }
|
||||
.ml-4 { margin-left:1.5rem!important; }
|
||||
.mx-4 { margin-right:1.5rem!important;margin-left:1.5rem!important; }
|
||||
.my-4 { margin-top:1.5rem!important;margin-bottom:1.5rem!important; }
|
||||
|
||||
.mt-5 { margin-top:3rem!important; }
|
||||
.mr-5 { margin-right:3rem!important; }
|
||||
.mb-5 { margin-bottom:3rem!important; }
|
||||
.ml-5 { margin-left:3rem!important; }
|
||||
.mx-5 { margin-right:3rem!important;margin-left:3rem!important; }
|
||||
.my-5 { margin-top:3rem!important;margin-bottom:3rem!important; }
|
||||
|
||||
.mt-auto { margin-top:auto!important; }
|
||||
.mr-auto { margin-right:auto!important; }
|
||||
.mb-auto { margin-bottom:auto!important; }
|
||||
.ml-auto { margin-left:auto!important; }
|
||||
.mx-auto { margin-right:auto!important;margin-left:auto!important; }
|
||||
.my-auto { margin-bottom:auto!important;margin-top:auto!important; }
|
||||
|
||||
.p-0 { padding:0!important; }
|
||||
.p-1 { padding:.25rem!important; }
|
||||
.p-2 { padding:.5rem!important; }
|
||||
.p-3 { padding:1rem!important; }
|
||||
.p-4 { padding:1.5rem!important; }
|
||||
.p-5 { padding:3rem!important; }
|
||||
|
||||
.pt-0 { padding-top:0!important; }
|
||||
.pr-0 { padding-right:0!important; }
|
||||
.pb-0 { padding-bottom:0!important; }
|
||||
.pl-0 { padding-left:0!important; }
|
||||
.px-0 { padding-left:0!important;padding-right:0!important; }
|
||||
.py-0 { padding-top:0!important;padding-bottom:0!important; }
|
||||
|
||||
.pt-1 { padding-top:.25rem!important; }
|
||||
.pr-1 { padding-right:.25rem!important; }
|
||||
.pb-1 { padding-bottom:.25rem!important; }
|
||||
.pl-1 { padding-left:.25rem!important; }
|
||||
.px-1 { padding-left:.25rem!important;padding-right:.25rem!important; }
|
||||
.py-1 { padding-top:.25rem!important;padding-bottom:.25rem!important; }
|
||||
|
||||
.pt-2 { padding-top:.5rem!important; }
|
||||
.pr-2 { padding-right:.5rem!important; }
|
||||
.pb-2 { padding-bottom:.5rem!important; }
|
||||
.pl-2 { padding-left:.5rem!important; }
|
||||
.px-2 { padding-right:.5rem!important;padding-left:.5rem!important; }
|
||||
.py-2 { padding-top:.5rem!important;padding-bottom:.5rem!important; }
|
||||
|
||||
.pt-3 { padding-top:1rem!important; }
|
||||
.pr-3 { padding-right:1rem!important; }
|
||||
.pb-3 { padding-bottom:1rem!important; }
|
||||
.pl-3 { padding-left:1rem!important; }
|
||||
.py-3 { padding-bottom:1rem!important;padding-top:1rem!important; }
|
||||
.px-3 { padding-right:1rem!important;padding-left:1rem!important; }
|
||||
|
||||
.pt-4 { padding-top:1.5rem!important; }
|
||||
.pr-4 { padding-right:1.5rem!important; }
|
||||
.pb-4 { padding-bottom:1.5rem!important; }
|
||||
.pl-4 { padding-left:1.5rem!important; }
|
||||
.px-4 { padding-right:1.5rem!important;padding-left:1.5rem!important; }
|
||||
.py-4 { padding-top:1.5rem!important;padding-bottom:1.5rem!important; }
|
||||
|
||||
.pt-5 { padding-top:3rem!important; }
|
||||
.pr-5 { padding-right:3rem!important; }
|
||||
.pb-5 { padding-bottom:3rem!important; }
|
||||
.pl-5 { padding-left:3rem!important; }
|
||||
.px-5 { padding-right:3rem!important;padding-left:3rem!important; }
|
||||
.py-5 { padding-top:3rem!important;padding-bottom:3rem!important; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* TODO find out where this is used. */
|
||||
input.ti-new-tag-input {
|
||||
font-size: 14px !important;
|
||||
|
||||
@@ -25,6 +25,10 @@ $(function () {
|
||||
lineChart('chart/piggy-bank/' + piggyBankID, 'piggy-bank-history');
|
||||
}
|
||||
|
||||
$('.confirm-history-delete').click(function() {
|
||||
return confirm(confirmText);
|
||||
});
|
||||
|
||||
// on submit of logout button:
|
||||
$('.reset-link').click(function(e) {
|
||||
console.log('here we are');
|
||||
|
||||
@@ -22,4 +22,7 @@
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
$('.confirm-tag-delete').on('click', function() {
|
||||
return confirm(confirmText);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -653,7 +653,7 @@ export default {
|
||||
// console.log('Upload complete!');
|
||||
return true;
|
||||
}).catch(error => {
|
||||
console.error('Could not upload');
|
||||
console.error('[b] Could not upload');
|
||||
console.error(error);
|
||||
// console.log('Uploaded attachment #' + key);
|
||||
uploads++;
|
||||
|
||||
@@ -964,7 +964,7 @@ export default {
|
||||
// console.log('Upload complete!');
|
||||
return true;
|
||||
}).catch(error => {
|
||||
console.error('Could not upload file.');
|
||||
console.error('[c] Could not upload file.');
|
||||
console.error(error);
|
||||
uploads++;
|
||||
this.error_message = 'Could not upload attachment: ' + error;
|
||||
|
||||
@@ -37,7 +37,7 @@ let uploadFiles = function (fileData) {
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('Could not upload');
|
||||
console.error('[a] Could not upload');
|
||||
console.error(error);
|
||||
uploads++;
|
||||
// break right away
|
||||
|
||||
@@ -197,6 +197,7 @@ return [
|
||||
'journals_in_period_for_category' => 'All transactions for category :name between :start and :end',
|
||||
'journals_in_period_for_tag' => 'All transactions for tag :tag between :start and :end',
|
||||
'not_available_demo_user' => 'The feature you try to access is not available to demo users.',
|
||||
'test_rate_limited' => 'Please wait a moment before trying again',
|
||||
'exchange_rate_instructions' => 'Asset account "@name" only accepts transactions in @primary_currency. If you wish to use @foreign_currency instead, make sure that the amount in @primary_currency is known as well:',
|
||||
'transfer_exchange_rate_instructions' => 'Source asset account "@source_name" only accepts transactions in @source_currency. Destination asset account "@dest_name" only accepts transactions in @dest_currency. You must provide the transferred amount correctly in both currencies.',
|
||||
'transaction_data' => 'Transaction data',
|
||||
@@ -1526,6 +1527,7 @@ return [
|
||||
'administration_role_mng_currencies' => 'Manage currencies',
|
||||
'administration_role_view_reports' => 'View reports',
|
||||
'administration_role_full' => 'Full access',
|
||||
'must_be_owner' => 'You must be system owner to do this',
|
||||
|
||||
// mfa
|
||||
'enable_mfa' => 'Enable multi-factor authentication',
|
||||
|
||||
@@ -26,12 +26,10 @@
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="box-body no-padding">
|
||||
<div style="padding:8px;">
|
||||
<div class="m-1">
|
||||
<a href="{{ route('accounts.create', objectType) }}" class="btn btn-success"><span class="fa fa-plus fa-fw"></span> {{ ('make_new_' ~ objectType ~ '_account')|_ }}</a>
|
||||
</div>
|
||||
|
||||
{% include 'list.accounts' %}
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
|
||||
@@ -16,22 +16,22 @@
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2" style="width:50%;">{{ 'start_balance'|_ }}</th>
|
||||
<th colspan="2" class="half">{{ 'start_balance'|_ }}</th>
|
||||
<th colspan="2">{{ 'end_balance'|_ }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width:25%;">
|
||||
<td class="quarter">
|
||||
{{ 'date'|_ }}
|
||||
</td>
|
||||
<td style="width:25%;">
|
||||
<td class="quarter">
|
||||
{{ 'balance'|_ }}
|
||||
</td>
|
||||
<td style="width:25%;">
|
||||
<td class="quarter">
|
||||
{{ 'date'|_ }}
|
||||
</td>
|
||||
<td style="width:25%;">
|
||||
<td class="quarter">
|
||||
{{ 'balance'|_ }}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -72,16 +72,16 @@
|
||||
<div class="update_balance_instruction">
|
||||
{{ 'update_balance_dates_instruction'|_ }}
|
||||
</div>
|
||||
<div class="select_transactions_instruction" style="display:none;">
|
||||
<div class="select_transactions_instruction hidden">
|
||||
{{ 'select_transactions_instruction'|_ }}
|
||||
</div>
|
||||
<div class="date_change_warning text-danger" style="display:none;">
|
||||
<div class="date_change_warning text-danger hidden">
|
||||
{{ 'date_change_instruction'|_ }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" class="btn btn-default start_reconcile">{{ 'start_reconcile'|_ }}</a>
|
||||
<a href="#" class="btn btn-default change_date_button" style="display: none;">{{ 'update_selection'|_ }}</a>
|
||||
<a href="#" class="btn btn-default change_date_button hidden">{{ 'update_selection'|_ }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<form style="display: inline;" class="form-horizontal" id="income" action="{{ route }}" method="POST">
|
||||
<form class="form-horizontal inline" id="income" action="{{ route }}" method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="start" value="{{ start.format('Y-m-d') }}"/>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:30%;">{{ trans('list.date') }}</td>
|
||||
<td class="thirty">{{ trans('list.date') }}</td>
|
||||
<td>{{ journal.date.isoFormat(monthAndDayFormat) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -81,7 +81,7 @@
|
||||
<td>
|
||||
{% for tag in journal.tags %}
|
||||
|
||||
<h4 style="display: inline;"><a class="label label-success" href="{{ route('tags.show',tag) }}">
|
||||
<h4 class="inline"><a class="label label-success" href="{{ route('tags.show',tag) }}">
|
||||
{% if tag.tag_mode == 'nothing' %}
|
||||
<span class="fa fa-fw fa-tag"></span>
|
||||
{% endif %}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<tr class="ignore">
|
||||
<th class="hidden-xs" colspan="2"> </th>
|
||||
<th>{{ trans('list.description') }}</th>
|
||||
<th style="text-align:right;">{{ trans('list.amount') }}</th>
|
||||
<th class="text-right">{{ trans('list.amount') }}</th>
|
||||
<th class="hidden-xs hidden-sm hidden-md">{{ trans('list.reconcile') }}</th>
|
||||
<th class="hidden-xs hidden-sm">{{ trans('list.date') }}</th>
|
||||
<th class="hidden-xs hidden-sm hidden-md">{{ trans('list.from') }}</th>
|
||||
@@ -100,8 +100,8 @@
|
||||
{{ journal.description }}</a>
|
||||
</td>
|
||||
|
||||
<td style="text-align: right;">
|
||||
<span style="margin-right:5px;">
|
||||
<td class="text-right">
|
||||
<span class="mr-1">
|
||||
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places) }}
|
||||
{% if null != journal.foreign_amount %}
|
||||
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div>
|
||||
<canvas id="overview-chart" style="width:100%;height:400px;" height="400" width="100%"></canvas>
|
||||
<canvas id="overview-chart" class="wide-chart" height="400" width="100%"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,8 +81,8 @@
|
||||
<h3 class="box-title">{{ 'expenses_by_category'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div style="width:100%;margin:0 auto;">
|
||||
<canvas id="account-cat-out" style="width:100%;height:250px;" height="250"></canvas>
|
||||
<div class="center-chart">
|
||||
<canvas id="account-cat-out" class="medium-chart" height="250"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -93,8 +93,8 @@
|
||||
<h3 class="box-title">{{ 'expenses_by_budget'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div style="width:100%;margin:0 auto;">
|
||||
<canvas id="account-budget-out" style="width:100%;height:250px;" height="250"></canvas>
|
||||
<div class="center-chart">
|
||||
<canvas id="account-budget-out" class="medium-chart" height="250"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,8 +105,8 @@
|
||||
<h3 class="box-title">{{ 'income_by_category'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div style="width:100%;margin:0 auto;">
|
||||
<canvas id="account-cat-in" style="width:100%;height:250px;" height="250"></canvas>
|
||||
<div class="center-chart">
|
||||
<canvas id="account-cat-in" class="medium-chart" height="250"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,7 +122,7 @@
|
||||
<h3 class="box-title">{{ 'location'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div id="location_map" style="width:100%;height:300px;"></div>
|
||||
<div id="location_map" class="map-size"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body no-padding">
|
||||
<div style="padding:8px;">
|
||||
<div class="m-1">
|
||||
<a class="btn btn-success" href="{{ route('subscriptions.create') }}"><span class="fa fa-plus fa-fw"></span> {{ 'create_new_bill'|_ }}</a>
|
||||
</div>
|
||||
{% include 'list/bills' %}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="width:50%;">{{ 'bill_is_active'|_ }}</td>
|
||||
<td class="half">{{ 'bill_is_active'|_ }}</td>
|
||||
<td>
|
||||
{% if object.data.active %}
|
||||
<span class="fa fa-check fa-fw" title="{{ 'active'|_ }}"></span> {{ 'yes'|_ }}
|
||||
@@ -162,7 +162,7 @@
|
||||
<h3 class="box-title">{{ 'chart'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="bill-overview" style="width:100%;height:400px;" height="400" width="100%"></canvas>
|
||||
<canvas id="bill-overview" class="wide-chart" height="400" width="100%"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<form style="display: inline;" id="income" action="{{ route('budget-limits.store') }}" method="POST">
|
||||
<form class="inline" id="income" action="{{ route('budget-limits.store') }}" method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="start" value="{{ start.format('Y-m-d') }}"/>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<form style="display: inline;" action="{{ route('budget-limits.update', [budgetLimit.id]) }}" method="POST">
|
||||
<form class="inline" action="{{ route('budget-limits.update', [budgetLimit.id]) }}" method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="redirect" value="true"/>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-lg-8 col-md-4 col-sm-12 col-xs-12 text-center">
|
||||
<div class="btn btn-group btn-group-lg" style="padding-top:0;">
|
||||
<div class="btn btn-group btn-group-lg pt-0">
|
||||
<a href="{{ route('budgets.index', [prevLoop[0].start.format('Y-m-d'), prevLoop[0].end.format('Y-m-d')]) }}"
|
||||
class="btn btn-default" title="{{ prevLoop[0].title }}">←</a>
|
||||
<a title="{{ start.isoFormat(monthAndDayFormat) }} - {{ end.isoFormat(monthAndDayFormat) }}"
|
||||
@@ -71,7 +71,7 @@
|
||||
</small>
|
||||
</div>
|
||||
{# info about the amount spent #}
|
||||
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9" style="text-align:right;margin-bottom:3px;">
|
||||
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9 text-right mb-1">
|
||||
<small class="available_bar"
|
||||
data-id="0">{{ trans('firefly.available_between', {start: start.isoFormat(monthAndDayFormat), end: end.isoFormat(monthAndDayFormat) }) }}
|
||||
:
|
||||
@@ -123,7 +123,7 @@
|
||||
</small>
|
||||
</div>
|
||||
{# info about the amount spent #}
|
||||
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9" style="text-align:right;margin-bottom:3px;">
|
||||
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9 text-right mb-1">
|
||||
<small class="available_bar"
|
||||
data-id="{{ budget.id }}">{{ trans('firefly.available_between', {start: budget.start_date.isoFormat(monthAndDayFormat), end: budget.end_date.isoFormat(monthAndDayFormat) }) }}
|
||||
:
|
||||
@@ -146,17 +146,17 @@
|
||||
{# red: the exact amount of the available budget, if more has budgeted. #}
|
||||
<div class="progress-bar progress-bar-danger" data-id="{{ budget.id }}" role="progressbar" aria-valuenow="10"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
aria-valuemax="100"></div>
|
||||
|
||||
{# orange: overbudgeted amount #}
|
||||
<div class="progress-bar progress-bar-warning" data-id="{{ budget.id }}" role="progressbar" aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
aria-valuemax="100"></div>
|
||||
|
||||
{# budgeted amount if enough or les #}
|
||||
<div class="progress-bar progress-bar-info" data-id="{{ budget.id }}" role="progressbar" aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -179,12 +179,9 @@
|
||||
<div class="progress spent_bar" data-id="{{ budget.id }}" data-budgeted="{{ budget.budgeted }}"
|
||||
data-spent="{{ budget.spent }}">
|
||||
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="0" aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
style="width: 0;"></div>
|
||||
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
style="width: 0;"></div>
|
||||
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
style="width: 0;"></div>
|
||||
aria-valuemax="100"></div>
|
||||
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,15 +204,15 @@
|
||||
<h3 class="box-title">{{ 'budgets'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body no-padding">
|
||||
<div style="padding:8px;">
|
||||
<div class="m-1">
|
||||
<a href="{{ route('budgets.create') }}" class="btn btn-success"><span class="fa fa-plus fa-fw"></span> {{ 'createBudget'|_ }}</a>
|
||||
</div>
|
||||
<table class="table table-bordered sortable-table table-striped sortable" id="budgetList">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="hidden-sm hidden-xs" style="width:10%;"> </th>
|
||||
<th class="hidden-sm hidden-xs ten"> </th>
|
||||
<th>{{ 'budget'|_ }}</th>
|
||||
<th style="width:25%;">{{ 'budgeted'|_ }}</th>
|
||||
<th class="quarter">{{ 'budgeted'|_ }}</th>
|
||||
<th class="hidden-sm hidden-xs">{{ 'spent'|_ }} ({{ 'per_day'|_|lower }})</th>
|
||||
<th>{{ 'left'|_ }} ({{ 'per_day'|_|lower }})</th>
|
||||
</tr>
|
||||
@@ -276,8 +273,8 @@
|
||||
---
|
||||
{% endif %}
|
||||
</div>
|
||||
<span class="text-danger budget_warning" data-id="{{ budget.id }}" data-budgetLimit="{{ budgetLimit.id }}"
|
||||
style="display:none;"></span>
|
||||
<span class="hidden text-danger budget_warning" data-id="{{ budget.id }}" data-budgetLimit="{{ budgetLimit.id }}"
|
||||
></span>
|
||||
{% endif %}
|
||||
{% if budget.budgeted|length > 0 %}
|
||||
{% for budgetLimit in budget.budgeted %}
|
||||
@@ -313,8 +310,8 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-danger budget_warning" data-id="{{ budget.id }}" data-budgetLimit="{{ budgetLimit.id }}"
|
||||
style="display:none;"></span>
|
||||
<span class="hidden text-danger budget_warning" data-id="{{ budget.id }}" data-budgetLimit="{{ budgetLimit.id }}"
|
||||
></span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if budget.budgeted|length < currencies.count() %}
|
||||
@@ -323,11 +320,11 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs spent" data-id="{{ budget.id }}" style="text-align:right;">
|
||||
<td class="hidden-sm hidden-xs spent text-right" data-id="{{ budget.id }}">
|
||||
{% include('budgets.partials.amount-spent') %}
|
||||
</td>
|
||||
{# this cell displays the amount left in the budget, per budget limit. #}
|
||||
<td class="left" data-id="{{ budget.id }}" style="text-align: right;">
|
||||
<td class="left" data-id="{{ budget.id }}" class="text-right">
|
||||
{% include('budgets.partials.amount-left') %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="budgetOverview" style="width:100%;height:400px;" height="400" width="100%"></canvas>
|
||||
<canvas id="budgetOverview" class="wide-chart" height="400" width="100%"></canvas>
|
||||
</div>
|
||||
{% if budgetLimit %}
|
||||
<div class="box-footer">
|
||||
@@ -60,8 +60,8 @@
|
||||
<h3 class="box-title">{{ 'expenses_by_category'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div style="width:100%;margin:0 auto;">
|
||||
<canvas id="budget-cat-out" style="width:100%;height:250px;" height="250"></canvas>
|
||||
<div class="center-chart">
|
||||
<canvas id="budget-cat-out" class="medium-chart" height="250"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{% if budgetLimit %}
|
||||
@@ -79,8 +79,8 @@
|
||||
<h3 class="box-title">{{ 'expenses_by_asset_account'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div style="width:100%;margin:0 auto;">
|
||||
<canvas id="budget-asset-out" style="width:100%;height:250px;" height="250"></canvas>
|
||||
<div class="center-chart">
|
||||
<canvas id="budget-asset-out" class="medium-chart" height="250"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{% if budgetLimit %}
|
||||
@@ -98,8 +98,8 @@
|
||||
<h3 class="box-title">{{ 'expenses_by_expense_account'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div style="width:100%;margin:0 auto;">
|
||||
<canvas id="budget-expense-out" style="width:100%;height:250px;" height="250"></canvas>
|
||||
<div class="center-chart">
|
||||
<canvas id="budget-expense-out" class="medium-chart" height="250"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{% if budgetLimit %}
|
||||
@@ -155,7 +155,7 @@
|
||||
<div class="box-body no-padding">
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<td style="width:33%;">{{ 'amount'|_ }}</td>
|
||||
<td class="third">{{ 'amount'|_ }}</td>
|
||||
<td>
|
||||
{{ formatAmountBySymbol(limit.amount, limit.transactionCurrency.symbol, limit.transactionCurrency.decimal_places) }}
|
||||
{% if convertToPrimary and null != limit.native_amount %}
|
||||
@@ -164,7 +164,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:33%;">{{ 'spent'|_ }}</td>
|
||||
<td class="third">{{ 'spent'|_ }}</td>
|
||||
<td>
|
||||
{% if convertToPrimary %}
|
||||
{{ formatAmountBySymbol(limit.spent, limit.transactionCurrency.symbol, limit.transactionCurrency.decimal_places) }}
|
||||
@@ -176,7 +176,7 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if limit.spent > 0 %}
|
||||
{% if 0 != bccomp('0', limit.spent) %}
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
{% set overspent = limit.amount + limit.spent < 0 %}
|
||||
@@ -184,18 +184,18 @@
|
||||
{% if overspent %}
|
||||
{% set pct = (limit.spent != 0 ? (limit.amount / (limit.spent*-1))*100 : 0) %} {# must have -1 here #}
|
||||
<div class="progress progress-striped">
|
||||
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="{{ pct|round }}"
|
||||
<div class="progress-bar progress-bar-warning width-{{ pct|round }}" role="progressbar" aria-valuenow="{{ pct|round }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: {{ pct|round }}%;"></div>
|
||||
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="{{ (100-pct)|round }}"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width: {{ (100-pct)|round }}%;"></div>
|
||||
aria-valuemax="100"></div>
|
||||
<div class="progress-bar progress-bar-danger width-{{ 100-pct|round }}" role="progressbar" aria-valuenow="{{ (100-pct)|round }}"
|
||||
aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% set pct = (limit.amount != 0 ? (((limit.spent*-1) / limit.amount)*100) : 0) %} {# must have -1 here #}
|
||||
<div class="progress progress-striped">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{ pct|round }}"
|
||||
<div class="progress-bar progress-bar-success width-{{ pct|round }}" role="progressbar" aria-valuenow="{{ pct|round }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: {{ pct|round }}%;"></div>
|
||||
aria-valuemax="100"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
</div>
|
||||
<div class="box-body no-padding">
|
||||
<div style="padding:8px;">
|
||||
<div class="m-1">
|
||||
<a class="btn btn-success" href="{{ route('categories.create') }}"><span class="fa fa-plus fa-fw"></span> {{ 'new_category'|_ }}</a>
|
||||
</div>
|
||||
{% include 'list/categories' %}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="specific-period" style="width:100%;height:400px;" height="400" width="100%"></canvas>
|
||||
<canvas id="specific-period" class="wide-chart" height="400" width="100%"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -28,7 +28,7 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="category-everything" style="width:100%;height:400px;" height="400" width="100%"></canvas>
|
||||
<canvas id="category-everything" class="wide-chart" height="400" width="100%"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +43,7 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="category-everything" style="width:100%;height:400px;" height="400" width="100%"></canvas>
|
||||
<canvas id="category-everything" class="wide-chart" height="400" width="100%"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
{{ 'currencies_switch_default'|_ }}
|
||||
</p>
|
||||
{% if currencies|length > 0 %}
|
||||
<div style="padding-left:8px;">
|
||||
<div class="m-1">
|
||||
{{ currencies.links('pagination.bootstrap-4')|raw }}
|
||||
</div>
|
||||
<table class="table table-hover">
|
||||
@@ -81,7 +81,7 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="padding-left:8px;">
|
||||
<div class="m-1">
|
||||
{{ currencies.links('pagination.bootstrap-4')|raw }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -3,16 +3,21 @@
|
||||
<head>
|
||||
<title>{{ trans('firefly.debug_page') }}</title>
|
||||
</head>
|
||||
<style nonce="{{ JS_NONCE }}">
|
||||
p.default-text {font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;}
|
||||
p.warn-text {font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;color:#a00;}
|
||||
textarea {font-family:Menlo, Monaco, Consolas, monospace;font-size:8pt;}
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<p style="font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;">
|
||||
<p class="default-text">
|
||||
{{ trans('firefly.debug_submit_instructions')|raw }}
|
||||
</p>
|
||||
<p style="font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;">
|
||||
<p class="default-text">
|
||||
<strong>{{ trans('firefly.debug_pretty_table') }}</strong>
|
||||
</p>
|
||||
<textarea rows="30" cols="100" name="debug_info" id="debug_info"
|
||||
style="font-family:Menlo, Monaco, Consolas, monospace;font-size:8pt;">
|
||||
<label for="debug_info"></label>
|
||||
<textarea rows="30" cols="100" name="debug_info" id="debug_info">
|
||||
Debug information generated at {{ now }} for Firefly III version **{% if not FF_IS_DEVELOP %}v{% endif %}{{ FF_VERSION }}**.
|
||||
|
||||
{{ table|escape('html') }}
|
||||
@@ -25,21 +30,21 @@ Debug information generated at {{ now }} for Firefly III version **{% if not FF_
|
||||
textArea.value = text;
|
||||
</script>
|
||||
|
||||
<p style="font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;color:#a00;">
|
||||
<p class=default-text">
|
||||
<a href="{{ route('index') }}">{{ trans('firefly.back_to_index') }}</a>
|
||||
</p>
|
||||
|
||||
<p style="font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;color:#a00;">
|
||||
<p class="warn-text">
|
||||
{{ trans('firefly.debug_additional_data')|raw }}
|
||||
</p>
|
||||
|
||||
<textarea rows="30" cols="100" name="log_info" style="font-family:Menlo, Monaco, Consolas, monospace;font-size:7pt;">
|
||||
<textarea rows="30" cols="100" name="log_info">
|
||||
```
|
||||
{{ logContent }}
|
||||
```
|
||||
</textarea>
|
||||
|
||||
<p style="font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;color:#a00;">
|
||||
<p class=default-text">
|
||||
<a href="{{ route('index') }}">{{ trans('firefly.back_to_index') }}</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<h4>
|
||||
{{ trans('errors.stacktrace') }}
|
||||
</h4>
|
||||
<div style="font-family: monospace;font-size:11px;">
|
||||
<div class="monospace">
|
||||
{!! nl2br($exception->getTraceAsString()) !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<h4>
|
||||
{{ trans('errors.stacktrace') }}
|
||||
</h4>
|
||||
<div style="font-family: monospace;font-size:11px;">
|
||||
<div class="monospace">
|
||||
{!! nl2br($exception->getTraceAsString()) !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<h4>
|
||||
{{ trans('errors.stacktrace') }}
|
||||
</h4>
|
||||
<div style="font-family: monospace;font-size:11px;">
|
||||
<div class="monospace">
|
||||
{!! nl2br($exception->getTraceAsString()) !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{% for groupName, accounts in grouped %}
|
||||
<strong>{{ groupName }}</strong><br/>
|
||||
{% for id, account in accounts %}
|
||||
<div class="checkbox" style="margin-left:2em;">
|
||||
<div class="checkbox ml-4">
|
||||
<label>
|
||||
{% if account in selected or (selected|length == 0 and options.select_all == true) %}
|
||||
{{ Html.checkbox(name~'[]',true, id).id(id) }}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
title="{{ 'yourAccounts'|_ }}">{{ 'yourAccounts'|_ }}</a></h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="accounts-chart" style="width:100%;height:400px;" height="400" width="100%"></canvas>
|
||||
<canvas id="accounts-chart" class="wide-chart" height="400" width="100%"></canvas>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<a href="{{ route('accounts.index',['asset']) }}" class="btn btn-default button-sm"><span
|
||||
@@ -29,7 +29,7 @@
|
||||
title="{{ 'budgetsAndSpending'|_ }}">{{ 'budgetsAndSpending'|_ }}</a></h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="budgets-chart" style="width:100%;height:400px;" height="400" width="100%"></canvas>
|
||||
<canvas id="budgets-chart" class="wide-chart" height="400" width="100%"></canvas>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<a href="{{ route('budgets.index') }}" class="btn btn-default button-sm">
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="categories-chart" style="width:100%;height:400px;" height="400" width="100%"></canvas>
|
||||
<canvas id="categories-chart" class="wide-chart" height="400" width="100%"></canvas>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<a href="{{ route('categories.index') }}" class="btn btn-default button-sm">
|
||||
@@ -137,8 +137,8 @@
|
||||
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div style="width:100%;margin:0 auto;">
|
||||
<canvas id="bills-chart" style="width:100%;height:200px;" height="200"></canvas>
|
||||
<div class="center-chart">
|
||||
<canvas id="bills-chart" class="low-chart" height="200"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
@@ -164,7 +164,7 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="expense-accounts-chart" style="width:100%;height:400px;" height="400"
|
||||
<canvas id="expense-accounts-chart" class="wide-chart" height="400"
|
||||
width="100%"></canvas>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
@@ -180,7 +180,7 @@
|
||||
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="revenue-accounts-chart" style="width:100%;height:400px;" height="400"
|
||||
<canvas id="revenue-accounts-chart" class="wide-chart" height="400"
|
||||
width="100%"></canvas>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{% extends "./layout/install" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card mb-2">
|
||||
<div class="card-body login-card-body">
|
||||
<p class="login-box-msg">Please wait...</p>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div id="status-box" style="border:1px #ddd solid;padding:5px;">
|
||||
<span class="fa fa-spin fa-spinner"></span> Waiting to start...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" nonce="{{ JS_NONCE }}">
|
||||
var token = '{{ csrf_token() }}';
|
||||
var index = 0;
|
||||
var runCommandUrl = '{{ route('installer.runCommand') }}';
|
||||
var homeUrl = '{{ route('flush') }}';
|
||||
</script>
|
||||
<script type="text/javascript" src="v1/js/ff/install/Xindex.js" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
<p>The upgrade and installation is ongoing. Please track its progress through the box below.</p>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div id="status-box" style="border:1px #ddd solid;padding:5px;">
|
||||
<div id="status-box" class="p-3 install-box-border">
|
||||
<span class="fa fa-spin fa-spinner"></span> Waiting to start...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
{% for entry in info %}
|
||||
<strong>{{ entry.name }}</strong><br/>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="{{ entry.percentage }}" aria-valuemin="0" aria-valuemax="100"
|
||||
style="width: {{ entry.percentage }}%;">
|
||||
<div class="progress-bar width-{{ entry.percentage|round }}" role="progressbar" aria-valuenow="{{ entry.percentage }}" aria-valuemin="0" aria-valuemax="100">
|
||||
{% if entry.percentage >=20 %}
|
||||
{% if convertToPrimary and 0 != avg.pc_amount %}
|
||||
{{ formatAmountBySymbol(entry.pc_amount, entry.primary_currency_symbol, entry.primary_currency_decimal_places, false) }}
|
||||
|
||||
@@ -131,13 +131,13 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<span style="color:#fff;padding: 15px;display: block;line-height: 20px;">
|
||||
<span class="date-range-holder">
|
||||
<span id="daterange"></span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="dropdown user user-menu">
|
||||
<span style="cursor:default;color:#fff;padding: 15px;display: block;line-height: 20px;">
|
||||
<span class="dropdown-holder">
|
||||
<span class="hidden-xs">{{ Auth.user.email }}</span>
|
||||
</span>
|
||||
</li>
|
||||
@@ -290,10 +290,10 @@
|
||||
</script>
|
||||
<noscript><p><img
|
||||
src="//{{ config('firefly.tracker_url') }}/matomo.php?idsite={{ config('firefly.tracker_site_id') }}&rec=1"
|
||||
style="border:0;" alt=""/></p></noscript>
|
||||
class="no-border" alt=""/></p></noscript>
|
||||
{% endif %}
|
||||
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="hidden">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
s.parentNode.insertBefore(g, s);
|
||||
})();
|
||||
</script>
|
||||
<noscript><p><img src="//{{ config('firefly.tracker_url') }}/matomo.php?idsite={{ config('firefly.tracker_site_id') }}&rec=1" style="border:0;" alt=""/></p></noscript>
|
||||
<noscript><p><img src="//{{ config('firefly.tracker_url') }}/matomo.php?idsite={{ config('firefly.tracker_site_id') }}&rec=1" class="no-border" alt=""/></p></noscript>
|
||||
{% endif %}
|
||||
|
||||
</body>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user