Add better redirect rules.

This commit is contained in:
James Cole
2026-08-09 17:42:23 +02:00
parent 19542974d7
commit e9c82420e3
14 changed files with 131 additions and 42 deletions
@@ -44,7 +44,7 @@ final class CreateController extends Controller
{
use ModelInformation;
private AttachmentHelperInterface $attachments;
private AttachmentHelperInterface $attachments;
private AccountRepositoryInterface $repository;
/**
@@ -57,7 +57,7 @@ final class CreateController extends Controller
// translations:
$this->middleware(function ($request, $next) {
app('view')->share('mainTitleIcon', 'bi-credit-card');
app('view')->share('title', (string) trans('firefly.accounts'));
app('view')->share('title', (string)trans('firefly.accounts'));
$this->repository = app(AccountRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
@@ -71,11 +71,12 @@ final class CreateController extends Controller
*
* @return Factory|View
*/
public function create(Request $request, string $objectType): Factory|\Illuminate\Contracts\View\View
public function create(Request $request, string $objectType): Factory | \Illuminate\Contracts\View\View
{
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
$subTitle = (string) trans(sprintf('firefly.make_new_%s_account', $objectType));
$subTitle = (string)trans(sprintf('firefly.make_new_%s_account', $objectType));
$roles = $this->getRoles();
$from = $request->input('_from', '');
$liabilityTypes = $this->getLiabilityTypes();
$hasOldInput = null !== $request->old('_token');
$locations = [
@@ -89,18 +90,18 @@ final class CreateController extends Controller
$liabilityDirections = ['debit' => trans('firefly.liability_direction_debit'), 'credit' => trans('firefly.liability_direction_credit')];
// interest calculation periods:
$interestPeriods = [];
$interestPeriods = [];
foreach (config('firefly.interest_periods') as $period) {
$interestPeriods[$period] = trans(sprintf('firefly.interest_calc_%s', $period));
}
// pre fill some data
// prefill some data
$request->session()->flash('preFilled', [
'currency_id' => $this->primaryCurrency->id,
'include_net_worth' => !$hasOldInput || (bool) $request->old('include_net_worth'),
'include_net_worth' => !$hasOldInput || (bool)$request->old('include_net_worth'),
]);
// issue #8321
$showNetWorth = true;
$showNetWorth = true;
if ('liabilities' !== $objectType && 'asset' !== $objectType) {
$showNetWorth = false;
}
@@ -114,6 +115,7 @@ final class CreateController extends Controller
return view('accounts.create', [
'subTitleIcon' => $subTitleIcon,
'from' => $from,
'liabilityDirections' => $liabilityDirections,
'showNetWorth' => $showNetWorth,
'locations' => $locations,
@@ -127,9 +129,9 @@ final class CreateController extends Controller
public function store(AccountFormRequest $request): RedirectResponse
{
$data = $request->getAccountData();
$account = $this->repository->store($data);
$request->session()->flash('success', (string) trans('firefly.stored_new_account', ['name' => $account->name]));
$data = $request->getAccountData();
$account = $this->repository->store($data);
$request->session()->flash('success', (string)trans('firefly.stored_new_account', ['name' => $account->name]));
Preferences::mark();
Log::channel('audit')->info('Stored new account.', $data);
@@ -146,13 +148,13 @@ final class CreateController extends Controller
// store attachment(s):
/** @var null|array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($account, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
@@ -160,8 +162,9 @@ final class CreateController extends Controller
}
// redirect to previous URL.
$redirect = redirect($this->getPreviousUrl('accounts.create.url'));
if (1 === (int) $request->input('create_another')) {
$from = $request->input('_from', '');
$redirect = redirect(route('index') . $from);
if (1 === (int)$request->input('create_another')) {
// set value so create routine will not overwrite URL:
$request->session()->put('accounts.create.fromStore', true);
@@ -84,7 +84,7 @@ final class EditController extends Controller
if (!$this->isEditableAccount($account)) {
return $this->redirectAccountToAccount($account);
}
$from = $request->input('_from', '');
$objectType = config('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = (string) trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]);
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
@@ -165,6 +165,7 @@ final class EditController extends Controller
$request->session()->flash('preFilled', $preFilled);
return view('accounts.edit', [
'from'=>$from,
'account' => $account,
'currency' => $currency,
'canEditCurrency' => $canEditCurrency,
@@ -213,7 +214,8 @@ final class EditController extends Controller
}
// redirect
$redirect = redirect($this->getPreviousUrl('accounts.edit.url'));
$from = $request->input('_from', '');
$redirect = redirect(route('index') . $from);;
if (1 === (int) $request->input('return_to_edit')) {
// set value so edit routine will not overwrite URL:
$request->session()->put('accounts.edit.fromUpdate', true);
+1 -1
View File
@@ -87,7 +87,7 @@ abstract class Controller extends BaseController
View::share('DEMO_PASSWORD', config('firefly.demo_password'));
View::share('FF_VERSION', config('firefly.version'));
View::share('FF_BUILD_TIME', config('firefly.build_time'));
View::share('FF3_FROM', urlencode($this->getFromUrl()));
View::share('FF3_FROM', $this->getFromUrl());
// this breaks when running < PHP 8.5 and is totally intentional.
$input = ' James is cool';
$output = $input
+2
View File
@@ -27,6 +27,7 @@ use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Account;
use FireflyIII\Models\Location;
use FireflyIII\Rules\IsValidAmount;
use FireflyIII\Rules\System\IsValidOriginUrl;
use FireflyIII\Rules\UniqueIban;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Request\AppendsLocationData;
@@ -120,6 +121,7 @@ class AccountFormRequest extends FormRequest
'what' => 'in:'.$types,
'interest_period' => 'in:daily,monthly,yearly',
'notes' => ['min:1', 'max:32768', 'nullable'],
'_from' => ['string','min:0','max:255', new IsValidOriginUrl()],
];
$rules = Location::requestRules($rules);
+64
View File
@@ -0,0 +1,64 @@
<?php
/*
* IsValidOriginUrl.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Rules\System;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Safe\Exceptions\UrlException;
use function Safe\parse_url;
class IsValidOriginUrl implements ValidationRule
{
#[\Override]
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (!auth()->check()) {
$fail('validation.no_auth_present')->translate();
return;
}
$value = (string)$value;
if(str_contains($value, '%2F')) {
$value = urldecode($value);
}
if('' === $value) {
// string can be empty.
return;
}
try {
$parts = parse_url($value);
} catch (UrlException) {
$fail('validation.bad_url_parts')->translate();
return;
}
if(!array_key_exists('path', $parts) || array_key_exists('scheme', $parts) || array_key_exists('host', $parts)) {
$fail('validation.bad_url_parts')->translate();
return;
}
if(!str_starts_with($parts['path'], '/')) {
$fail('validation.bad_url_parts')->translate();
// return;
}
}
}