Compare commits

..

19 Commits

Author SHA1 Message Date
github-actions[bot]
ef5c35c04d Merge pull request #11900 from firefly-iii/release-1772860900
🤖 Automatically merge the PR into the develop branch.
2026-03-07 06:21:47 +01:00
JC5
4dfbdc644c 🤖 Auto commit for release 'develop' on 2026-03-07 2026-03-07 06:21:40 +01:00
github-actions[bot]
a08f776ffa Merge pull request #11897 from firefly-iii/release-1772828319
🤖 Automatically merge the PR into the develop branch.
2026-03-06 21:18:48 +01:00
JC5
e246c1f4b7 🤖 Auto commit for release 'develop' on 2026-03-06 2026-03-06 21:18:39 +01:00
github-actions[bot]
f2d1e8e184 Merge pull request #11896 from firefly-iii/release-1772825547
🤖 Automatically merge the PR into the develop branch.
2026-03-06 20:32:37 +01:00
JC5
19d8f46f24 🤖 Auto commit for release 'develop' on 2026-03-06 2026-03-06 20:32:27 +01:00
James Cole
9ac991edd7 Fix phpstan issues. 2026-03-06 20:25:07 +01:00
github-actions[bot]
e85f06792b Merge pull request #11889 from firefly-iii/release-1772807505
🤖 Automatically merge the PR into the develop branch.
2026-03-06 15:31:55 +01:00
JC5
b66f95f1dc 🤖 Auto commit for release 'develop' on 2026-03-06 2026-03-06 15:31:45 +01:00
James Cole
4d63146524 Fix more phpstan issues. 2026-03-06 15:24:35 +01:00
James Cole
695f990236 Correct phpstan errors. 2026-03-06 15:18:49 +01:00
James Cole
e882530a69 Clean up various phpstan issues. 2026-03-06 13:52:11 +01:00
James Cole
993a2491e9 Clean up some phpstan issues. 2026-03-06 11:46:04 +01:00
James Cole
f6ea517b5d Fix https://github.com/orgs/firefly-iii/discussions/11879 2026-03-06 11:41:18 +01:00
github-actions[bot]
6f4143bb79 Merge pull request #11888 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-03-06 10:07:42 +01:00
github-actions[bot]
4a2c77c6b7 Merge pull request #11887 from firefly-iii/release-1772788047
🤖 Automatically merge the PR into the develop branch.
2026-03-06 10:07:36 +01:00
JC5
0e62f980b0 🤖 Auto commit for release 'v6.5.4' on 2026-03-06 2026-03-06 10:07:27 +01:00
James Cole
41533fd922 Clean up changelog. 2026-03-06 10:01:32 +01:00
James Cole
d6b5fbe341 Expand changelog. 2026-03-06 08:02:30 +01:00
74 changed files with 361 additions and 331 deletions

View File

@@ -1264,16 +1264,16 @@
},
{
"name": "symfony/console",
"version": "v8.0.6",
"version": "v8.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "488285876e807a4777f074041d8bb508623419fa"
"reference": "15ed9008a4ebe2d6a78e4937f74e0c13ef2e618a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/488285876e807a4777f074041d8bb508623419fa",
"reference": "488285876e807a4777f074041d8bb508623419fa",
"url": "https://api.github.com/repos/symfony/console/zipball/15ed9008a4ebe2d6a78e4937f74e0c13ef2e618a",
"reference": "15ed9008a4ebe2d6a78e4937f74e0c13ef2e618a",
"shasum": ""
},
"require": {
@@ -1330,7 +1330,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v8.0.6"
"source": "https://github.com/symfony/console/tree/v8.0.7"
},
"funding": [
{
@@ -1350,7 +1350,7 @@
"type": "tidelift"
}
],
"time": "2026-02-25T16:59:43+00:00"
"time": "2026-03-06T14:06:22+00:00"
},
{
"name": "symfony/deprecation-contracts",

View File

@@ -14,10 +14,6 @@ parameters:
enabled: false
noNullableReturnTypeDeclaration:
enabled: false
privateInFinalClass:
enabled: false
noIsset:
enabled: false
finalInAbstractClass:
enabled: false
noConstructorParameterWithDefaultValue:
@@ -38,50 +34,18 @@ parameters:
reportUnmatchedIgnoredErrors: true
ignoreErrors:
# these are actually interesting but not right now:
- identifier: staticMethod.dynamicCall
- identifier: argument.templateType
- identifier: method.childReturnType
- identifier: instanceof.alwaysFalse
- identifier: deadCode.unreachable
- identifier: method.dynamicName
- identifier: larastan.noEnvCallsOutsideOfConfig
- identifier: property.notFound
- identifier: arguments.count
- identifier: staticMethod.dynamicName
- identifier: catch.neverThrown
- identifier: notIdentical.alwaysTrue
- identifier: method.notFound
- identifier: nullsafe.neverNull
- identifier: identical.alwaysFalse
- identifier: if.condNotBoolean
# - identifier: booleanNot.exprNotBoolean
- identifier: method.nonObject
- identifier: function.impossibleType
- identifier: booleanNot.exprNotBoolean
- identifier: ternary.condNotBoolean
- identifier: booleanNot.alwaysFalse
- identifier: booleanAnd.alwaysFalse
- identifier: greater.alwaysTrue
- identifier: function.alreadyNarrowedType
- identifier: booleanNot.alwaysTrue
- identifier: property.phpDocType
- identifier: nullCoalesce.offset
- identifier: nullCoalesce.variable
- identifier: larastan.noUnnecessaryCollectionCall
- identifier: varTag.differentVariable
- identifier: identical.alwaysTrue
- identifier: clone.nonObject
- identifier: assign.propertyReadOnly
- identifier: property.nonObject
- identifier: varTag.nativeType
- identifier: booleanAnd.leftAlwaysFalse
- identifier: property.onlyWritten
- identifier: parameter.phpDocType
- identifier: property.dynamicName
- identifier: property.unusedType
- identifier: staticMethod.deprecated
- identifier: greater.invalid
- identifier: instanceof.alwaysTrue
# ignore everything but things that BREAK
- identifier: property.deprecated
- identifier: method.deprecated
@@ -93,7 +57,15 @@ parameters:
- identifier: assign.propertyType
- identifier: return.unusedType
- identifier: return.phpDocType
# all errors below I will never fix.
# all errors below I will (probably) never fix.
- identifier: method.notFound # way too many false positives
- identifier: catch.neverThrown # plenty of errors that are thrown undocumented
- identifier: staticMethod.dynamicName # dont care
- identifier: arguments.count # one false positive
- identifier: property.notFound # false positives
- identifier: method.dynamicName # i dont care
- identifier: staticMethod.dynamicCall # many false positives.
- identifier: argument.templateType # no clue how to fix single occurrence.
# - '#expects view-string\|null, string given#'
# - '#expects view-string, string given#'
# - "#Parameter \\#[1-2] \\$num[1-2] of function bc[a-z]+ expects numeric-string, [a-z\\-|&]+ given#"

View File

@@ -63,6 +63,8 @@ abstract class Controller extends BaseController
use ValidatesRequests;
use ValidatesUserGroupTrait;
protected array $acceptedRoles = [];
protected const string CONTENT_TYPE = 'application/vnd.api+json';
protected const string JSON_CONTENT_TYPE = 'application/json';

View File

@@ -112,19 +112,6 @@ final class BasicController extends Controller
return response()->json($return);
}
/**
* Check if date is outside session range.
*/
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool
{ // Validate a preference
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
return true;
}
// start and end in the past? use $end
return $start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date);
}
private function getBalanceInformation(Carbon $start, Carbon $end): array
{
Log::debug('getBalanceInformation');

View File

@@ -62,7 +62,7 @@ final class BatchController extends Controller
}
Log::debug(sprintf('Counted %d journals.', count($journals)));
/** @var TransactionJournal $first */
/** @var null|TransactionJournal $first */
$first = $journals->first();
$group = $first?->transactionGroup;
if (null === $group) {

View File

@@ -34,7 +34,9 @@ abstract class AggregateFormRequest extends ApiRequest
/**
* @var ApiRequest[]
*/
protected array $requests = [];
protected array $requests = [];
protected array $acceptedRoles = [];
#[Override]
public function initialize(

View File

@@ -38,7 +38,7 @@ class DateRangeRequest extends ApiRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($validator->failed()) {
if (count($validator->failed()) > 0) {
return;
}
$start = $this->getCarbonDate('start')?->startOfDay();

View File

@@ -36,7 +36,7 @@ class DateRequest extends ApiRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($validator->failed()) {
if (count($validator->failed()) > 0) {
return;
}
$date = $this->getCarbonDate('date')?->endOfDay();

View File

@@ -50,7 +50,7 @@ class ObjectTypeApiRequest extends ApiRequest
$this->objectType = $config['object_type'] ?? null;
if (!$this->objectType) {
if (null === $this->objectType) {
throw new RuntimeException('ObjectTypeApiRequest requires a object_type config');
}
}
@@ -75,7 +75,7 @@ class ObjectTypeApiRequest extends ApiRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($validator->failed()) {
if (count($validator->failed()) > 0) {
return;
}
$type = $this->convertString('types', 'all');

View File

@@ -42,7 +42,7 @@ class QueryRequest extends ApiRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($validator->failed()) {
if (count($validator->failed()) > 0) {
return;
}
$query = $this->convertString('query');

View File

@@ -40,7 +40,7 @@ class AccountTypeApiRequest extends ApiRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($validator->failed()) {
if (count($validator->failed()) > 0) {
return;
}

View File

@@ -42,7 +42,7 @@ class AccountTypesApiRequest extends ApiRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($validator->failed()) {
if (count($validator->failed()) > 0) {
return;
}
$types = explode(',', $this->convertString('types', 'all'));

View File

@@ -40,6 +40,8 @@ class Request extends FormRequest
use ChecksLogin;
use ConvertsDataTypes;
protected array $acceptedRoles = [];
/**
* Get all data from the request.
*/

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit;
use Carbon\Carbon;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
@@ -85,6 +86,7 @@ class StoreRequest extends FormRequest
*/
public function withValidator(Validator $validator): void
{
/** @var Budget $budget */
$budget = $this->route()->parameter('budget');
$validator->after(static function (Validator $validator) use ($budget): void {
if (0 !== count($validator->failed())) {

View File

@@ -61,7 +61,7 @@ class UpdateRequest extends FormRequest
*/
public function rules(): array
{
/** @var TransactionCurrency $currency */
/** @var string|TransactionCurrency $currency */
$currency = $this->route()->parameter('currency_code');
if (is_string($currency)) {
$currency = TransactionCurrency::whereCode($currency)->first();

View File

@@ -41,7 +41,7 @@ class PaginationRequest extends ApiRequest
$this->sortClass = $config['sort_class'] ?? null;
if (!$this->sortClass) {
if (null === $this->sortClass) {
throw new RuntimeException('PaginationRequest requires a sort_class config');
}
}
@@ -58,7 +58,7 @@ class PaginationRequest extends ApiRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($validator->failed()) {
if (count($validator->failed()) > 0) {
return;
}
@@ -72,7 +72,7 @@ class PaginationRequest extends ApiRequest
$page = $this->convertInteger('page');
$page = min(max(1, $page), 2 ** 16);
$offset = ($page - 1) * $limit;
$sort = $this->sortClass ? $this->convertSortParameters('sort', $this->sortClass) : $this->get('sort');
$sort = null !== $this->sortClass ? $this->convertSortParameters('sort', $this->sortClass) : $this->get('sort');
$this->attributes->set('limit', $limit);
$this->attributes->set('sort', $sort);
$this->attributes->set('page', $page);

View File

@@ -45,7 +45,7 @@ class CountRequest extends AggregateFormRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($validator->failed()) {
if (count($validator->failed()) > 0) {
return;
}
$this->attributes->set('include_deleted', $this->convertBoolean($this->input('include_deleted', 'false')));

View File

@@ -37,7 +37,7 @@ class SearchQueryRequest extends ApiRequest
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($validator->failed()) {
if (count($validator->failed()) > 0) {
return;
}
$query = $this->convertString('query');

View File

@@ -57,24 +57,28 @@ class CorrectsIbans extends Command
/** @var Account $account */
foreach ($accounts as $account) {
$userId = $account->user_id;
$userId = $account->user_id;
$set[$userId] ??= [];
$iban = (string) $account->iban;
$iban = (string) $account->iban;
if ('' === $iban) {
continue;
}
$type = $account->accountType->type;
$type = $account->accountType->type;
if (in_array($type, [AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value], true)) {
$type = 'liabilities';
}
// iban already in use! two exceptions exist:
if (
array_key_exists($iban, $set[$userId]) && (
!AccountTypeEnum::EXPENSE->value === $set[$userId][$iban]
&& AccountTypeEnum::REVENUE->value === $type
&& !(AccountTypeEnum::REVENUE->value === $set[$userId][$iban] && AccountTypeEnum::EXPENSE->value === $type)
)
) {
$showWarningAndCorrect = false;
if (array_key_exists($iban, $set[$userId])) {
// the type is a revenue account, and the existing IBAN is NOT an expense account.
if (AccountTypeEnum::REVENUE->value === $type && AccountTypeEnum::EXPENSE->value !== $set[$userId][$iban]) {
$showWarningAndCorrect = true;
}
// the type is an expense account, and the existing IBAN is NOT a revenue account
if (AccountTypeEnum::EXPENSE->value === $type && AccountTypeEnum::REVENUE->value !== $set[$userId][$iban]) {
$showWarningAndCorrect = true;
}
}
if ($showWarningAndCorrect) {
$this->friendlyWarning(sprintf(
'IBAN "%s" is used more than once and will be removed from %s #%d ("%s")',
$iban,

View File

@@ -69,10 +69,6 @@ class CorrectsInvertedBudgetLimits extends Command
$budgetLimit->saveQuietly();
}
if ($set->count() > 0) {
// FIXME here be a available budget event.
}
if (1 === $set->count()) {
$this->friendlyInfo('Corrected one budget limit to have the right start/end dates.');

View File

@@ -322,7 +322,7 @@ class CorrectsUnevenAmount extends Command
foreach ($journals as $entry) {
$sum = (string) $entry->the_sum;
$sum = Steam::floatalize($sum);
if (!is_numeric($sum) || '' === $sum || str_contains($sum, 'e') || str_contains($sum, ',')) {
if (!is_numeric($sum) || str_contains($sum, 'e') || str_contains($sum, ',')) {
$message = sprintf('Journal #%d has an invalid sum ("%s"). No sure what to do.', $entry->transaction_journal_id, $entry->the_sum);
$this->friendlyWarning($message);
Log::warning($message);

View File

@@ -127,7 +127,7 @@ class RemovesDatabaseDecryption extends Command
}
// A separate routine for preferences table:
if ('preferences' === $table && is_string($value)) {
if ('preferences' === $table) {
$this->decryptPreferencesRow($id, $value);
return;

View File

@@ -101,8 +101,13 @@ class UpgradesLiabilitiesEight extends Command
private function hasBadOpening(Account $account): bool
{
/** @var TransactionType $openingBalanceType */
$openingBalanceType = TransactionType::whereType(TransactionTypeEnum::OPENING_BALANCE->value)->first();
/** @var TransactionType $liabilityType */
$liabilityType = TransactionType::whereType(TransactionTypeEnum::LIABILITY_CREDIT->value)->first();
/** @var null|TransactionJournal $openingJournal */
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
@@ -111,6 +116,8 @@ class UpgradesLiabilitiesEight extends Command
if (null === $openingJournal) {
return false;
}
/** @var null|TransactionJournal $liabilityJournal */
$liabilityJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)
->where('transaction_journals.transaction_type_id', $liabilityType->id)
@@ -120,7 +127,7 @@ class UpgradesLiabilitiesEight extends Command
return false;
}
return (bool) $openingJournal->date->isSameDay($liabilityJournal->date);
return $openingJournal->date->isSameDay($liabilityJournal->date);
}
private function isExecuted(): bool

View File

@@ -36,7 +36,7 @@ final class IntervalException extends Exception
public array $availableIntervals = [];
public Periodicity $periodicity = Periodicity::Monthly;
/** @var mixed */
/** @var string */
protected $message = 'The periodicity %s is unknown. Choose one of available periodicity: %s';
public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null)

View File

@@ -338,7 +338,7 @@ class TransactionJournalFactory
'date_tz' => $carbon->format('e'),
'order' => $order,
'tag_count' => 0,
'completed' => !$row['batch_submission'],
'completed' => is_bool($row['batch_submission']) && !$row['batch_submission'],
]);
Log::debug(sprintf('Created new journal #%d: "%s"', $journal->id, $journal->description));
@@ -574,16 +574,11 @@ class TransactionJournalFactory
return [$sourceAccount, $account];
}
if (!$sourceAccount instanceof Account) {
Log::debug('Source account is NULL, destination account is not.');
$account = $this->accountRepository->getReconciliation($destinationAccount);
Log::debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type));
Log::debug('Source account is NULL, destination account is not.');
$account = $this->accountRepository->getReconciliation($destinationAccount);
Log::debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type));
return [$account, $destinationAccount];
}
Log::debug('Unused fallback');
return [$sourceAccount, $destinationAccount];
return [$account, $destinationAccount];
}
private function storeLocation(TransactionJournal $journal, NullArrayObject $data): void

View File

@@ -275,7 +275,7 @@ final class LoginController extends Controller
$request->session()->regenerate();
$this->clearLoginAttempts($request);
$response = $this->authenticated($request, $this->guard()->user());
if ($response) {
if (null !== $response) {
return $response;
}
$path = Steam::getSafeUrl(session()->pull('url.intended', route('index')), route('index'));

View File

@@ -176,7 +176,7 @@ final class RegisterController extends Controller
/**
* @throws FireflyException
*/
protected function allowedToRegister(): bool
private function allowedToRegister(): bool
{
// is allowed to register?
$allowRegistration = true;

View File

@@ -304,7 +304,7 @@ final class DebugController extends Controller
return [
'debug' => var_export(config('app.debug'), true),
'audit_log_channel' => envNonEmpty('AUDIT_LOG_CHANNEL', '(empty)'),
'audit_log_channel' => implode(', ', config('logging.channels.audit.channels')),
'default_language' => (string) config('firefly.default_language'),
'default_locale' => (string) config('firefly.default_locale'),
'remote_header' => 'remote_user_guard' === $userGuard ? config('auth.guard_header') : 'N/A',
@@ -323,11 +323,11 @@ final class DebugController extends Controller
private function getBuildInfo(): array
{
$return = [
'is_docker' => env('IS_DOCKER', false),
'is_docker' => config('firefly.is_docker'),
'build' => '(unknown)',
'build_date' => '(unknown)',
'base_build' => '(unknown)',
'base_build_date' => '(unknown)',
'base_build' => config('firefly.base_image_build'),
'base_build_date' => config('firefly.base_image_date'),
];
try {
@@ -348,12 +348,6 @@ final class DebugController extends Controller
Log::debug('Could not check build date, but thats ok.');
Log::warning($e->getMessage());
}
if ('' !== (string) env('BASE_IMAGE_BUILD')) {
$return['base_build'] = env('BASE_IMAGE_BUILD');
}
if ('' !== (string) env('BASE_IMAGE_DATE')) {
$return['base_build_date'] = env('BASE_IMAGE_DATE');
}
return $return;
}

View File

@@ -86,7 +86,7 @@ final class PreferencesController extends Controller
AccountTypeEnum::DEBT->value,
AccountTypeEnum::MORTGAGE->value,
]);
$isDocker = env('IS_DOCKER', false);
$isDocker = config('firefly.is_docker');
$groupedAccounts = [];
/** @var Account $account */

View File

@@ -67,7 +67,7 @@ class SecureHeaders
];
// overrule in development mode
if (true === env('IS_LOCAL_DEV')) {
if (true === config('firefly.is_local_dev')) {
$csp = [
"default-src 'none'",
"object-src 'none'",

View File

@@ -213,7 +213,7 @@ class CreateRecurringTransactions implements ShouldQueue
/** @var RecurrenceTransaction $transaction */
foreach ($transactions as $index => $transaction) {
$single = [
'type' => null === $transaction?->transactionType?->type
'type' => null === $transaction->transactionType?->type
? strtolower((string) $recurrence->transactionType->type)
: strtolower($transaction->transactionType->type),
'date' => $date,

View File

@@ -42,7 +42,7 @@ class ProcessesBudgetLimits implements ShouldQueue
public function handle(CreatedBudgetLimit|DestroyedBudgetLimit|UpdatedBudgetLimit $event): void
{
Log::debug(sprintf('Now in ProcessesBudgetLimits::handle for event %s', get_class($event)));
if ($event instanceof DestroyedBudgetLimit && null !== $event->user) {
if ($event instanceof DestroyedBudgetLimit) {
// need to recalculate all available budgets for this user.
$calculator = new AvailableBudgetCalculator();
$calculator->setUser($event->user);

View File

@@ -58,10 +58,10 @@ class SendsWebhookMessages implements ShouldQueue
$message->save();
Log::debug(sprintf('Send message #%d', $message->id));
SendWebhookMessage::dispatch($message)->afterResponse();
continue;
}
if (false !== $message->sent) {
Log::debug(sprintf('Skip message #%d', $message->id));
}
Log::debug(sprintf('Skip message #%d', $message->id));
}
// clean up sent messages table:

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Handlers\Observer\BillObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
@@ -38,6 +39,11 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* @property Carbon $date
* @property null|Carbon $end_date
* @property null|Carbon $extension_date
*/
#[ObservedBy([BillObserver::class])]
class Bill extends Model
{

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
@@ -32,6 +33,9 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* @property Carbon $date
*/
class CurrencyExchangeRate extends Model
{
use ReturnsIntegerIdTrait;

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use Illuminate\Database\Eloquent\Casts\Attribute;
@@ -11,6 +12,10 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property Carbon $start
* @property Carbon $end
*/
class PeriodStatistic extends Model
{
use ReturnsIntegerUserIdTrait;

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use FireflyIII\Handlers\Observer\PiggyBankObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
@@ -36,6 +37,10 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* @property null|Carbon $target_date
* @property null|Carbon $start_date
*/
#[ObservedBy([PiggyBankObserver::class])]
class PiggyBank extends Model
{

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Handlers\Observer\PiggyBankEventObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
@@ -31,6 +32,9 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property Carbon $date
*/
#[ObservedBy([PiggyBankEventObserver::class])]
class PiggyBankEvent extends Model
{

View File

@@ -30,6 +30,9 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* @property mixed $data
*/
class Preference extends Model
{
use ReturnsIntegerIdTrait;

View File

@@ -40,8 +40,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* @property Carbon $first_date
* @property null|Carbon $first_date
* @property null|Carbon $latest_date
* @property null|Carbon $repeat_until
*/
#[ObservedBy([DeletedRecurrenceObserver::class])]
class Recurrence extends Model

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Handlers\Observer\DeletedTagObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
@@ -36,6 +37,9 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* @property null|Carbon $date
*/
#[ObservedBy([DeletedTagObserver::class])]
class Tag extends Model
{

View File

@@ -47,7 +47,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method EloquentBuilder|static after()
* @method static EloquentBuilder|static query()
*
* @property TransactionGroup $transactionGroup
* @property null|TransactionGroup $transactionGroup
* @property Carbon $date
*/
#[ObservedBy([DeletedTransactionJournalObserver::class])]
class TransactionJournal extends Model

View File

@@ -687,6 +687,31 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
return $dbQuery->take($limit)->get(['accounts.*']);
}
public function searchAccountIncludingInactive(string $query, array $types, int $limit): Collection
{
$dbQuery = $this->user
->accounts()
->orderBy('accounts.order', 'ASC')
->orderBy('accounts.account_type_id', 'ASC')
->orderBy('accounts.name', 'ASC')
->with(['accountType'])
;
if ('' !== $query) {
// split query on spaces just in case:
$parts = explode(' ', $query);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$dbQuery->whereLike('name', $search);
}
}
if (0 !== count($types)) {
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$dbQuery->whereIn('account_types.type', $types);
}
return $dbQuery->take($limit)->get(['accounts.*']);
}
public function searchAccountNr(string $query, array $types, int $limit): Collection
{
$dbQuery = $this->user

View File

@@ -156,6 +156,8 @@ interface AccountRepositoryInterface
public function searchAccount(string $query, array $types, int $limit): Collection;
public function searchAccountIncludingInactive(string $query, array $types, int $limit): Collection;
public function searchAccountNr(string $query, array $types, int $limit): Collection;
public function store(array $data): Account;

View File

@@ -389,17 +389,13 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface, U
return (string) $budgetLimit->amount;
}
// if budget limit period is inside AB period, it can be added in full.
if (!$limitPeriod->equals($availableBudgetPeriod) && $availableBudgetPeriod->contains($limitPeriod)) {
if ($availableBudgetPeriod->contains($limitPeriod)) {
Log::debug('This budget limit is smaller than the available budget period.');
return (string) $budgetLimit->amount;
}
if (
!$limitPeriod->equals($availableBudgetPeriod)
&& !$availableBudgetPeriod->contains($limitPeriod)
&& $availableBudgetPeriod->overlapsWith($limitPeriod)
) {
if ($availableBudgetPeriod->overlapsWith($limitPeriod)) {
Log::debug('This budget limit is something else entirely!');
$overlap = $availableBudgetPeriod->overlap($limitPeriod);
if ($overlap instanceof Period) {

View File

@@ -101,8 +101,12 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
'currency_code' => (string) $journal['currency_code'],
'currency_decimal_places' => (int) $journal['currency_decimal_places'],
'foreign_currency_id' => (int) ($journal['foreign_currency_id'] ?? 0),
'foreign_amount' => isset($journal['foreign_amount']) ? Steam::negative((string) $journal['foreign_amount']) : null,
'pc_amount' => isset($journal['pc_amount']) ? Steam::negative((string) $journal['pc_amount']) : null,
'foreign_amount' => array_key_exists('foreign_amount', $journal) && null !== $journal['foreign_amount']
? Steam::negative((string) $journal['foreign_amount'])
: null,
'pc_amount' => array_key_exists('pc_amount', $journal) && null !== $journal['pc_amount']
? Steam::negative((string) $journal['pc_amount'])
: null,
'date' => $journal['date'],
'source_account_id' => $journal['source_account_id'],
'budget_name' => $journal['budget_name'],
@@ -186,8 +190,12 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
'currency_code' => (string) $journal['currency_code'],
'currency_decimal_places' => (int) $journal['currency_decimal_places'],
'foreign_currency_id' => (int) ($journal['foreign_currency_id'] ?? 0),
'foreign_amount' => isset($journal['foreign_amount']) ? Steam::positive((string) $journal['foreign_amount']) : null,
'pc_amount' => isset($journal['pc_amount']) ? Steam::positive((string) $journal['pc_amount']) : null,
'foreign_amount' => array_key_exists('foreign_amount', $journal) && null !== $journal['foreign_amount']
? Steam::positive((string) $journal['foreign_amount'])
: null,
'pc_amount' => array_key_exists('pc_amount', $journal) && null !== $journal['pc_amount']
? Steam::positive((string) $journal['pc_amount'])
: null,
'date' => $journal['date'],
'source_account_id' => $journal['source_account_id'],
'budget_name' => $journal['budget_name'],

View File

@@ -253,10 +253,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte
// group has multiple members. How many are owner, except the user we're editing now?
$ownerCount = $userGroup->groupMemberships()->where('user_role_id', $owner->id)->where('user_id', '!=', $user->id)->count();
// if there are no other owners and the current users does not get or keep the owner role, refuse.
if (
0 === $ownerCount
&& (0 === count($data['roles']) || count($data['roles']) > 0 && !in_array(UserRoleEnum::OWNER->value, $data['roles'], true))
) {
if (0 === $ownerCount && (0 === count($data['roles']) || !in_array(UserRoleEnum::OWNER->value, $data['roles'], true))) {
Log::debug('User needs to keep owner role in this group, refuse to act');
throw new FireflyException('The last owner in this user group must keep the "owner" role.');

View File

@@ -41,9 +41,6 @@ class IsValidAccountTypeList implements ValidationRule
if (is_string($value)) {
$values = explode(',', $value);
}
if (!is_array($values)) {
$fail('validation.invalid_account_list')->translate();
}
$keys = array_keys($this->types);
foreach ($values as $entry) {
if (!in_array($entry, $keys, true)) {

View File

@@ -41,9 +41,6 @@ class IsValidTransactionTypeList implements ValidationRule
if (is_string($value)) {
$values = explode(',', $value);
}
if (!is_array($values)) {
$fail('validation.invalid_transaction_type_list')->translate();
}
$keys = array_keys($this->transactionTypes);
foreach ($values as $entry) {
if (!in_array($entry, $keys, true)) {

View File

@@ -86,6 +86,8 @@ class AccountDestroyService
foreach ($collection as $row) {
if ((int) $row->the_count > 1) {
$journalId = $row->transaction_journal_id;
/** @var null|TransactionJournal $journal */
$journal = $user->transactionJournals()->find($journalId);
if (null !== $journal) {
Log::debug(sprintf('Deleted journal #%d because it has the same source as destination.', $journal->id));

View File

@@ -144,15 +144,17 @@ class RemoteUserGuard implements Guard
return $this->user?->id;
}
public function setUser(Authenticatable|User|null $user): void
public function setUser(Authenticatable|User|null $user): Guard
{
// Log::debug(sprintf('Now at %s', __METHOD__));
if ($user instanceof User) {
$this->user = $user;
return;
return $this;
}
Log::error(sprintf('Did not set user at %s', __METHOD__));
return $this;
}
public function user(): ?User

View File

@@ -43,14 +43,12 @@ class BudgetList implements BinderInterface
if ('allBudgets' === $value) {
return auth()->user()->budgets()->where('active', true)->orderBy('order', 'ASC')->orderBy('name', 'ASC')->get();
}
$list = array_unique(array_map(\intval(...), explode(',', $value)));
if (0 === count($list)) {
if ('' === $value) {
Log::warning('Budget list count is zero, return 404.');
throw new NotFoundHttpException();
}
$list = array_unique(array_map(\intval(...), explode(',', $value)));
/** @var Collection $collection */
$collection = auth()->user()->budgets()->where('active', true)->whereIn('id', $list)->get();

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Support\Binder;
use FireflyIII\Models\Category;
use Illuminate\Routing\Route;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
@@ -42,11 +43,12 @@ class CategoryList implements BinderInterface
if ('allCategories' === $value) {
return auth()->user()->categories()->orderBy('name', 'ASC')->get();
}
if ('' === $value) {
Log::warning('Category list count is zero, return 404.');
$list = array_unique(array_map(\intval(...), explode(',', $value)));
if (0 === count($list)) {
throw new NotFoundHttpException();
}
$list = array_unique(array_map(\intval(...), explode(',', $value)));
/** @var Collection $collection */
$collection = auth()->user()->categories()->whereIn('id', $list)->get();

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Support\Binder;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use Illuminate\Routing\Route;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
@@ -65,11 +66,12 @@ class JournalList implements BinderInterface
protected static function parseList(string $value): array
{
$list = array_unique(array_map(\intval(...), explode(',', $value)));
if (0 === count($list)) {
if ('' === $value) {
Log::warning('Category list count is zero, return 404.');
throw new NotFoundHttpException();
}
return $list;
return array_unique(array_map(\intval(...), explode(',', $value)));
}
}

View File

@@ -44,15 +44,15 @@ class TagList implements BinderInterface
if ('allTags' === $value) {
return auth()->user()->tags()->orderBy('tag', 'ASC')->get();
}
$list = array_unique(array_map(\strtolower(...), explode(',', $value)));
Log::debug('List of tags is', $list);
if (0 === count($list)) {
Log::error('Tag list is empty.');
if ('' === $value) {
Log::warning('Category list count is zero, return 404.');
throw new NotFoundHttpException();
}
$list = array_unique(array_map(\strtolower(...), explode(',', $value)));
Log::debug('List of tags is', $list);
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$repository->setUser(auth()->user());

View File

@@ -89,16 +89,15 @@ trait ValidatesUserGroupTrait
throw new AuthorizationException((string) trans('validation.belongs_user_or_user_group'));
}
Log::debug(sprintf('validateUserGroup: validate access of user to group #%d ("%s").', $groupId, $group->title));
$roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : [];
if (0 === count($roles)) {
if (0 === count($this->acceptedRoles)) {
Log::debug('validateUserGroup: no roles defined, so no access.');
throw new AuthorizationException((string) trans('validation.no_accepted_roles_defined'));
}
Log::debug(sprintf('validateUserGroup: have %d roles to check.', count($roles)), $roles);
Log::debug(sprintf('validateUserGroup: have %d roles to check.', count($this->acceptedRoles)), $this->acceptedRoles);
/** @var UserRoleEnum $role */
foreach ($roles as $role) {
foreach ($this->acceptedRoles as $role) {
if ($user->hasRoleInGroupOrOwner($group, $role)) {
Log::debug(sprintf('validateUserGroup: User has role "%s" in group #%d, return the group.', $role->value, $groupId));
$this->userGroup = $group;

View File

@@ -128,6 +128,12 @@ class BillDateCalculator
}
}
Log::debug('end of loop');
/** @template T
* @param Carbon $date
*
* @return null|T
*/
$simple = $set->map(static fn (Carbon $date) => $date->format('Y-m-d'));
Log::debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray());

View File

@@ -201,25 +201,25 @@ class Navigation
Log::debug('endOfPeriod() requests "YTD" + future, set it to "3M" instead.');
$repeatFreq = '3M';
}
$new = Carbon::now();
$functionMap = [
'1D' => 'endOfDay',
'daily' => 'endOfDay',
'1W' => 'addWeek',
'week' => 'addWeek',
'weekly' => 'addWeek',
'1M' => 'addMonth',
'month' => 'addMonth',
'monthly' => 'addMonth',
'3M' => 'addQuarter',
'quarter' => 'addQuarter',
'quarterly' => 'addQuarter',
'1W' => 'addWeeks',
'week' => 'addWeeks',
'weekly' => 'addWeeks',
'1M' => 'addMonths',
'month' => 'addMonths',
'monthly' => 'addMonths',
'3M' => 'addQuarters',
'quarter' => 'addQuarters',
'quarterly' => 'addQuarters',
'6M' => 'addMonths',
'half-year' => 'addMonths',
'half_year' => 'addMonths',
'year' => 'addYear',
'yearly' => 'addYear',
'1Y' => 'addYear',
'year' => 'addYears',
'yearly' => 'addYears',
'1Y' => 'addYears',
];
$modifierMap = ['half-year' => 6, 'half_year' => 6, '6M' => 6];
$subDay = ['week', 'weekly', '1W', 'month', 'monthly', '1M', '3M', 'quarter', 'quarterly', '6M', 'half-year', 'half_year', '1Y', 'year', 'yearly'];

View File

@@ -45,8 +45,8 @@ trait ChecksLogin
if (!$check) {
return false;
}
if (!property_exists($this, 'acceptedRoles')) {
Log::debug(sprintf('Request class %s has no acceptedRoles array', static::class));
if (0 === count($this->acceptedRoles)) {
Log::debug(sprintf('Request class %s has 0 entries in acceptedRoles array', static::class));
return true; // check for false already took place.
}

View File

@@ -205,10 +205,9 @@ trait ConvertsDataTypes
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
if (method_exists($this, 'validateUserGroup')) {
$userGroup = $this->validateUserGroup($this);
$repository->setUserGroup($userGroup);
}
// blindly assume this method exists.
$userGroup = $this->validateUserGroup($this);
$repository->setUserGroup($userGroup);
// set administration ID
// group ID

View File

@@ -487,7 +487,7 @@ class OperatorQuerySearch implements SearchInterface
// search direction: for destination accounts
if (SearchDirection::DESTINATION === $searchDirection) { // destination
// destination can be
// the destination account can be
$searchTypes = [
AccountTypeEnum::ASSET->value,
AccountTypeEnum::MORTGAGE->value,
@@ -530,7 +530,7 @@ class OperatorQuerySearch implements SearchInterface
}
// get accounts:
$accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337);
$accounts = $this->accountRepository->searchAccountIncludingInactive($value, $searchTypes, 1337);
if (0 === $accounts->count() && false === $prohibited) {
Log::warning('Found zero accounts, search for non existing account, NO results will be returned.');
$this->collector->findNothing();

View File

@@ -47,7 +47,6 @@ use Illuminate\Http\Middleware\HandleCors;
use Illuminate\Http\Middleware\ValidatePostSize;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
use PragmaRX\Google2FALaravel\Middleware as MFAMiddleware;
/*
@@ -63,19 +62,20 @@ use PragmaRX\Google2FALaravel\Middleware as MFAMiddleware;
bcscale(12);
if (!function_exists('envNonEmpty')) {
if (!function_exists('envDefaultWhenEmpty')) {
/**
*
* @return mixed|null
*/
function envNonEmpty(string $key, string | int | bool | null $default = null)
function envDefaultWhenEmpty(mixed $value, string | int | bool | null $default = null): mixed
{
$result = env($key, $default);
if ('' === $result) {
if(null === $value) {
return $default;
}
return $result;
if('' === $value) {
return $default;
}
return $value;
}
}

View File

@@ -3,11 +3,11 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.5.4 - 2026-03-06
## v6.5.4 - 2026-03-06
### Added
- Add some debug info to find
- Add some debug info to find problems with available budget calculations.
### Fixed
@@ -17,13 +17,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Security
- Published security advisory https://github.com/firefly-iii/firefly-iii/security/advisories/GHSA-5q8v-j673-m5v4 found and reported by @lighthousekeeper1212
- It's possible to submit webhook URLs that point to internal IP addresses. This will still be the case in the future, though some reserved ranges are no blocked. Let me know if this impacts you.
- It's possible to submit webhook URLs that point to internal IP addresses. This will still be the case in the future, though some reserved ranges are now blocked. Let me know if this impacts you.
### API
- Initial release.
## 6.5.3 - 2026-03-05
## v6.5.3 - 2026-03-05
This release fixes some sloppy coding on my part, but good news everyone! A new linter is in place that should prevent that from happening. Turns out I had disabled it in the past :(.
@@ -38,7 +34,7 @@ This release fixes some sloppy coding on my part, but good news everyone! A new
### Fixed
- [Issue 11866](https://github.com/firefly-iii/firefly-iii/issues/11866) (ReflectionException on Transaction\ListRequest) reported by @brot
## 6.5.2 - 2026-03-04
## v6.5.2 - 2026-03-04
### Changed
@@ -61,7 +57,7 @@ This release fixes some sloppy coding on my part, but good news everyone! A new
- [Issue 11822](https://github.com/firefly-iii/firefly-iii/issues/11822) (API - account transaction type filtering) reported by @mgrove36
- [Issue 11842](https://github.com/firefly-iii/firefly-iii/issues/11842) (API: `/api/v1/configuration` always returns unauthenticated for v6.5.1) reported by @dreautall
## 6.5.1 - 2026-02-28
## v6.5.1 - 2026-02-28
> [!IMPORTANT]
> This releases also fixes a security issue, relevant only if you have multiple users using your Firefly III instance. Upgrading is recommended.

84
composer.lock generated
View File

@@ -6366,16 +6366,16 @@
},
{
"name": "symfony/cache",
"version": "v8.0.6",
"version": "v8.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
"reference": "59184fa14658d7724cd9b8743d91c1b1aa618bff"
"reference": "b7b0f4ce5fb57a8dc061d494639e44e2cf7aa30f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/59184fa14658d7724cd9b8743d91c1b1aa618bff",
"reference": "59184fa14658d7724cd9b8743d91c1b1aa618bff",
"url": "https://api.github.com/repos/symfony/cache/zipball/b7b0f4ce5fb57a8dc061d494639e44e2cf7aa30f",
"reference": "b7b0f4ce5fb57a8dc061d494639e44e2cf7aa30f",
"shasum": ""
},
"require": {
@@ -6442,7 +6442,7 @@
"psr6"
],
"support": {
"source": "https://github.com/symfony/cache/tree/v8.0.6"
"source": "https://github.com/symfony/cache/tree/v8.0.7"
},
"funding": [
{
@@ -6462,7 +6462,7 @@
"type": "tidelift"
}
],
"time": "2026-02-21T23:29:37+00:00"
"time": "2026-03-06T13:17:40+00:00"
},
{
"name": "symfony/cache-contracts",
@@ -6619,16 +6619,16 @@
},
{
"name": "symfony/console",
"version": "v7.4.6",
"version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "6d643a93b47398599124022eb24d97c153c12f27"
"reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/6d643a93b47398599124022eb24d97c153c12f27",
"reference": "6d643a93b47398599124022eb24d97c153c12f27",
"url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d",
"reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d",
"shasum": ""
},
"require": {
@@ -6693,7 +6693,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.4.6"
"source": "https://github.com/symfony/console/tree/v7.4.7"
},
"funding": [
{
@@ -6713,7 +6713,7 @@
"type": "tidelift"
}
],
"time": "2026-02-25T17:02:47+00:00"
"time": "2026-03-06T14:06:20+00:00"
},
{
"name": "symfony/css-selector",
@@ -7231,16 +7231,16 @@
},
{
"name": "symfony/http-client",
"version": "v8.0.6",
"version": "v8.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "f425139487f904e198f99e3c416c79ed08cef3c3"
"reference": "ade9bd433450382f0af154661fc8e72758b4de36"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/f425139487f904e198f99e3c416c79ed08cef3c3",
"reference": "f425139487f904e198f99e3c416c79ed08cef3c3",
"url": "https://api.github.com/repos/symfony/http-client/zipball/ade9bd433450382f0af154661fc8e72758b4de36",
"reference": "ade9bd433450382f0af154661fc8e72758b4de36",
"shasum": ""
},
"require": {
@@ -7303,7 +7303,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v8.0.6"
"source": "https://github.com/symfony/http-client/tree/v8.0.7"
},
"funding": [
{
@@ -7323,7 +7323,7 @@
"type": "tidelift"
}
],
"time": "2026-02-20T07:51:53+00:00"
"time": "2026-03-06T13:17:40+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -7405,16 +7405,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v7.4.6",
"version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "fd97d5e926e988a363cef56fbbf88c5c528e9065"
"reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/fd97d5e926e988a363cef56fbbf88c5c528e9065",
"reference": "fd97d5e926e988a363cef56fbbf88c5c528e9065",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/f94b3e7b7dafd40e666f0c9ff2084133bae41e81",
"reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81",
"shasum": ""
},
"require": {
@@ -7463,7 +7463,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v7.4.6"
"source": "https://github.com/symfony/http-foundation/tree/v7.4.7"
},
"funding": [
{
@@ -7483,20 +7483,20 @@
"type": "tidelift"
}
],
"time": "2026-02-21T16:25:55+00:00"
"time": "2026-03-06T13:15:18+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v7.4.6",
"version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "002ac0cf4cd972a7fd0912dcd513a95e8a81ce83"
"reference": "3b3fcf386c809be990c922e10e4c620d6367cab1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/002ac0cf4cd972a7fd0912dcd513a95e8a81ce83",
"reference": "002ac0cf4cd972a7fd0912dcd513a95e8a81ce83",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/3b3fcf386c809be990c922e10e4c620d6367cab1",
"reference": "3b3fcf386c809be990c922e10e4c620d6367cab1",
"shasum": ""
},
"require": {
@@ -7582,7 +7582,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v7.4.6"
"source": "https://github.com/symfony/http-kernel/tree/v7.4.7"
},
"funding": [
{
@@ -7602,7 +7602,7 @@
"type": "tidelift"
}
],
"time": "2026-02-26T08:30:57+00:00"
"time": "2026-03-06T16:33:18+00:00"
},
{
"name": "symfony/mailer",
@@ -7760,16 +7760,16 @@
},
{
"name": "symfony/mime",
"version": "v7.4.6",
"version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "9fc881d95feae4c6c48678cb6372bd8a7ba04f5f"
"reference": "da5ab4fde3f6c88ab06e96185b9922f48b677cd1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/9fc881d95feae4c6c48678cb6372bd8a7ba04f5f",
"reference": "9fc881d95feae4c6c48678cb6372bd8a7ba04f5f",
"url": "https://api.github.com/repos/symfony/mime/zipball/da5ab4fde3f6c88ab06e96185b9922f48b677cd1",
"reference": "da5ab4fde3f6c88ab06e96185b9922f48b677cd1",
"shasum": ""
},
"require": {
@@ -7825,7 +7825,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.4.6"
"source": "https://github.com/symfony/mime/tree/v7.4.7"
},
"funding": [
{
@@ -7845,7 +7845,7 @@
"type": "tidelift"
}
],
"time": "2026-02-05T15:57:06+00:00"
"time": "2026-03-05T15:24:09+00:00"
},
{
"name": "symfony/options-resolver",
@@ -10373,16 +10373,16 @@
},
{
"name": "driftingly/rector-laravel",
"version": "2.1.11",
"version": "2.1.12",
"source": {
"type": "git",
"url": "https://github.com/driftingly/rector-laravel.git",
"reference": "84ea7e03f4a2d9d33e303559ed7e3280bfdb8d01"
"reference": "2a2175eefabca6d15c247d55de17c75dc2f787a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/84ea7e03f4a2d9d33e303559ed7e3280bfdb8d01",
"reference": "84ea7e03f4a2d9d33e303559ed7e3280bfdb8d01",
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/2a2175eefabca6d15c247d55de17c75dc2f787a9",
"reference": "2a2175eefabca6d15c247d55de17c75dc2f787a9",
"shasum": ""
},
"require": {
@@ -10403,9 +10403,9 @@
"description": "Rector upgrades rules for Laravel Framework",
"support": {
"issues": "https://github.com/driftingly/rector-laravel/issues",
"source": "https://github.com/driftingly/rector-laravel/tree/2.1.11"
"source": "https://github.com/driftingly/rector-laravel/tree/2.1.12"
},
"time": "2026-03-05T19:46:28+00:00"
"time": "2026-03-06T19:59:21+00:00"
},
{
"name": "ergebnis/phpstan-rules",

View File

@@ -36,12 +36,12 @@ use Illuminate\Support\Facades\URL;
use Spatie\Html\Facades\Html;
return [
'name' => envNonEmpty('APP_NAME', 'Firefly III'),
'env' => envNonEmpty('APP_ENV', 'production'),
'name' => envDefaultWhenEmpty(env('APP_NAME'), 'Firefly III'),
'env' => envDefaultWhenEmpty(env('APP_ENV'), 'production'),
'debug' => env('APP_DEBUG', false),
'url' => envNonEmpty('APP_URL', 'http://localhost'),
'timezone' => envNonEmpty('TZ', 'UTC'),
'locale' => envNonEmpty('DEFAULT_LANGUAGE', 'en_US'),
'url' => envDefaultWhenEmpty(env('APP_URL'), 'http://localhost'),
'timezone' => envDefaultWhenEmpty(env('TZ'), 'UTC'),
'locale' => envDefaultWhenEmpty(env('DEFAULT_LANGUAGE'), 'en_US'),
'fallback_locale' => 'en_US',
'key' => env('APP_KEY'),
'cipher' => 'AES-256-CBC',

View File

@@ -37,11 +37,11 @@ return [
*/
'defaults' => [
'guard' => envNonEmpty('AUTHENTICATION_GUARD', 'web'),
'guard' => envDefaultWhenEmpty(env('AUTHENTICATION_GUARD'), 'web'),
'passwords' => 'users',
],
'guard_header' => envNonEmpty('AUTHENTICATION_GUARD_HEADER', 'REMOTE_USER'),
'guard_email' => envNonEmpty('AUTHENTICATION_GUARD_EMAIL'),
'guard_header' => envDefaultWhenEmpty(env('AUTHENTICATION_GUARD_HEADER'), 'REMOTE_USER'),
'guard_email' => env('AUTHENTICATION_GUARD_EMAIL'),
/*
|--------------------------------------------------------------------------

View File

@@ -36,7 +36,7 @@ return [
|
*/
'default' => envNonEmpty('CACHE_DRIVER', 'file'),
'default' => envDefaultWhenEmpty(env('CACHE_DRIVER'), 'file'),
/*
|--------------------------------------------------------------------------

View File

@@ -41,15 +41,15 @@ if (false !== $databaseUrl) {
}
// Get SSL parameters from .env file.
$mysql_ssl_ca_dir = envNonEmpty('MYSQL_SSL_CAPATH');
$mysql_ssl_ca_file = envNonEmpty('MYSQL_SSL_CA');
$mysql_ssl_cert = envNonEmpty('MYSQL_SSL_CERT');
$mysql_ssl_key = envNonEmpty('MYSQL_SSL_KEY');
$mysql_ssl_ciphers = envNonEmpty('MYSQL_SSL_CIPHER');
$mysql_ssl_verify = envNonEmpty('MYSQL_SSL_VERIFY_SERVER_CERT');
$mysql_ssl_ca_dir = env('MYSQL_SSL_CAPATH');
$mysql_ssl_ca_file = env('MYSQL_SSL_CA');
$mysql_ssl_cert = env('MYSQL_SSL_CERT');
$mysql_ssl_key = env('MYSQL_SSL_KEY');
$mysql_ssl_ciphers = env('MYSQL_SSL_CIPHER');
$mysql_ssl_verify = env('MYSQL_SSL_VERIFY_SERVER_CERT');
$mySqlSSLOptions = [];
$useSSL = envNonEmpty('MYSQL_USE_SSL', false);
$useSSL = envDefaultWhenEmpty(env('MYSQL_USE_SSL'), false);
if (false !== $useSSL && null !== $useSSL && '' !== $useSSL) {
if (null !== $mysql_ssl_ca_dir) {
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CAPATH] = $mysql_ssl_ca_dir;
@@ -72,19 +72,19 @@ if (false !== $useSSL && null !== $useSSL && '' !== $useSSL) {
}
return [
'default' => envNonEmpty('DB_CONNECTION', 'mysql'),
'default' => envDefaultWhenEmpty(env('DB_CONNECTION'), 'mysql'),
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => envNonEmpty('DB_DATABASE', storage_path('database/database.sqlite')),
'database' => envDefaultWhenEmpty(env('DB_DATABASE'), storage_path('database/database.sqlite')),
'prefix' => '',
],
'mysql' => [
'driver' => 'mysql',
'host' => envNonEmpty('DB_HOST', $host),
'port' => envNonEmpty('DB_PORT', $port),
'database' => envNonEmpty('DB_DATABASE', $database),
'username' => envNonEmpty('DB_USERNAME', $username),
'host' => envDefaultWhenEmpty(env('DB_HOST'), $host),
'port' => envDefaultWhenEmpty(env('DB_PORT'), $port),
'database' => envDefaultWhenEmpty(env('DB_DATABASE'), $database),
'username' => envDefaultWhenEmpty(env('DB_USERNAME'), $username),
'password' => env('DB_PASSWORD', $password),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
@@ -96,19 +96,19 @@ return [
],
'pgsql' => [
'driver' => 'pgsql',
'host' => envNonEmpty('DB_HOST', $host),
'port' => envNonEmpty('DB_PORT', $port),
'database' => envNonEmpty('DB_DATABASE', $database),
'username' => envNonEmpty('DB_USERNAME', $username),
'host' => envDefaultWhenEmpty(env('DB_HOST'), $host),
'port' => envDefaultWhenEmpty(env('DB_PORT'), $port),
'database' => envDefaultWhenEmpty(env('DB_DATABASE'), $database),
'username' => envDefaultWhenEmpty(env('DB_USERNAME'), $username),
'password' => env('DB_PASSWORD', $password),
'charset' => 'utf8',
'prefix' => '',
'search_path' => envNonEmpty('PGSQL_SCHEMA', 'public'),
'schema' => envNonEmpty('PGSQL_SCHEMA', 'public'),
'sslmode' => envNonEmpty('PGSQL_SSL_MODE', 'prefer'),
'sslcert' => envNonEmpty('PGSQL_SSL_CERT'),
'sslkey' => envNonEmpty('PGSQL_SSL_KEY'),
'sslrootcert' => envNonEmpty('PGSQL_SSL_ROOT_CERT'),
'search_path' => envDefaultWhenEmpty(env('PGSQL_SCHEMA'), 'public'),
'schema' => envDefaultWhenEmpty(env('PGSQL_SCHEMA'), 'public'),
'sslmode' => envDefaultWhenEmpty(env('PGSQL_SSL_MODE'), 'prefer'),
'sslcert' => env('PGSQL_SSL_CERT'),
'sslkey' => env('PGSQL_SSL_KEY'),
'sslrootcert' => env('PGSQL_SSL_ROOT_CERT'),
],
'sqlsrv' => [
'driver' => 'sqlsrv',
@@ -139,21 +139,21 @@ return [
// 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'),
],
'default' => [
'scheme' => envNonEmpty('REDIS_SCHEME', 'tcp'),
'url' => envNonEmpty('REDIS_URL'),
'path' => envNonEmpty('REDIS_PATH'),
'host' => envNonEmpty('REDIS_HOST', '127.0.0.1'),
'port' => envNonEmpty('REDIS_PORT', 6379),
'scheme' => envDefaultWhenEmpty(env('REDIS_SCHEME'), 'tcp'),
'url' => env('REDIS_URL'),
'path' => env('REDIS_PATH'),
'host' => envDefaultWhenEmpty(env('REDIS_HOST'), '127.0.0.1'),
'port' => envDefaultWhenEmpty(env('REDIS_PORT'), 6379),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'scheme' => envNonEmpty('REDIS_SCHEME', 'tcp'),
'url' => envNonEmpty('REDIS_URL'),
'path' => envNonEmpty('REDIS_PATH'),
'host' => envNonEmpty('REDIS_HOST', '127.0.0.1'),
'port' => envNonEmpty('REDIS_PORT', 6379),
'scheme' => envDefaultWhenEmpty(env('REDIS_SCHEME'), 'tcp'),
'url' => env('REDIS_URL'),
'path' => env('REDIS_PATH'),
'host' => envDefaultWhenEmpty(env('REDIS_HOST'), '127.0.0.1'),
'port' => envDefaultWhenEmpty(env('REDIS_PORT'), 6379),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'database' => env('REDIS_CACHE_DB', '1'),

View File

@@ -75,14 +75,20 @@ return [
'webhooks' => true,
'handle_debts' => true,
'expression_engine' => true,
'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used.
'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-06',
'build_time' => 1772780248,
'version' => 'develop/2026-03-07',
'build_time' => 1772860710,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.
// Docker build info, if present:
'is_docker' => env('IS_DOCKER', false),
'base_image_build' => envDefaultWhenEmpty(env('BASE_IMAGE_BUILD'), '(unknown)'),
'base_image_date' => envDefaultWhenEmpty(env('BASE_IMAGE_DATE'), '(unknown)'),
'is_local_dev' => env('IS_LOCAL_DEV', false),
// generic settings
'maxUploadSize' => 1073741824, // 1 GB
'send_error_message' => env('SEND_ERROR_MESSAGE', true),
@@ -91,7 +97,7 @@ return [
// tokens and keys
'fixer_api_key' => env('FIXER_API_KEY', ''),
'ipinfo_token' => env('IPINFO_TOKEN', ''),
'static_cron_token' => envNonEmpty('STATIC_CRON_TOKEN'),
'static_cron_token' => env('STATIC_CRON_TOKEN'),
// flags
'enable_external_map' => env('ENABLE_EXTERNAL_MAP', false), // no longer used, only for default.
@@ -106,8 +112,8 @@ return [
'tracker_url' => env('TRACKER_URL', ''),
// authentication settings
'authentication_guard' => envNonEmpty('AUTHENTICATION_GUARD', 'web'),
'custom_logout_url' => envNonEmpty('CUSTOM_LOGOUT_URL', ''),
'authentication_guard' => envDefaultWhenEmpty(env('AUTHENTICATION_GUARD'), 'web'),
'custom_logout_url' => envDefaultWhenEmpty(env('CUSTOM_LOGOUT_URL'), ''),
// static config (cannot be changed by user)
'update_endpoint' => 'https://version.firefly-iii.org/index.json',
@@ -188,8 +194,8 @@ return [
'convertToPrimary' => false,
],
'default_currency' => 'EUR',
'default_language' => envNonEmpty('DEFAULT_LANGUAGE', 'en_US'),
'default_locale' => envNonEmpty('DEFAULT_LOCALE', 'equal'),
'default_language' => envDefaultWhenEmpty(env('DEFAULT_LANGUAGE'), 'en_US'),
'default_locale' => envDefaultWhenEmpty(env('DEFAULT_LOCALE'), 'equal'),
// account types that may have or set a currency
'valid_currency_account_types' => [
@@ -218,7 +224,7 @@ return [
'available_dark_modes' => ['light', 'dark', 'browser'],
'bill_reminder_periods' => [90, 30, 14, 7, 0],
'valid_view_ranges' => ['1D', '1W', '1M', '3M', '6M', '1Y'],
'valid_url_protocols' => envNonEmpty('VALID_URL_PROTOCOLS', 'http,https,ftp,ftps,mailto'), // no longer used, only for default.
'valid_url_protocols' => envDefaultWhenEmpty(env('VALID_URL_PROTOCOLS'), 'http,https,ftp,ftps,mailto'), // no longer used, only for default.
'allowedMimes' => [
// plain files
'text/plain',

View File

@@ -34,8 +34,8 @@ $validChannels = ['single', 'papertrail', 'stdout', 'daily', 'syslog', 'err
$validAuditChannels = ['audit_papertrail', 'audit_stdout', 'audit_stdout', 'audit_daily', 'audit_syslog', 'audit_errorlog'];
// which settings did the user set, if any?
$defaultLogChannel = (string) envNonEmpty('LOG_CHANNEL', 'stack');
$auditLogChannel = (string) envNonEmpty('AUDIT_LOG_CHANNEL', '');
$defaultLogChannel = (string) envDefaultWhenEmpty(env('LOG_CHANNEL'), 'stack');
$auditLogChannel = (string) env('AUDIT_LOG_CHANNEL');
if ('stack' === $defaultLogChannel) {
$defaultChannels = ['daily', 'stdout'];
@@ -60,8 +60,8 @@ return [
|
*/
'default' => envNonEmpty('LOG_CHANNEL', 'stack'),
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'default' => envDefaultWhenEmpty(env('LOG_CHANNEL'), 'stack'),
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
/*
|--------------------------------------------------------------------------
| Log Channels
@@ -93,11 +93,11 @@ return [
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
],
'papertrail' => [
'driver' => 'monolog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
'handler' => SyslogUdpHandler::class,
'handler_with' => [
'host' => env('PAPERTRAIL_HOST'),
@@ -107,21 +107,21 @@ return [
'stdout' => [
'driver' => 'single',
'path' => 'php://stdout',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/ff3-'.PHP_SAPI.'.log'),
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
'days' => 7,
],
'syslog' => [
'driver' => 'syslog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
],
'errorlog' => [
'driver' => 'errorlog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
],
/*
@@ -130,7 +130,7 @@ return [
*/
'audit_papertrail' => [
'driver' => 'monolog',
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
'handler' => SyslogUdpHandler::class,
'tap' => [AuditLogger::class],
'handler_with' => [
@@ -142,24 +142,24 @@ return [
'driver' => 'single',
'path' => 'php://stdout',
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
],
'audit_daily' => [
'driver' => 'daily',
'path' => storage_path('logs/ff3-audit.log'),
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
'days' => 90,
],
'audit_syslog' => [
'driver' => 'syslog',
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
],
'audit_errorlog' => [
'driver' => 'errorlog',
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
],
],
];

View File

@@ -34,16 +34,16 @@ return [
| and used as needed; however, this mailer will be used by default.
|
*/
'default' => envNonEmpty('MAIL_MAILER', 'log'),
'default' => envDefaultWhenEmpty(env('MAIL_MAILER'), 'log'),
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'host' => envNonEmpty('MAIL_HOST', 'smtp.mailtrap.io'),
'host' => envDefaultWhenEmpty(env('MAIL_HOST'), 'smtp.mailtrap.io'),
'port' => (int) env('MAIL_PORT', 2525),
'encryption' => envNonEmpty('MAIL_ENCRYPTION', 'tls'),
'username' => envNonEmpty('MAIL_USERNAME', 'user@example.com'),
'password' => envNonEmpty('MAIL_PASSWORD', 'password'),
'encryption' => envDefaultWhenEmpty(env('MAIL_ENCRYPTION'), 'tls'),
'username' => envDefaultWhenEmpty(env('MAIL_USERNAME'), 'user@example.com'),
'password' => envDefaultWhenEmpty(env('MAIL_PASSWORD'), 'password'),
'timeout' => null,
'scheme' => env('MAIL_SCHEME'),
'url' => env('MAIL_URL'),
@@ -73,7 +73,7 @@ return [
'sendmail' => [
'transport' => 'sendmail',
'path' => envNonEmpty('MAIL_SENDMAIL_COMMAND', '/usr/sbin/sendmail -bs'),
'path' => envDefaultWhenEmpty(env('MAIL_SENDMAIL_COMMAND'), '/usr/sbin/sendmail -bs'),
],
'log' => [
'transport' => 'log',
@@ -91,7 +91,7 @@ return [
],
],
'from' => ['address' => envNonEmpty('MAIL_FROM', 'changeme@example.com'), 'name' => 'Firefly III Mailer'],
'from' => ['address' => envDefaultWhenEmpty(env('MAIL_FROM'), 'changeme@example.com'), 'name' => 'Firefly III Mailer'],
'markdown' => [
'theme' => 'default',

View File

@@ -34,7 +34,7 @@ return [
|
*/
'guard' => envNonEmpty('AUTHENTICATION_GUARD', 'web'),
'guard' => envDefaultWhenEmpty(env('AUTHENTICATION_GUARD'), 'web'),
/*
|--------------------------------------------------------------------------

View File

@@ -29,7 +29,7 @@ use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
protected static function hasForeign(string $table, string $column): bool
private static function hasForeign(string $table, string $column): bool
{
$foreignKeysDefinitions = Schema::getForeignKeys($table);
foreach ($foreignKeysDefinitions as $foreignKeyDefinition) {

30
package-lock.json generated
View File

@@ -200,9 +200,9 @@
}
},
"node_modules/@babel/helper-define-polyfill-provider": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz",
"integrity": "sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==",
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.7.tgz",
"integrity": "sha512-6Fqi8MtQ/PweQ9xvux65emkLQ83uB+qAVtfHkC9UodyHMIZdxNI01HjLCLUtybElp2KY2XNE0nOgyP1E1vXw9w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1586,13 +1586,13 @@
}
},
"node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.0.tgz",
"integrity": "sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==",
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.1.tgz",
"integrity": "sha512-ENp89vM9Pw4kv/koBb5N2f9bDZsR0hpf3BdPMOg/pkS3pwO4dzNnQZVXtBbeyAadgm865DmQG2jMMLqmZXvuCw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.6",
"@babel/helper-define-polyfill-provider": "^0.6.7",
"core-js-compat": "^3.48.0"
},
"peerDependencies": {
@@ -4055,14 +4055,14 @@
}
},
"node_modules/babel-plugin-polyfill-corejs2": {
"version": "0.4.15",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz",
"integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==",
"version": "0.4.16",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.16.tgz",
"integrity": "sha512-xaVwwSfebXf0ooE11BJovZYKhFjIvQo7TsyVpETuIeH2JHv0k/T6Y5j22pPTvqYqmpkxdlPAJlyJ0tfOJAoMxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.28.6",
"@babel/helper-define-polyfill-provider": "^0.6.6",
"@babel/helper-define-polyfill-provider": "^0.6.7",
"semver": "^6.3.1"
},
"peerDependencies": {
@@ -4094,13 +4094,13 @@
}
},
"node_modules/babel-plugin-polyfill-regenerator": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz",
"integrity": "sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==",
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.7.tgz",
"integrity": "sha512-OTYbUlSwXhNgr4g6efMZgsO8//jA61P7ZbRX3iTT53VON8l+WQS8IAUEVo4a4cWknrg2W8Cj4gQhRYNCJ8GkAA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.6"
"@babel/helper-define-polyfill-provider": "^0.6.7"
},
"peerDependencies": {
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"