Compare commits

...

40 Commits

Author SHA1 Message Date
github-actions[bot]
a0c423b9ed Merge pull request #12017 from firefly-iii/release-1774238341
🤖 Automatically merge the PR into the develop branch.
2026-03-23 04:59:09 +01:00
JC5
2844929351 🤖 Auto commit for release 'develop' on 2026-03-23 2026-03-23 04:59:01 +01:00
James Cole
c0d4a70d46 Update changelog for next release. 2026-03-22 17:10:47 +01:00
James Cole
a62f8bbaff Fix https://github.com/firefly-iii/firefly-iii/issues/12014 2026-03-22 17:05:31 +01:00
James Cole
b115b4ad92 Improve PR template. 2026-03-22 13:38:04 +01:00
github-actions[bot]
dc60025097 Merge pull request #12012 from firefly-iii/release-1774166607
🤖 Automatically merge the PR into the develop branch.
2026-03-22 09:03:34 +01:00
JC5
d77769b2f4 🤖 Auto commit for release 'develop' on 2026-03-22 2026-03-22 09:03:27 +01:00
James Cole
c6497960f8 Try updating package lock/ 2026-03-22 08:58:15 +01:00
James Cole
4eee0c79cd Go to vite plugin 3. 2026-03-22 08:36:43 +01:00
James Cole
e333c3254b Merge pull request #11961 from firefly-iii/dependabot/npm_and_yarn/develop/vite-8.0.0
Bump vite from 7.3.1 to 8.0.0
2026-03-22 07:21:07 +01:00
github-actions[bot]
9d244883a1 Merge pull request #12007 from firefly-iii/release-1774159543
🤖 Automatically merge the PR into the develop branch.
2026-03-22 07:05:54 +01:00
JC5
a17131c2f7 🤖 Auto commit for release 'develop' on 2026-03-22 2026-03-22 07:05:43 +01:00
James Cole
898459198d Fix null pointer. 2026-03-22 07:00:27 +01:00
James Cole
6466bc9272 Restore currency functionality. 2026-03-22 06:57:27 +01:00
James Cole
dd8a8dba85 Merge pull request #12005 from IDevJoe/main
Adjust $request->only() in testNotification function
2026-03-22 06:42:41 +01:00
James Cole
ee16888317 Catch null pointer. 2026-03-22 06:41:55 +01:00
Joe Longendyke
56a2580fd7 Fix testNotification function
Signed-off-by: Joe Longendyke <IDevJoe@users.noreply.github.com>
2026-03-21 16:05:03 -04:00
github-actions[bot]
2ab0225223 Merge pull request #12003 from firefly-iii/release-1774107220
🤖 Automatically merge the PR into the develop branch.
2026-03-21 16:33:48 +01:00
JC5
83662415c3 🤖 Auto commit for release 'develop' on 2026-03-21 2026-03-21 16:33:41 +01:00
James Cole
cf976b2ab1 Throw the error still to find out what's happening. 2026-03-21 16:28:19 +01:00
James Cole
bf79c9db72 Also add post data when PUT. 2026-03-21 16:19:05 +01:00
James Cole
29f4c09a7b Switch to unreported error to cut down on spam. 2026-03-21 16:17:52 +01:00
James Cole
22ef456dca Remove admin access from routes. 2026-03-21 16:15:15 +01:00
github-actions[bot]
9c706465b2 Merge pull request #12001 from firefly-iii/release-1774102841
🤖 Automatically merge the PR into the develop branch.
2026-03-21 15:20:48 +01:00
JC5
a40425fd75 🤖 Auto commit for release 'develop' on 2026-03-21 2026-03-21 15:20:41 +01:00
James Cole
e9a37592ba Improved sorting and slicing for https://github.com/firefly-iii/firefly-iii/issues/12000 2026-03-21 15:05:00 +01:00
James Cole
0c598cb034 Fix changelog header. 2026-03-21 11:57:08 +01:00
github-actions[bot]
f5dea9ac09 Merge pull request #11999 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-03-21 11:54:49 +01:00
github-actions[bot]
4ef7944147 Merge pull request #11998 from firefly-iii/release-1774090477
🤖 Automatically merge the PR into the develop branch.
2026-03-21 11:54:45 +01:00
JC5
4e1c84944c 🤖 Auto commit for release 'v6.5.8' on 2026-03-21 2026-03-21 11:54:37 +01:00
github-actions[bot]
f36da26cc3 Merge pull request #11997 from firefly-iii/release-1774090121
🤖 Automatically merge the PR into the develop branch.
2026-03-21 11:48:48 +01:00
JC5
5983a8eb6d 🤖 Auto commit for release 'develop' on 2026-03-21 2026-03-21 11:48:41 +01:00
James Cole
b4a8a219ff Fix https://github.com/firefly-iii/firefly-iii/issues/11995 2026-03-21 11:42:55 +01:00
github-actions[bot]
4190c4d243 Merge pull request #11994 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-03-21 07:44:44 +01:00
github-actions[bot]
70cbbc1523 Merge pull request #11993 from firefly-iii/release-1774075474
🤖 Automatically merge the PR into the develop branch.
2026-03-21 07:44:40 +01:00
JC5
c724f13501 🤖 Auto commit for release 'v6.5.7' on 2026-03-21 2026-03-21 07:44:34 +01:00
James Cole
5f01a83b43 Fix phpstan issues. 2026-03-21 07:36:52 +01:00
James Cole
53c13d221d Clean up API routes. 2026-03-21 07:27:10 +01:00
James Cole
266cd7d8d0 Update changelog and html rendering. 2026-03-21 07:01:42 +01:00
dependabot[bot]
974c84a877 Bump vite from 7.3.1 to 8.0.0
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.3.1 to 8.0.0.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/create-vite@8.0.0/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 03:52:51 +00:00
26 changed files with 725 additions and 950 deletions

View File

@@ -6,6 +6,8 @@
2. If your PR is more than 25 lines, talk to me FIRST.
3. If you fix spelling or code comments, talk to me FIRST.
This is to prevent AI bots, low-effort PRs and spam. Sorry about that.
Wanna talk to me? Open a GitHub Issue, Discussion, or email me: james@firefly-iii.org
👀 Please ensure you have taken a look at the contribution guidelines:
@@ -17,7 +19,9 @@ Remember that your PR may be CLOSED:
2. If you open a PR on the main branch, your PR will be CLOSED.
3. If you only fix a spelling error or code comment, your PR will be CLOSED.
Thanks again, and happy developing!
Again, this is to prevent AI bots, low-effort PRs and spam. I apologize for the harsh tone.
But if you made it this far thanks again for contributing, and happy developing!
-->
@@ -48,3 +52,6 @@ I used AI assistance for:
<!--
Thanks for contributing!
-->
@JC5

View File

@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
## 2026
- Joe Longendyke
- Daniel Holøien
- Matthew Grove
- Cinnamon Pyro

View File

@@ -28,9 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
@@ -41,7 +39,6 @@ use Illuminate\Validation\ValidationException;
final class DestroyController extends Controller
{
private CurrencyRepositoryInterface $repository;
private UserRepositoryInterface $userRepository;
/**
* CurrencyRepository constructor.
@@ -50,8 +47,7 @@ final class DestroyController extends Controller
{
parent::__construct();
$this->middleware(function ($request, $next) {
$this->repository = app(CurrencyRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
$this->repository = app(CurrencyRepositoryInterface::class);
$this->repository->setUser(auth()->user());
return $next($request);
@@ -69,15 +65,8 @@ final class DestroyController extends Controller
*/
public function destroy(TransactionCurrency $currency): JsonResponse
{
/** @var User $admin */
$admin = auth()->user();
$rules = ['currency_code' => 'required'];
if (!$this->userRepository->hasRole($admin, 'owner')) {
// access denied:
$messages = ['currency_code' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
if ($this->repository->currencyInUse($currency)) {
$messages = ['currency_code' => '200006: Currency in use.'];
Validator::make([], $rules, $messages)->validate();

View File

@@ -35,7 +35,6 @@ use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\CurrencyTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use League\Fractal\Resource\Item;
/**
@@ -154,7 +153,6 @@ final class UpdateController extends Controller
public function update(UpdateRequest $request, TransactionCurrency $currency): JsonResponse
{
$data = $request->getAll();
Log::debug(__METHOD__, $data);
/** @var User $user */
$user = auth()->user();

View File

@@ -32,7 +32,6 @@ 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
@@ -72,11 +71,6 @@ 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.');
return response()->json([], 401);
}
$this->repository->destroy($linkType);
Preferences::mark();

View File

@@ -27,12 +27,10 @@ namespace FireflyIII\Api\V1\Controllers\Models\TransactionLinkType;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\TransactionLinkType\StoreRequest;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use League\Fractal\Resource\Item;
@@ -44,7 +42,6 @@ final class StoreController extends Controller
use TransactionFilter;
private LinkTypeRepositoryInterface $repository;
private UserRepositoryInterface $userRepository;
/**
* LinkTypeController constructor.
@@ -54,9 +51,8 @@ final class StoreController extends Controller
parent::__construct();
$this->middleware(function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(LinkTypeRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
$user = auth()->user();
$this->repository = app(LinkTypeRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
@@ -73,15 +69,6 @@ final class StoreController extends Controller
*/
public function store(StoreRequest $request): JsonResponse
{
/** @var User $admin */
$admin = auth()->user();
$rules = ['name' => 'required'];
if (!$this->userRepository->hasRole($admin, 'owner')) {
// access denied:
$messages = ['name' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
$data = $request->getAll();
// if currency ID is 0, find the currency by the code:
$linkType = $this->repository->store($data);

View File

@@ -29,12 +29,10 @@ use FireflyIII\Api\V1\Requests\Models\TransactionLinkType\UpdateRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\LinkType;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use League\Fractal\Resource\Item;
@@ -46,7 +44,6 @@ final class UpdateController extends Controller
use TransactionFilter;
private LinkTypeRepositoryInterface $repository;
private UserRepositoryInterface $userRepository;
/**
* LinkTypeController constructor.
@@ -56,9 +53,8 @@ final class UpdateController extends Controller
parent::__construct();
$this->middleware(function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(LinkTypeRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
$user = auth()->user();
$this->repository = app(LinkTypeRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
@@ -80,15 +76,6 @@ final class UpdateController extends Controller
throw new FireflyException('200020: Link type cannot be changed.');
}
/** @var User $admin */
$admin = auth()->user();
$rules = ['name' => 'required'];
if (!$this->userRepository->hasRole($admin, 'owner')) {
$messages = ['name' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
$data = $request->getAll();
$this->repository->update($linkType, $data);
$manager = $this->getManager();

View File

@@ -30,12 +30,10 @@ use FireflyIII\Enums\WebhookDelivery;
use FireflyIII\Enums\WebhookResponse;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Binder\EitherConfigKey;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
/**
@@ -43,21 +41,6 @@ use Illuminate\Validation\ValidationException;
*/
final class ConfigurationController extends Controller
{
private UserRepositoryInterface $repository;
/**
* ConfigurationController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(function ($request, $next) {
$this->repository = app(UserRepositoryInterface::class);
return $next($request);
});
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/configuration/getConfiguration
@@ -142,11 +125,6 @@ final class ConfigurationController extends Controller
*/
public function update(UpdateRequest $request, string $name): JsonResponse
{
$rules = ['value' => 'required'];
if (!$this->repository->hasRole(auth()->user(), 'owner')) {
$messages = ['value' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
$data = $request->getAll();
$shortName = str_replace('configuration.', '', $name);

View File

@@ -74,13 +74,9 @@ final class UserController extends Controller
return response()->json([], 500);
}
if ($this->repository->hasRole($admin, 'owner')) {
$this->repository->destroy($user);
$this->repository->destroy($user);
return response()->json([], 204);
}
throw new FireflyException('200025: No access to function.');
return response()->json([], 204);
}
/**

View File

@@ -28,6 +28,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\User;
use Illuminate\Foundation\Http\FormRequest;
/**
@@ -45,15 +46,23 @@ class UpdateRequest extends FormRequest
*/
public function getAll(): array
{
// return nothing that isn't explicitly in the array:
$fields = [
'name' => ['name', 'convertString'],
'code' => ['code', 'convertString'],
'symbol' => ['symbol', 'convertString'],
'decimal_places' => ['decimal_places', 'convertInteger'],
'default' => ['default', 'boolean'],
'enabled' => ['enabled', 'boolean'],
/** @var User $user */
$user = auth()->user();
$isAdmin = $user->hasRole('owner');
$fields = [
'enabled' => ['enabled', 'boolean'],
];
if ($isAdmin) {
$fields = [
'name' => ['name', 'convertString'],
'code' => ['code', 'convertString'],
'symbol' => ['symbol', 'convertString'],
'decimal_places' => ['decimal_places', 'convertInteger'],
'default' => ['default', 'boolean'],
'enabled' => ['enabled', 'boolean'],
];
}
return $this->getAllData($fields);
}

View File

@@ -251,7 +251,7 @@ class Handler extends ExceptionHandler
'method' => request()->method(),
'headers' => $headers,
// @mago-expect lint:no-request-all
'post' => 'POST' === request()->method() ? json_encode(request()->all()) : '',
'post' => 'PUT' === request()->method() || 'POST' === request()->method() ? json_encode(request()->all()) : '',
];
// create job that will mail.

View File

@@ -75,8 +75,6 @@ class GroupCollector implements GroupCollectorInterface
$this->userGroup = null;
$this->limit = null;
$this->page = null;
$this->startRow = null;
$this->endRow = null;
$this->hasAccountInfo = false;
$this->hasCatInformation = false;
@@ -443,9 +441,15 @@ class GroupCollector implements GroupCollectorInterface
$this->query->orWhereIn('transaction_journals.transaction_group_id', $groupIds);
}
$result = $this->query->get($this->fields);
$this->total = $result->count();
// if no post-filters are present, it can be sliced and returned.
if (0 === count($this->sorting) && 0 === count($this->postFilters) && null !== $this->limit && null !== $this->page) {
$offset = ($this->page - 1) * $this->limit;
$result = $result->slice($offset, $this->limit);
}
// $this->dumpQueryInLogs();
// Log::debug(sprintf('Count of result is %d', $result->count()));
// now to parse this into an array.
// now to parse the rest into an array.
$collection = $this->parseArray($result);
// filter the array using all available post filters:
@@ -454,19 +458,12 @@ class GroupCollector implements GroupCollectorInterface
// sort the collection, if sort instructions are present.
$collection = $this->sortCollection($collection);
// count it and continue:
$this->total = $collection->count();
// now filter the array according to the page and the limit (if necessary)
if (null !== $this->limit && null !== $this->page) {
if (count($this->postFilters) > 0 && null !== $this->limit && null !== $this->page) {
$offset = ($this->page - 1) * $this->limit;
return $collection->slice($offset, $this->limit);
}
// OR filter the array according to the start and end row variable
if (null !== $this->startRow && null !== $this->endRow) {
return $collection->slice($this->startRow, $this->endRow);
}
return $collection;
}
@@ -477,17 +474,11 @@ class GroupCollector implements GroupCollectorInterface
public function getPaginatedGroups(): LengthAwarePaginator
{
Log::debug('Now in getPaginatedGroups()');
$set = $this->getGroups();
$limit = $this->limit ?? 1;
if (0 === $this->limit) {
$this->setLimit(50);
}
if (null !== $this->startRow && null !== $this->endRow) {
/** @var int $total */
$total = $this->endRow - $this->startRow;
return new LengthAwarePaginator($set, $this->total, $total, 1);
}
$limit = $this->limit ?? 1;
$set = $this->getGroups();
return new LengthAwarePaginator($set, $this->total, $limit, $this->page);
}
@@ -519,13 +510,6 @@ class GroupCollector implements GroupCollectorInterface
return $this;
}
public function setEndRow(int $endRow): self
{
$this->endRow = $endRow;
return $this;
}
public function setExpandGroupSearch(bool $expandGroupSearch): GroupCollectorInterface
{
$this->expandGroupSearch = $expandGroupSearch;
@@ -636,13 +620,6 @@ class GroupCollector implements GroupCollectorInterface
return $this;
}
public function setStartRow(int $startRow): self
{
$this->startRow = $startRow;
return $this;
}
/**
* Limit the search to one specific transaction group.
*/
@@ -692,6 +669,10 @@ class GroupCollector implements GroupCollectorInterface
#[Override]
public function sortCollection(Collection $collection): Collection
{
if (0 === count($this->sorting)) {
return $collection;
}
/**
* @var string $field
* @var string $direction

View File

@@ -469,11 +469,6 @@ interface GroupCollectorInterface
*/
public function setEnd(Carbon $end): self;
/**
* Set the page to get.
*/
public function setEndRow(int $endRow): self;
public function setExpandGroupSearch(bool $expandGroupSearch): self;
/**
@@ -573,11 +568,6 @@ interface GroupCollectorInterface
*/
public function setStart(Carbon $start): self;
/**
* Set the page to get.
*/
public function setStartRow(int $startRow): self;
/**
* Limit results to a specific tag.
*/

View File

@@ -132,7 +132,7 @@ final class NotificationController extends Controller
return redirect(route('settings.notification.index'));
}
$all = $request->only(['channel']);
$all = $request->only(['test_submit']);
$channel = $all['test_submit'] ?? '';
switch ($channel) {

View File

@@ -83,7 +83,7 @@ final class RegisterController extends Controller
throw new FireflyException('Registration is currently not available :(');
}
$this->validator($request->only(['email', 'password']))->validate();
$this->validator($request->only(['email', 'password', 'password_confirmation']))->validate();
$user = $this->createUser($request->only(['email', 'password']));
Log::info(sprintf('Registered new user %s', $user->email));
$owner = new OwnerNotifiable();

View File

@@ -0,0 +1,71 @@
<?php
/**
* IsAdmin.php
* Copyright (c) 2019 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\Http\Middleware;
use Closure;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
/**
* Class IsAdmin.
*/
class IsAdminApi
{
/**
* Handle an incoming request. Must be admin.
*
* @param null|string $guard
*
* @return mixed
*
* @throws AuthorizationException
*/
public function handle(Request $request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
}
return response()->redirectTo(route('login'));
}
/** @var User $user */
$user = auth()->user();
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
if (!$repository->hasRole($user, 'owner')) {
Log::error(sprintf('Cannot access %s?%s.', $request->url(), $request->getQueryString()));
throw new AuthorizationException();
}
return $next($request);
}
}

View File

@@ -202,7 +202,7 @@ class PrimaryAmountRecalculationService
/** @var Account $account */
foreach ($set as $account) {
$currencyId = (int) $account->accountMeta()->where('name', 'currency_id')->first()->data;
$currencyId = (int) $account->accountMeta()->where('name', 'currency_id')->first()?->data;
if ($groupCurrency->id === $currencyId) {
Log::debug(sprintf('Account "%s" is in group currency %s. Skip.', $account->name, $groupCurrency->code));

View File

@@ -774,8 +774,8 @@ class JournalUpdateService
$this->transactionJournal,
'update_foreign_amount',
[
'currency_symbol' => $oldForeignCurrency->symbol,
'decimal_places' => $oldForeignCurrency->decimal_places,
'currency_symbol' => $oldForeignCurrency?->symbol,
'decimal_places' => $oldForeignCurrency?->decimal_places,
'amount' => $originalSourceAmount,
],
[

View File

@@ -70,7 +70,7 @@ class Date implements BinderInterface
try {
$result = new Carbon($value);
} catch (InvalidDateException|InvalidFormatException $e) {
$message = sprintf('Could not parse date "%s" for user #%d: %s', $value, auth()->user()->id, $e->getMessage());
$message = sprintf('Could not parse date "%s" for user #%d: %s', $value, (int) auth()->user()?->id, $e->getMessage());
Log::error($message);
throw new NotFoundHttpException('Could not parse value', $e);

View File

@@ -29,6 +29,7 @@ use FireflyIII\Http\Middleware\EncryptCookies;
use FireflyIII\Http\Middleware\Installer;
use FireflyIII\Http\Middleware\InterestingMessage;
use FireflyIII\Http\Middleware\IsAdmin;
use FireflyIII\Http\Middleware\IsAdminApi;
use FireflyIII\Http\Middleware\Range;
use FireflyIII\Http\Middleware\RedirectIfAuthenticated;
use FireflyIII\Http\Middleware\SecureHeaders;
@@ -157,7 +158,7 @@ $app = Application::configure(basePath: dirname(__DIR__))
// This middleware is added to ensure that the user is not only logged in and
// authenticated (with MFA and everything), but also admin.
$middleware->appendToGroup('api-admin', [
IsAdmin::class,
IsAdminApi::class,
]);
$middleware->appendToGroup('admin', [
IsAdmin::class,

View File

@@ -3,9 +3,28 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## v6.5.9 - 2026-03-23
<!-- summary: Bug fixes mainly, but also updated dependencies and new wording in the instructions you see when you open a PR. -->
### Fixed
- [Issue 12004](https://github.com/firefly-iii/firefly-iii/issues/12004) (Test notification buttons always generate an error) reported by @IDevJoe
- [Issue 12014](https://github.com/firefly-iii/firefly-iii/issues/12014) (Converting a transaction to a transfer and setting the destination account to one with a different currency breaks the audit log) reported by @avee87
# Changed
- [Issue 12000](https://github.com/firefly-iii/firefly-iii/issues/12000) (Improved transaction pagination for large data sets) reported by @christiaanderidder
## v6.5.8 - 2026-03-22
<!-- summary: This release fixes a regression bug in user registration. -->
### Fixed
- [Issue 11995](https://github.com/firefly-iii/firefly-iii/issues/11995) (User registration breaks on password validation) reported by @mikaelhm
## v6.5.7 - 2026-03-21
<!-- summary: There is a new security policy for AI-generated security advisories and of course, interesting and annoying bugs fixed. -->
<!-- summary: There is a new security policy for AI-generated security advisories and of course, some interesting but annoying bugs fixed. -->
### Fixed

View File

@@ -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-20',
'build_time' => 1774047487,
'version' => 'develop/2026-03-23',
'build_time' => 1774238153,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.

1331
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,10 +9,10 @@
},
"devDependencies": {
"axios": "^1",
"laravel-vite-plugin": "^2",
"laravel-vite-plugin": "^3",
"patch-package": "^8",
"sass": "^1",
"vite": "^7",
"vite": "^8",
"vite-plugin-manifest-sri": "^0.2.0"
},
"dependencies": {

View File

@@ -20,6 +20,14 @@
export default class GenericObjectRenderer {
renderUrl(url, title, text) {
return `<a href="${url}" title="${title}">${text}</a>`;
return `<a href="${url}" title="${this.escapeHtml(title)}">${this.escapeHtml(text)}</a>`;
}
escapeHtml(unsafe) {
return unsafe
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
};
}

View File

@@ -347,7 +347,6 @@ Route::group(
'namespace' => 'FireflyIII\Api\V1\Controllers\Models\UserGroup',
'prefix' => 'v1/user-groups',
'as' => 'api.v1.user-groups.',
'middleware' => ['api-admin'],
],
static function (): void {
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
@@ -636,6 +635,7 @@ Route::group(
],
static function (): void {
Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']);
Route::put('{currency_code?}', ['uses' => 'UpdateController@update', 'as' => 'update']);
Route::get('primary', ['uses' => 'ShowController@showPrimary', 'as' => 'show.primary']);
Route::get('default', ['uses' => 'ShowController@showPrimary', 'as' => 'show.default']);
Route::get('{currency_code}', ['uses' => 'ShowController@show', 'as' => 'show']);
@@ -655,7 +655,7 @@ Route::group(
}
);
// transaction currency API routes that require admin rights:
// Transaction currency API routes that require admin rights:
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\Models\TransactionCurrency',
@@ -664,9 +664,8 @@ Route::group(
'middleware' => ['api-admin'],
],
static function (): void {
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::put('{currency_code?}', ['uses' => 'UpdateController@update', 'as' => 'update']);
Route::delete('{currency_code}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']);
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
}
);
@@ -696,11 +695,23 @@ Route::group(
],
static function (): void {
Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']);
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::get('{linkType}', ['uses' => 'ShowController@show', 'as' => 'show']);
Route::get('{linkType}/transactions', ['uses' => 'ListController@transactions', 'as' => 'transactions']);
}
);
// Transaction Link Type API routes that need admin rights.
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\Models\TransactionLinkType',
'prefix' => 'v1/link-types',
'as' => 'api.v1.link-types.',
'middleware' => ['api-admin'],
],
static function (): void {
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::put('{linkType}', ['uses' => 'UpdateController@update', 'as' => 'update']);
Route::delete('{linkType}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']);
Route::get('{linkType}/transactions', ['uses' => 'ListController@transactions', 'as' => 'transactions']);
}
);
@@ -740,10 +751,23 @@ Route::group(
],
static function (): void {
Route::get('', ['uses' => 'ConfigurationController@index', 'as' => 'index']);
Route::put('{dynamicConfigKey}', ['uses' => 'ConfigurationController@update', 'as' => 'update']);
Route::get('{eitherConfigKey}', ['uses' => 'ConfigurationController@show', 'as' => 'show']);
}
);
// Configuration API routes that need admin rights
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\System',
'prefix' => 'v1/configuration',
'as' => 'api.v1.configuration.',
'middleware' => ['api-admin'],
],
static function (): void {
Route::put('{dynamicConfigKey}', ['uses' => 'ConfigurationController@update', 'as' => 'update']);
}
);
// Users API routes:
Route::group(
[