Compare commits

...

14 Commits

Author SHA1 Message Date
github-actions[bot]
f91063c88b Merge pull request #12184 from firefly-iii/release-1777100403
🤖 Automatically merge the PR into the develop branch.
2026-04-25 09:00:10 +02:00
JC5
30c1f4c13d 🤖 Auto commit for release 'develop' on 2026-04-25 2026-04-25 09:00:03 +02:00
James Cole
39be5075eb Clean up JS scripts. 2026-04-25 08:54:28 +02:00
James Cole
714133dad5 Merge pull request #12182 from tasnim0tantawi/bug/fix-sidebar-expanding-when-navigating
fix shrinked sidebar expanding when navigating by clicking on icons
2026-04-25 08:45:47 +02:00
James Cole
1caf639b85 Move to jQuery func 2026-04-25 08:45:20 +02:00
github-actions[bot]
33ad34a719 Merge pull request #12183 from firefly-iii/release-1777088715
🤖 Automatically merge the PR into the develop branch.
2026-04-25 05:45:23 +02:00
JC5
dca1d962af 🤖 Auto commit for release 'develop' on 2026-04-25 2026-04-25 05:45:16 +02:00
tasnim0tantawi
1ebff22785 fix delay 2026-04-24 22:42:22 +03:00
tasnim0tantawi
8b14a11969 lines 2026-04-24 22:28:06 +03:00
tasnim0tantawi
7b0e40543b all code in firefly js 2026-04-24 22:26:38 +03:00
tasnim0tantawi
ea57a0a8c8 remove code from default twig 2026-04-24 21:49:37 +03:00
tasnim0tantawi
62ee37f631 implement collapse save in v1 2026-04-24 21:17:19 +03:00
tasnim0tantawi
c96cc8d941 fix shrinked sidebar expanding when navigating by clicking on icons 2026-04-24 21:01:03 +03:00
James Cole
0e97b4a6b8 Add script and add exception. 2026-04-24 10:26:48 +02:00
10 changed files with 160 additions and 136 deletions

13
composer.lock generated
View File

@@ -11511,21 +11511,22 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "14.1.4",
"version": "14.1.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "13dd8d33c6a23da100acec635cd351bfa7ff1a98"
"reference": "4991e47adce8e31e554aee8fdaabfc3b1d60707d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/13dd8d33c6a23da100acec635cd351bfa7ff1a98",
"reference": "13dd8d33c6a23da100acec635cd351bfa7ff1a98",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4991e47adce8e31e554aee8fdaabfc3b1d60707d",
"reference": "4991e47adce8e31e554aee8fdaabfc3b1d60707d",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^5.7.0",
"php": ">=8.4",
@@ -11576,7 +11577,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.1.4"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.1.6"
},
"funding": [
{
@@ -11596,7 +11597,7 @@
"type": "tidelift"
}
],
"time": "2026-04-23T17:00:20+00:00"
"time": "2026-04-24T13:10:08+00:00"
},
{
"name": "phpunit/php-file-iterator",

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => (bool)env_default_when_empty(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-04-24',
'build_time' => 1777003029,
'version' => 'develop/2026-04-25',
'build_time' => 1777100403,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.

66
package-lock.json generated
View File

@@ -3350,9 +3350,9 @@
}
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3738,9 +3738,9 @@
"license": "MIT"
},
"node_modules/body-parser": {
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
"version": "1.20.5",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
"integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3752,7 +3752,7 @@
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"on-finished": "~2.4.1",
"qs": "~6.14.0",
"qs": "~6.15.1",
"raw-body": "~2.5.3",
"type-is": "~1.6.18",
"unpipe": "~1.0.0"
@@ -3779,22 +3779,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/body-parser/node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/bonjour-service": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz",
@@ -6646,9 +6630,9 @@
}
},
"node_modules/i18next": {
"version": "26.0.7",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.7.tgz",
"integrity": "sha512-f7tL/iw0VQsx4nC5oNxBM2RjM8alNys5KzyiQTU6A9TI5TI89py4/Ez1cKFvHiLWsvzOXvuGUES+Kk/A2WiANQ==",
"version": "26.0.8",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.8.tgz",
"integrity": "sha512-BRzLom0mhDhV9v0QhgUUHWQJuwFmnr1194xEcNLYD6ym8y8s542n4jXUvRLnhNTbh9PmpU6kGZamyuGHQMsGjw==",
"funding": [
{
"type": "individual",
@@ -7709,9 +7693,9 @@
"license": "MIT"
},
"node_modules/loader-runner": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
"integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz",
"integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10812,9 +10796,9 @@
}
},
"node_modules/terser-webpack-plugin/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11686,9 +11670,9 @@
}
},
"node_modules/webpack-dev-middleware/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11803,9 +11787,9 @@
}
},
"node_modules/webpack-dev-server/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11923,9 +11907,9 @@
}
},
"node_modules/webpack/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,3 +1,3 @@
*.LICENSE.txt
*.js
#webhooks
!ff/*/*.js

View File

@@ -0,0 +1 @@
Options -Indexes

View File

@@ -0,0 +1,99 @@
/*
* register.js
* 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/>.
*/
$(function () {
"use strict";
const form = document.querySelector('form[action="'+route+'"]');
const errorBox = document.getElementById('client-errors');
const errorList = document.getElementById('client-errors-list');
const submitBtn = form.querySelector('button[type="submit"]');
const originalBtnText = submitBtn.textContent;
function showErrors(errors) {
errorList.innerHTML = errors.map(function(e) { return '<li>' + e + '</li>'; }).join('');
errorBox.style.display = 'block';
errorBox.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
async function sha1Hex(str) {
const buf = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(str));
return Array.from(new Uint8Array(buf))
.map(function(b) { return b.toString(16).padStart(2, '0'); })
.join('')
.toUpperCase();
}
async function isPwned(password) {
const hash = await sha1Hex(password);
const prefix = hash.slice(0, 5);
const suffix = hash.slice(5);
const res = await fetch('https://api.pwnedpasswords.com/range/' + prefix, {
headers: { 'Add-Padding': 'true' }
});
if (!res.ok) { return false; }
const text = await res.text();
return text.toUpperCase().split('\n').some(function(line) {
return line.split(':')[0] === suffix;
});
}
form.addEventListener('submit', async function (e) {
e.preventDefault();
errorBox.style.display = 'none';
const password = form.querySelector('[name="password"]').value;
const confirm = form.querySelector('[name="password_confirmation"]').value;
const verify = form.querySelector('[name="verify_password"]');
const errors = [];
if (password.length < 16) {
errors.push(passwordLengthError);
}
if (password !== confirm) {
errors.push(passwordMatchError);
}
if (errors.length > 0) {
showErrors(errors);
return;
}
if (verify && verify.checked) {
submitBtn.disabled = true;
submitBtn.textContent = waitForVerify;
try {
if (await isPwned(password)) {
errors.push(needSecurePassword);
}
} catch (_) {
// network failure — let server validate
}
submitBtn.disabled = false;
submitBtn.textContent = originalBtnText;
}
if (errors.length > 0) {
showErrors(errors);
return;
}
form.submit();
});
})();

View File

@@ -38,9 +38,9 @@ function parseToLocalDates() {
var obj = moment.utc(date).local();
console.log('auto convert to timezone is: "' + obj.format() + '"');
console.log('moment.js format is: "'+date_time_js+'"');
console.log('moment.js format is: "' + date_time_js + '"');
$(this).text(obj.format(date_time_js) + ' ('+ timeZone +')');
$(this).text(obj.format(date_time_js) + ' (' + timeZone + ')');
});
}
@@ -50,19 +50,25 @@ $(function () {
configAccounting(currencySymbol);
// on submit of logout button:
$('.logout-link').click(function(e) {
$('.logout-link').click(function (e) {
e.preventDefault();
document.getElementById('logout-form').submit();
return false;
});
// save sidebar collapsed state when page loads.
$('[data-toggle="push-menu"]').click(function () {
localStorage.setItem('ff3_sidebar_collapsed', (!$('body').hasClass('sidebar-collapse')).toString());
});
// on submit of form, disable any button in form:
$('form.form-horizontal:not(.nodisablebutton)').on('submit', function () {
$('button[type="submit"]').prop('disabled', true);
});
// when you click on a currency, this happens:
$('.currency-option').on('click', currencySelect);

View File

@@ -36,6 +36,7 @@
"/public/v1/js/ff/accounts/show.js": "/public/v1/js/ff/accounts/show.js",
"/public/v1/js/ff/admin/update/index.js": "/public/v1/js/ff/admin/update/index.js",
"/public/v1/js/ff/admin/users.js": "/public/v1/js/ff/admin/users.js",
"/public/v1/js/ff/auth/register.js": "/public/v1/js/ff/auth/register.js",
"/public/v1/js/ff/bills/create.js": "/public/v1/js/ff/bills/create.js",
"/public/v1/js/ff/bills/edit.js": "/public/v1/js/ff/bills/edit.js",
"/public/v1/js/ff/bills/index.js": "/public/v1/js/ff/bills/index.js",

View File

@@ -81,83 +81,11 @@
@endsection
@section('scripts')
<script nonce="{{ $JS_NONCE }}">
(function () {
const form = document.querySelector('form[action="{{ route('register') }}"]');
const errorBox = document.getElementById('client-errors');
const errorList = document.getElementById('client-errors-list');
const submitBtn = form.querySelector('button[type="submit"]');
const originalBtnText = submitBtn.textContent;
function showErrors(errors) {
errorList.innerHTML = errors.map(function(e) { return '<li>' + e + '</li>'; }).join('');
errorBox.style.display = 'block';
errorBox.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
async function sha1Hex(str) {
const buf = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(str));
return Array.from(new Uint8Array(buf))
.map(function(b) { return b.toString(16).padStart(2, '0'); })
.join('')
.toUpperCase();
}
async function isPwned(password) {
const hash = await sha1Hex(password);
const prefix = hash.slice(0, 5);
const suffix = hash.slice(5);
const res = await fetch('https://api.pwnedpasswords.com/range/' + prefix, {
headers: { 'Add-Padding': 'true' }
});
if (!res.ok) { return false; }
const text = await res.text();
return text.toUpperCase().split('\n').some(function(line) {
return line.split(':')[0] === suffix;
});
}
form.addEventListener('submit', async function (e) {
e.preventDefault();
errorBox.style.display = 'none';
const password = form.querySelector('[name="password"]').value;
const confirm = form.querySelector('[name="password_confirmation"]').value;
const verify = form.querySelector('[name="verify_password"]');
const errors = [];
if (password.length < 16) {
errors.push('{{ blade_escape_js((string)trans('validation.min.string', ['attribute' => 'password', 'min' => 16])) }}');
}
if (password !== confirm) {
errors.push('{{ blade_escape_js(trans('validation.confirmed', ['attribute' => 'password'])) }}');
}
if (errors.length > 0) {
showErrors(errors);
return;
}
if (verify && verify.checked) {
submitBtn.disabled = true;
submitBtn.textContent = '{{ blade_escape_js(trans('validation.verifying_password')) }}';
try {
if (await isPwned(password)) {
errors.push('{{ blade_escape_js(trans('validation.secure_password')) }}');
}
} catch (_) {
// network failure — let server validate
}
submitBtn.disabled = false;
submitBtn.textContent = originalBtnText;
}
if (errors.length > 0) {
showErrors(errors);
return;
}
form.submit();
});
})();
</script>
var route = '{{ route('register') }}';
var passwordLengthError = '{{ blade_escape_js((string)trans('validation.min.string', ['attribute' => 'password', 'min' => 16])) }}';
var passwordMatchError = '{{ blade_escape_js(trans('validation.confirmed', ['attribute' => 'password'])) }}';
var waitForVerify = '{{ blade_escape_js(trans('validation.verifying_password')) }}';
var needSecurePassword = '{{ blade_escape_js(trans('validation.secure_password')) }}';
</script>
<script nonce="{{$JS_NONCE}}" src="v1/js/ff/auth/register.js"></script>
@endsection

View File

@@ -86,14 +86,18 @@
<script src="v1/js/lib/respond.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
<![endif]-->
{# this entry is in the header so it's loaded early #}
<script type="text/javascript" nonce="{{ JS_NONCE }}">var forceDemoOff = false;</script>
{# favicons #}
{% include 'partials.favicons' %}
</head>
<body class="skin-firefly-iii sidebar-mini hold-transition">
{# this entry is in the header so it's loaded early #}
<script type="text/javascript" nonce="{{ JS_NONCE }}">
var forceDemoOff = false;
if ('true' === localStorage.getItem('ff3_sidebar_collapsed')) {
document.body.classList.add('sidebar-collapse');
}
</script>
<div class="wrapper" id="app">
<header class="main-header">