mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 18:54:58 +00:00 
			
		
		
		
	Compare commits
	
		
			278 Commits
		
	
	
		
			develop-20
			...
			develop-20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 36d9e5c3fe | ||
|  | 8d614de67f | ||
|  | d17da670ab | ||
|  | 5bf4df9ad8 | ||
|  | 07db6b59ce | ||
|  | e16f1cf4ee | ||
|  | 4c80d929ca | ||
|  | 16364d9859 | ||
|  | c24f6acb2c | ||
|  | 4bd19e0627 | ||
|  | 69d839997a | ||
|  | c02c027f4f | ||
|  | b14606625e | ||
|  | 222d7b56c7 | ||
|  | b7f7bf42b2 | ||
|  | 0cbd64d31a | ||
|  | 60bdae47c4 | ||
|  | ca4b38d905 | ||
|  | 3674465f53 | ||
|  | b951d4130c | ||
|  | 7992b810fd | ||
|  | c1c0afa40b | ||
|  | 56c9026299 | ||
|  | 021ddfc36b | ||
|  | feabfe54f0 | ||
|  | 565409b486 | ||
|  | f57366da5f | ||
|  | 064217ccb0 | ||
|  | fa3ccbda33 | ||
|  | f43aadf02d | ||
|  | 3b8a4d3e9b | ||
|  | 3d410556ef | ||
|  | f15ca1d0a1 | ||
|  | 7002463c54 | ||
|  | 649f876437 | ||
|  | 3cfd178cbd | ||
|  | cefbaafa19 | ||
|  | a8c88800c4 | ||
|  | 9d1a127200 | ||
|  | 3fdde2d1c8 | ||
|  | cdc0b8dd2c | ||
|  | 1a1e06e6e8 | ||
|  | 6d39b8468c | ||
|  | bdee3947b2 | ||
|  | 2317037655 | ||
|  | dcea6b757b | ||
|  | bd7fe92818 | ||
|  | 850e47d8db | ||
|  | 96fe62400f | ||
|  | 5d07fcdcb6 | ||
|  | fd5d2d57a8 | ||
|  | 8e7d42201f | ||
|  | f26bd3cb31 | ||
|  | 36915cdace | ||
|  | 8a5cecd2a0 | ||
|  | 78da1b22bb | ||
|  | 6d970a9794 | ||
|  | 8bb7739f05 | ||
|  | 7788bb4b33 | ||
|  | 2ecb4bb3b7 | ||
|  | 4a783d3c3c | ||
|  | e16645ae87 | ||
|  | 9d3189be7e | ||
|  | 07fca78293 | ||
|  | 82080501c7 | ||
|  | d93d6bfc66 | ||
|  | a41326ef94 | ||
|  | 90b77845c3 | ||
|  | 57af80d820 | ||
|  | fc4d5a1dfd | ||
|  | 8ab9ab8d21 | ||
|  | 75674b5793 | ||
|  | a7d6f26051 | ||
|  | eb540ce148 | ||
|  | a3077fe43b | ||
|  | 63bb84d375 | ||
|  | e5f5aa628e | ||
|  | c54f84dc8e | ||
|  | c2e562623c | ||
|  | c8d5e8a9dc | ||
|  | 963f017be3 | ||
|  | 0e0eeb736f | ||
|  | e8d9b8fa49 | ||
|  | c166b9242e | ||
|  | 8ff8efced2 | ||
|  | 0b4fb9a806 | ||
|  | ba9fef9410 | ||
|  | f7d94d17cd | ||
|  | 1fea9c6817 | ||
|  | a88c8bedbe | ||
|  | fbf3468053 | ||
|  | 2a3ba9799e | ||
|  | d121aad28f | ||
|  | dc808fa807 | ||
|  | a1be6ff62b | ||
|  | 20dc5b0256 | ||
|  | edd54e23c5 | ||
|  | 1238df8784 | ||
|  | b8c62652b0 | ||
|  | 54b2d02f63 | ||
|  | 47faf89a5c | ||
|  | b7fb5a3854 | ||
|  | a384b4202a | ||
|  | 99ecac0ce4 | ||
|  | 6102982456 | ||
|  | b88e981b4b | ||
|  | 827263b03e | ||
|  | d44e74d334 | ||
|  | 911f46c590 | ||
|  | 7d42c4ee5d | ||
|  | ea89f6177f | ||
|  | 74291b3870 | ||
|  | 2c4f2082fe | ||
|  | d8d58cc29b | ||
|  | 85b17e4035 | ||
|  | 83de5667b3 | ||
|  | 5fffe873c6 | ||
|  | 78c09c82d6 | ||
|  | 704abc315d | ||
|  | 86b4965458 | ||
|  | d2dc0c2bf0 | ||
|  | 9f4894bbb5 | ||
|  | 76a8675a34 | ||
|  | 6988301da1 | ||
|  | 109cd37211 | ||
|  | 284ff4d1b0 | ||
|  | bc0ab7af99 | ||
|  | a17bc7258f | ||
|  | 87911c2438 | ||
|  | 746f1fd300 | ||
|  | 9e5faf919f | ||
|  | cc6cbe6605 | ||
|  | dc6d708897 | ||
|  | 6189d24b98 | ||
|  | f6e28dc88f | ||
|  | 75ea035630 | ||
|  | 4cdb14301d | ||
|  | 9f95221ba3 | ||
|  | e3a67be412 | ||
|  | 5749b642ce | ||
|  | baff7c67f9 | ||
|  | ccc005942f | ||
|  | 5b83c33039 | ||
|  | cc32578c5f | ||
|  | 80f410835b | ||
|  | b537a3145d | ||
|  | bfa1fcbaf8 | ||
|  | 56243907c4 | ||
|  | 5928dd72e6 | ||
|  | c6bf0ff1cd | ||
|  | 19d1cf192b | ||
|  | 37d7dc7e3e | ||
|  | 95a3a194b8 | ||
|  | 3542387188 | ||
|  | da1b002a64 | ||
|  | 46daee28e7 | ||
|  | 9ade5635d4 | ||
|  | e14e80f33c | ||
|  | 0c824e21c8 | ||
|  | fab1c68569 | ||
|  | c1534657f2 | ||
|  | 39841de680 | ||
|  | 43a720b62b | ||
|  | 5ec54de29e | ||
|  | 397e37f344 | ||
|  | b6f84c2b99 | ||
|  | 843f86fc66 | ||
|  | 0e8e364074 | ||
|  | bbccbef578 | ||
|  | ee11a8e3a0 | ||
|  | e8618047bd | ||
|  | f104b76f73 | ||
|  | cb701d8506 | ||
|  | 70a334c56e | ||
|  | e6b2db1e29 | ||
|  | e8dffa0052 | ||
|  | c4f0512f39 | ||
|  | 3268019d0c | ||
|  | a0ef6a1fc8 | ||
|  | 99d0098b20 | ||
|  | a7a54c042c | ||
|  | c44e48a793 | ||
|  | 53b501ca73 | ||
|  | 322f70bcca | ||
|  | 35559c077b | ||
|  | 590ffe7c76 | ||
|  | 8a2d8f148e | ||
|  | 4f0e15e07d | ||
|  | 7463861e0c | ||
|  | 1e70fa28be | ||
|  | 26c6ca470b | ||
|  | 5e54034e0e | ||
|  | 25873ef734 | ||
|  | 1092b04b22 | ||
|  | 01ce74dd72 | ||
|  | 41430d8386 | ||
|  | 01eb19169c | ||
|  | cfaa7d7c68 | ||
|  | 14d3312a10 | ||
|  | 87be478dd8 | ||
|  | 0b6877a20e | ||
|  | 7186f0ef60 | ||
|  | 538933691e | ||
|  | 46c49ddbd8 | ||
|  | bcfb134b6e | ||
|  | 57981f1cf9 | ||
|  | 0310186fb7 | ||
|  | 4dcb38290e | ||
|  | 2f5c37048b | ||
|  | 370c8b16ae | ||
|  | af0555592a | ||
|  | 9c07ddaed6 | ||
|  | bb7355a566 | ||
|  | 1d48347f8c | ||
|  | 060b76ca9c | ||
|  | 2b2b9b6f7a | ||
|  | f3dd05a0c0 | ||
|  | 47a91aa273 | ||
|  | 41bc236603 | ||
|  | 65349451ea | ||
|  | e77b6a55a4 | ||
|  | 2379bcff11 | ||
|  | 7133156fa1 | ||
|  | a59176689d | ||
|  | bc2d8f3dfb | ||
|  | ddf89a9d5a | ||
|  | 7daaba17f6 | ||
|  | 9cb5b1384f | ||
|  | 7d13263482 | ||
|  | d9ff252915 | ||
|  | 51ba550251 | ||
|  | fd21c467ad | ||
|  | 9aa90650b4 | ||
|  | d892257e8b | ||
|  | db0dbcfcf1 | ||
|  | f591996f04 | ||
|  | b08d385586 | ||
|  | 20ef22f67e | ||
|  | c888baf542 | ||
|  | 8b0af3f666 | ||
|  | 7043e1e7c0 | ||
|  | c5854eba23 | ||
|  | ddf1a8cebb | ||
|  | 7dcaf167e9 | ||
|  | b359d51d3a | ||
|  | 3913fa5086 | ||
|  | ab2772abe0 | ||
|  | bc7875b17b | ||
|  | 4938fa9990 | ||
|  | 84df2c80ee | ||
|  | dc17060754 | ||
|  | e2fa81dddc | ||
|  | 182dfc95fe | ||
|  | c8979b6c33 | ||
|  | ab872e8912 | ||
|  | d36b94fabf | ||
|  | e3d4ceaecb | ||
|  | e3a6e5b788 | ||
|  | 57235c0e00 | ||
|  | 2298c3ddaf | ||
|  | 7224f1be6f | ||
|  | 1bd3019c16 | ||
|  | f0fa21dead | ||
|  | 845eaed8d7 | ||
|  | b3649cd4d0 | ||
|  | 55f14c587b | ||
|  | 441a8a8408 | ||
|  | 060c9648f1 | ||
|  | 7680c8733f | ||
|  | 5a0af5c93b | ||
|  | f4b066add1 | ||
|  | 9ecb414b02 | ||
|  | ad4f908c24 | ||
|  | 025f739442 | ||
|  | 6df7354c48 | ||
|  | 3f77c845ca | ||
|  | d4771f7a5c | ||
|  | ec4e2bfa4f | 
| @@ -1,5 +1,5 @@ | ||||
| { | ||||
|   "require": { | ||||
|     "friendsofphp/php-cs-fixer": "^3.12" | ||||
|   } | ||||
|     "require": { | ||||
|         "friendsofphp/php-cs-fixer": "^3.12" | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										185
									
								
								.ci/php-cs-fixer/composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										185
									
								
								.ci/php-cs-fixer/composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -8,16 +8,16 @@ | ||||
|     "packages": [ | ||||
|         { | ||||
|             "name": "composer/pcre", | ||||
|             "version": "3.1.2", | ||||
|             "version": "3.1.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/composer/pcre.git", | ||||
|                 "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace" | ||||
|                 "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace", | ||||
|                 "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace", | ||||
|                 "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", | ||||
|                 "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -59,7 +59,7 @@ | ||||
|             ], | ||||
|             "support": { | ||||
|                 "issues": "https://github.com/composer/pcre/issues", | ||||
|                 "source": "https://github.com/composer/pcre/tree/3.1.2" | ||||
|                 "source": "https://github.com/composer/pcre/tree/3.1.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -75,7 +75,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-03-07T15:38:35+00:00" | ||||
|             "time": "2024-03-19T10:26:25+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "composer/semver", | ||||
| @@ -160,16 +160,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "composer/xdebug-handler", | ||||
|             "version": "3.0.3", | ||||
|             "version": "3.0.5", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/composer/xdebug-handler.git", | ||||
|                 "reference": "ced299686f41dce890debac69273b47ffe98a40c" | ||||
|                 "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", | ||||
|                 "reference": "ced299686f41dce890debac69273b47ffe98a40c", | ||||
|                 "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", | ||||
|                 "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -180,7 +180,7 @@ | ||||
|             "require-dev": { | ||||
|                 "phpstan/phpstan": "^1.0", | ||||
|                 "phpstan/phpstan-strict-rules": "^1.1", | ||||
|                 "symfony/phpunit-bridge": "^6.0" | ||||
|                 "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "autoload": { | ||||
| @@ -204,9 +204,9 @@ | ||||
|                 "performance" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "irc": "irc://irc.freenode.org/composer", | ||||
|                 "irc": "ircs://irc.libera.chat:6697/composer", | ||||
|                 "issues": "https://github.com/composer/xdebug-handler/issues", | ||||
|                 "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" | ||||
|                 "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -222,20 +222,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2022-02-25T21:32:43+00:00" | ||||
|             "time": "2024-05-06T16:37:16+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "friendsofphp/php-cs-fixer", | ||||
|             "version": "v3.51.0", | ||||
|             "version": "v3.56.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", | ||||
|                 "reference": "127fa74f010da99053e3f5b62672615b72dd6efd" | ||||
|                 "reference": "4429303e62a4ce583ddfe64ff5c34c76bcf74931" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/127fa74f010da99053e3f5b62672615b72dd6efd", | ||||
|                 "reference": "127fa74f010da99053e3f5b62672615b72dd6efd", | ||||
|                 "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4429303e62a4ce583ddfe64ff5c34c76bcf74931", | ||||
|                 "reference": "4429303e62a4ce583ddfe64ff5c34c76bcf74931", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -259,6 +259,7 @@ | ||||
|             }, | ||||
|             "require-dev": { | ||||
|                 "facile-it/paraunit": "^1.3 || ^2.0", | ||||
|                 "infection/infection": "^0.27.11", | ||||
|                 "justinrainbow/json-schema": "^5.2", | ||||
|                 "keradus/cli-executor": "^2.1", | ||||
|                 "mikey179/vfsstream": "^1.6.11", | ||||
| @@ -306,7 +307,7 @@ | ||||
|             ], | ||||
|             "support": { | ||||
|                 "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", | ||||
|                 "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.51.0" | ||||
|                 "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.56.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -314,7 +315,7 @@ | ||||
|                     "type": "github" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-02-28T19:50:06+00:00" | ||||
|             "time": "2024-05-07T15:50:05+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "psr/container", | ||||
| @@ -538,16 +539,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/console", | ||||
|             "version": "v7.0.4", | ||||
|             "version": "v7.0.7", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/console.git", | ||||
|                 "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f" | ||||
|                 "reference": "c981e0e9380ce9f146416bde3150c79197ce9986" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f", | ||||
|                 "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f", | ||||
|                 "url": "https://api.github.com/repos/symfony/console/zipball/c981e0e9380ce9f146416bde3150c79197ce9986", | ||||
|                 "reference": "c981e0e9380ce9f146416bde3150c79197ce9986", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -611,7 +612,7 @@ | ||||
|                 "terminal" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/console/tree/v7.0.4" | ||||
|                 "source": "https://github.com/symfony/console/tree/v7.0.7" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -627,20 +628,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-02-22T20:27:20+00:00" | ||||
|             "time": "2024-04-18T09:29:19+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/deprecation-contracts", | ||||
|             "version": "v3.4.0", | ||||
|             "version": "v3.5.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/deprecation-contracts.git", | ||||
|                 "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" | ||||
|                 "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", | ||||
|                 "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", | ||||
|                 "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", | ||||
|                 "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -649,7 +650,7 @@ | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "3.4-dev" | ||||
|                     "dev-main": "3.5-dev" | ||||
|                 }, | ||||
|                 "thanks": { | ||||
|                     "name": "symfony/contracts", | ||||
| @@ -678,7 +679,7 @@ | ||||
|             "description": "A generic function and convention to trigger deprecation notices", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" | ||||
|                 "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -694,20 +695,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-05-23T14:45:45+00:00" | ||||
|             "time": "2024-04-18T09:32:20+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/event-dispatcher", | ||||
|             "version": "v7.0.3", | ||||
|             "version": "v7.0.7", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/event-dispatcher.git", | ||||
|                 "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e" | ||||
|                 "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/834c28d533dd0636f910909d01b9ff45cc094b5e", | ||||
|                 "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e", | ||||
|                 "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/db2a7fab994d67d92356bb39c367db115d9d30f9", | ||||
|                 "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -758,7 +759,7 @@ | ||||
|             "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.3" | ||||
|                 "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.7" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -774,20 +775,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|             "time": "2024-04-18T09:29:19+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/event-dispatcher-contracts", | ||||
|             "version": "v3.4.0", | ||||
|             "version": "v3.5.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/event-dispatcher-contracts.git", | ||||
|                 "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" | ||||
|                 "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", | ||||
|                 "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", | ||||
|                 "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", | ||||
|                 "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -797,7 +798,7 @@ | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "3.4-dev" | ||||
|                     "dev-main": "3.5-dev" | ||||
|                 }, | ||||
|                 "thanks": { | ||||
|                     "name": "symfony/contracts", | ||||
| @@ -834,7 +835,7 @@ | ||||
|                 "standards" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" | ||||
|                 "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -850,26 +851,27 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-05-23T14:45:45+00:00" | ||||
|             "time": "2024-04-18T09:32:20+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/filesystem", | ||||
|             "version": "v7.0.3", | ||||
|             "version": "v7.0.7", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/filesystem.git", | ||||
|                 "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12" | ||||
|                 "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/filesystem/zipball/2890e3a825bc0c0558526c04499c13f83e1b6b12", | ||||
|                 "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12", | ||||
|                 "url": "https://api.github.com/repos/symfony/filesystem/zipball/cc168be6fbdcdf3401f50ae863ee3818ed4338f5", | ||||
|                 "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "php": ">=8.2", | ||||
|                 "symfony/polyfill-ctype": "~1.8", | ||||
|                 "symfony/polyfill-mbstring": "~1.8" | ||||
|                 "symfony/polyfill-mbstring": "~1.8", | ||||
|                 "symfony/process": "^6.4|^7.0" | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "autoload": { | ||||
| @@ -897,7 +899,7 @@ | ||||
|             "description": "Provides basic utilities for the filesystem", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/filesystem/tree/v7.0.3" | ||||
|                 "source": "https://github.com/symfony/filesystem/tree/v7.0.7" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -913,20 +915,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|             "time": "2024-04-18T09:29:19+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/finder", | ||||
|             "version": "v7.0.0", | ||||
|             "version": "v7.0.7", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/finder.git", | ||||
|                 "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56" | ||||
|                 "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/finder/zipball/6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", | ||||
|                 "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", | ||||
|                 "url": "https://api.github.com/repos/symfony/finder/zipball/4d58f0f4fe95a30d7b538d71197135483560b97c", | ||||
|                 "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -961,7 +963,7 @@ | ||||
|             "description": "Finds files and directories via an intuitive fluent interface", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/finder/tree/v7.0.0" | ||||
|                 "source": "https://github.com/symfony/finder/tree/v7.0.7" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -977,20 +979,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-10-31T17:59:56+00:00" | ||||
|             "time": "2024-04-28T11:44:19+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/options-resolver", | ||||
|             "version": "v7.0.0", | ||||
|             "version": "v7.0.7", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/options-resolver.git", | ||||
|                 "reference": "700ff4096e346f54cb628ea650767c8130f1001f" | ||||
|                 "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/options-resolver/zipball/700ff4096e346f54cb628ea650767c8130f1001f", | ||||
|                 "reference": "700ff4096e346f54cb628ea650767c8130f1001f", | ||||
|                 "url": "https://api.github.com/repos/symfony/options-resolver/zipball/23cc173858776ad451e31f053b1c9f47840b2cfa", | ||||
|                 "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1028,7 +1030,7 @@ | ||||
|                 "options" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/options-resolver/tree/v7.0.0" | ||||
|                 "source": "https://github.com/symfony/options-resolver/tree/v7.0.7" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1044,7 +1046,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-08-08T10:20:21+00:00" | ||||
|             "time": "2024-04-18T09:29:19+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/polyfill-ctype", | ||||
| @@ -1522,16 +1524,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/process", | ||||
|             "version": "v7.0.4", | ||||
|             "version": "v7.0.7", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/process.git", | ||||
|                 "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" | ||||
|                 "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", | ||||
|                 "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", | ||||
|                 "url": "https://api.github.com/repos/symfony/process/zipball/3839e56b94dd1dbd13235d27504e66baf23faba0", | ||||
|                 "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1563,7 +1565,7 @@ | ||||
|             "description": "Executes commands in sub-processes", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/process/tree/v7.0.4" | ||||
|                 "source": "https://github.com/symfony/process/tree/v7.0.7" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1579,25 +1581,26 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-02-22T20:27:20+00:00" | ||||
|             "time": "2024-04-18T09:29:19+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/service-contracts", | ||||
|             "version": "v3.4.1", | ||||
|             "version": "v3.5.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/service-contracts.git", | ||||
|                 "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" | ||||
|                 "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", | ||||
|                 "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", | ||||
|                 "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", | ||||
|                 "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "php": ">=8.1", | ||||
|                 "psr/container": "^1.1|^2.0" | ||||
|                 "psr/container": "^1.1|^2.0", | ||||
|                 "symfony/deprecation-contracts": "^2.5|^3" | ||||
|             }, | ||||
|             "conflict": { | ||||
|                 "ext-psr": "<1.1|>=2" | ||||
| @@ -1605,7 +1608,7 @@ | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "3.4-dev" | ||||
|                     "dev-main": "3.5-dev" | ||||
|                 }, | ||||
|                 "thanks": { | ||||
|                     "name": "symfony/contracts", | ||||
| @@ -1645,7 +1648,7 @@ | ||||
|                 "standards" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" | ||||
|                 "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1661,20 +1664,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-12-26T14:02:43+00:00" | ||||
|             "time": "2024-04-18T09:32:20+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/stopwatch", | ||||
|             "version": "v7.0.3", | ||||
|             "version": "v7.0.7", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/stopwatch.git", | ||||
|                 "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112" | ||||
|                 "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/stopwatch/zipball/983900d6fddf2b0cbaacacbbad07610854bd8112", | ||||
|                 "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112", | ||||
|                 "url": "https://api.github.com/repos/symfony/stopwatch/zipball/41a7a24aa1dc82adf46a06bc292d1923acfe6b84", | ||||
|                 "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1707,7 +1710,7 @@ | ||||
|             "description": "Provides a way to profile code", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/stopwatch/tree/v7.0.3" | ||||
|                 "source": "https://github.com/symfony/stopwatch/tree/v7.0.7" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1723,20 +1726,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|             "time": "2024-04-18T09:29:19+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/string", | ||||
|             "version": "v7.0.4", | ||||
|             "version": "v7.0.7", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/string.git", | ||||
|                 "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" | ||||
|                 "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", | ||||
|                 "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", | ||||
|                 "url": "https://api.github.com/repos/symfony/string/zipball/e405b5424dc2528e02e31ba26b83a79fd4eb8f63", | ||||
|                 "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1793,7 +1796,7 @@ | ||||
|                 "utf8" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/string/tree/v7.0.4" | ||||
|                 "source": "https://github.com/symfony/string/tree/v7.0.7" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1809,7 +1812,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-02-01T13:17:36+00:00" | ||||
|             "time": "2024-04-18T09:29:19+00:00" | ||||
|         } | ||||
|     ], | ||||
|     "packages-dev": [], | ||||
|   | ||||
							
								
								
									
										19
									
								
								.ci/phpcs.sh
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								.ci/phpcs.sh
									
									
									
									
									
								
							| @@ -20,23 +20,8 @@ | ||||
| # along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
| # | ||||
|  | ||||
| # Install composer packages | ||||
| #composer install --no-scripts --no-ansi | ||||
|  | ||||
| SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" | ||||
|  | ||||
| # enable test .env file. | ||||
| # cp .ci/.env.ci .env | ||||
|  | ||||
| OUTPUT_FORMAT=txt | ||||
| EXTRA_PARAMS="" | ||||
|  | ||||
| if [[ $GITHUB_ACTIONS = "true" ]] | ||||
| then | ||||
|     OUTPUT_FORMAT=txt | ||||
|     EXTRA_PARAMS="" | ||||
| fi | ||||
|  | ||||
| # clean up php code | ||||
| cd $SCRIPT_DIR/php-cs-fixer | ||||
| composer update --quiet | ||||
| @@ -44,8 +29,8 @@ rm -f .php-cs-fixer.cache | ||||
| PHP_CS_FIXER_IGNORE_ENV=true | ||||
| ./vendor/bin/php-cs-fixer fix \ | ||||
|     --config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php \ | ||||
|     --format=$OUTPUT_FORMAT \ | ||||
|     --allow-risky=yes $EXTRA_PARAMS | ||||
|     --format=txt \ | ||||
|     --allow-risky=yes | ||||
|  | ||||
| EXIT_CODE=$? | ||||
|  | ||||
|   | ||||
							
								
								
									
										28
									
								
								.ci/phpmd/composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										28
									
								
								.ci/phpmd/composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -9,16 +9,16 @@ | ||||
|     "packages-dev": [ | ||||
|         { | ||||
|             "name": "composer/pcre", | ||||
|             "version": "3.1.2", | ||||
|             "version": "3.1.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/composer/pcre.git", | ||||
|                 "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace" | ||||
|                 "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace", | ||||
|                 "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace", | ||||
|                 "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", | ||||
|                 "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -60,7 +60,7 @@ | ||||
|             ], | ||||
|             "support": { | ||||
|                 "issues": "https://github.com/composer/pcre/issues", | ||||
|                 "source": "https://github.com/composer/pcre/tree/3.1.2" | ||||
|                 "source": "https://github.com/composer/pcre/tree/3.1.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -76,20 +76,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-03-07T15:38:35+00:00" | ||||
|             "time": "2024-03-19T10:26:25+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "composer/xdebug-handler", | ||||
|             "version": "3.0.3", | ||||
|             "version": "3.0.4", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/composer/xdebug-handler.git", | ||||
|                 "reference": "ced299686f41dce890debac69273b47ffe98a40c" | ||||
|                 "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", | ||||
|                 "reference": "ced299686f41dce890debac69273b47ffe98a40c", | ||||
|                 "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/4f988f8fdf580d53bdb2d1278fe93d1ed5462255", | ||||
|                 "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -100,7 +100,7 @@ | ||||
|             "require-dev": { | ||||
|                 "phpstan/phpstan": "^1.0", | ||||
|                 "phpstan/phpstan-strict-rules": "^1.1", | ||||
|                 "symfony/phpunit-bridge": "^6.0" | ||||
|                 "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "autoload": { | ||||
| @@ -124,9 +124,9 @@ | ||||
|                 "performance" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "irc": "irc://irc.freenode.org/composer", | ||||
|                 "irc": "ircs://irc.libera.chat:6697/composer", | ||||
|                 "issues": "https://github.com/composer/xdebug-handler/issues", | ||||
|                 "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" | ||||
|                 "source": "https://github.com/composer/xdebug-handler/tree/3.0.4" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -142,7 +142,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2022-02-25T21:32:43+00:00" | ||||
|             "time": "2024-03-26T18:29:49+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "pdepend/pdepend", | ||||
|   | ||||
| @@ -19,9 +19,9 @@ | ||||
|   ~ along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|   --> | ||||
|  | ||||
| <ruleset name="pcsg-generated-ruleset" | ||||
| <ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          name="pcsg-generated-ruleset" | ||||
|          xmlns="http://pmd.sf.net/ruleset/1.0.0" | ||||
|          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" | ||||
|          xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> | ||||
|     <description>Firefly III ruleset.</description> | ||||
|   | ||||
							
								
								
									
										10
									
								
								.env.example
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.env.example
									
									
									
									
									
								
							| @@ -111,7 +111,10 @@ PGSQL_SSL_CERT=null | ||||
| PGSQL_SSL_KEY=null | ||||
| PGSQL_SSL_CRL_FILE=null | ||||
|  | ||||
| # more PostgreSQL settings | ||||
| # For postgresql 15 and up, setting this to public will no longer work as expected, becasuse the | ||||
| # 'public' schema is without grants. This can be worked around by having a super user grant those | ||||
| # necessary privileges, but in security conscious setups that's not viable. | ||||
| # You will need to set this to the schema you want to use. | ||||
| PGSQL_SCHEMA=public | ||||
|  | ||||
| # If you're looking for performance improvements, you could install memcached or redis | ||||
| @@ -184,6 +187,11 @@ SEND_REPORT_JOURNALS=true | ||||
| # Since this involves an external service, it's optional and disabled by default. | ||||
| ENABLE_EXTERNAL_MAP=false | ||||
|  | ||||
| # | ||||
| # Enable or disable exchange rate conversion. This function isn't used yet by Firefly III | ||||
| # | ||||
| ENABLE_EXCHANGE_RATES=false | ||||
|  | ||||
| # Set this value to true if you want Firefly III to download currency exchange rates | ||||
| # from the internet. These rates are hosted by the creator of Firefly III inside | ||||
| # an Azure Storage Container. | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/funding.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/funding.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,6 @@ | ||||
| # These are supported funding model platforms | ||||
| # Firefly III sponsor options | ||||
|  | ||||
| github: jc5 | ||||
| patreon: JC5 | ||||
| ko_fi: jamesc5 | ||||
| liberapay: JC5 | ||||
|   | ||||
							
								
								
									
										18
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +1,19 @@ | ||||
| <!-- | ||||
| Before you create a new PR, please consider: | ||||
| Thank you for submitting new code to Firefly III, or any of the related projects. Please read the following rules carefully. | ||||
| 
 | ||||
| 1) Pull requests for the MAIN branch will be closed. | ||||
| 2) DO NOT include translations in your PR. Only English US sentences. | ||||
| - Please do not submit solutions for problems that are not already reported in an issue. | ||||
| - Unfortunately, Firefly III can't be your learning experience. If you're new to all of this, please open an issue first. | ||||
| - Please do not open PRs to "discuss" possible solutions or to "get feedback" on your code. I simply don't have time for that. | ||||
| - Pull requests for the MAIN branch will be closed. | ||||
| - DO NOT include translated strings in your PR. | ||||
| 
 | ||||
| If it feels necessary to open an issue first, please do so, before you open a PR. | ||||
| 
 | ||||
| See also: https://docs.firefly-iii.org/explanation/support/#contributing-code | ||||
| 
 | ||||
| Thanks. | ||||
| --> | ||||
| 
 | ||||
| Fixes issue # (if relevant) | ||||
|      | ||||
| This PR fixes issue # (if relevant). | ||||
| 
 | ||||
| Changes in this pull request: | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										58
									
								
								.github/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										58
									
								
								.github/stale.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,58 +0,0 @@ | ||||
| # Configuration for probot-stale - https://github.com/probot/stale | ||||
|  | ||||
| # Number of days of inactivity before an Issue or Pull Request becomes stale | ||||
| daysUntilStale: 14 | ||||
|  | ||||
| # Number of days of inactivity before a stale Issue or Pull Request is closed. | ||||
| # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. | ||||
| daysUntilClose: 14 | ||||
|  | ||||
| # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable | ||||
| # - "[Status] Maybe Later" | ||||
| exemptLabels: | ||||
|   - enhancement | ||||
|   - feature | ||||
|   - bug | ||||
|   - announcement | ||||
|   - "layout-v3" | ||||
|  | ||||
| # Set to true to ignore issues in a project (defaults to false) | ||||
| exemptProjects: false | ||||
|  | ||||
| # Set to true to ignore issues in a milestone (defaults to false) | ||||
| exemptMilestones: false | ||||
|  | ||||
| # Label to use when marking as stale | ||||
| staleLabel: stale | ||||
|  | ||||
| # Comment to post when marking as stale. Set to `false` to disable | ||||
| markComment: > | ||||
|   This issue has been automatically marked as stale because it has not had | ||||
|   recent activity. It will be closed if no further activity occurs. Thank you | ||||
|   for your contributions. | ||||
|  | ||||
| # Comment to post when removing the stale label. | ||||
| # unmarkComment: > | ||||
| #   Your comment here. | ||||
|  | ||||
| # Comment to post when closing a stale Issue or Pull Request. | ||||
| # closeComment: > | ||||
| #   Your comment here. | ||||
|  | ||||
| # Limit the number of actions per hour, from 1-30. Default is 30 | ||||
| limitPerRun: 30 | ||||
|  | ||||
| # Limit to only `issues` or `pulls` | ||||
| # only: issues | ||||
|  | ||||
| # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': | ||||
| # pulls: | ||||
| #   daysUntilStale: 30 | ||||
| #   markComment: > | ||||
| #     This pull request has been automatically marked as stale because it has not had | ||||
| #     recent activity. It will be closed if no further activity occurs. Thank you | ||||
| #     for your contributions. | ||||
|  | ||||
| # issues: | ||||
| #   exemptLabels: | ||||
| #     - confirmed | ||||
							
								
								
									
										2
									
								
								.github/workflows/cleanup.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/cleanup.yml
									
									
									
									
										vendored
									
									
								
							| @@ -7,7 +7,7 @@ permissions: | ||||
|  | ||||
| on: | ||||
|   schedule: | ||||
|     - cron: '0 0 * * *' | ||||
|     - cron: '0 1 * * *' | ||||
|   workflow_dispatch: | ||||
| jobs: | ||||
|   prune: | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/close-duplicates.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/close-duplicates.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,7 +3,7 @@ name: "Issues - Command to close duplicate issues" | ||||
| # the workflow to execute on is comments that are newly created | ||||
| on: | ||||
|   issue_comment: | ||||
|     types: [created] | ||||
|     types: [ created ] | ||||
|  | ||||
| permissions: | ||||
|   issues: write | ||||
| @@ -13,7 +13,7 @@ jobs: | ||||
|   close_duplicates: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: github/command@v1.1.0 | ||||
|       - uses: github/command@v1.1.1 | ||||
|         id: command | ||||
|         with: | ||||
|           allowed_contexts: "issue" | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/debug-info-actions.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/debug-info-actions.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,9 +3,9 @@ name: 'Issues - Respond to hidden commands' | ||||
| # the workflow to execute on is comments that are newly created | ||||
| on: | ||||
|   issues: | ||||
|     types: [opened, edited] | ||||
|     types: [ opened, edited ] | ||||
|   issue_comment: | ||||
|     types: [created] | ||||
|     types: [ created ] | ||||
|  | ||||
| # permissions needed for reacting to IssueOps commands on issues and PRs | ||||
| permissions: | ||||
|   | ||||
							
								
								
									
										6
									
								
								.github/workflows/label-actions.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/label-actions.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,11 +2,11 @@ name: 'Issues - Reply to specific labels' | ||||
|  | ||||
| on: | ||||
|   issues: | ||||
|     types: [labeled, unlabeled] | ||||
|     types: [ labeled, unlabeled ] | ||||
|   pull_request_target: | ||||
|     types: [labeled, unlabeled] | ||||
|     types: [ labeled, unlabeled ] | ||||
|   discussion: | ||||
|     types: [labeled, unlabeled] | ||||
|     types: [ labeled, unlabeled ] | ||||
|  | ||||
| permissions: | ||||
|   contents: read | ||||
|   | ||||
							
								
								
									
										9
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,7 +3,7 @@ name: 'Issues - Lock old issues' | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|   schedule: | ||||
|     - cron: '0 0 * * *' | ||||
|     - cron: '0 2 * * *' | ||||
|  | ||||
| concurrency: | ||||
|   group: lock-threads | ||||
| @@ -18,11 +18,12 @@ jobs: | ||||
|     permissions: | ||||
|       issues: write | ||||
|       pull-requests: write | ||||
|       discussions: write | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: dessant/lock-threads@v5 | ||||
|         with: | ||||
|           github-token: ${{ github.token }} | ||||
|           issue-inactive-days: 7 | ||||
|           pr-inactive-days: 7 | ||||
|           issue-inactive-days: 21 | ||||
|           pr-inactive-days: 21 | ||||
|           discussion-inactive-days: 21 | ||||
|           log-output: true | ||||
|   | ||||
							
								
								
									
										179
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										179
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -4,11 +4,11 @@ on: | ||||
|   workflow_dispatch: | ||||
|     inputs: | ||||
|       version: | ||||
|         description: 'Version to release' | ||||
|         description: 'Release "v1.2.3" or "develop"' | ||||
|         required: true | ||||
|         default: 'develop' | ||||
|   schedule: | ||||
|     - cron:  '15 0 * * MON,THU' | ||||
|     - cron: '0 3 * * MON,THU' | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
| @@ -51,7 +51,7 @@ jobs: | ||||
|           CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }} | ||||
|       - name: Cleanup translations | ||||
|         id: cleanup-transactions | ||||
|         uses: JC5/firefly-iii-dev@v34 | ||||
|         uses: JC5/firefly-iii-dev@main | ||||
|         with: | ||||
|           action: 'ff3:crowdin-warning' | ||||
|           output: '' | ||||
| @@ -60,7 +60,7 @@ jobs: | ||||
|           GH_TOKEN: '' | ||||
|       - name: Cleanup changelog | ||||
|         id: cleanup-changelog | ||||
|         uses: JC5/firefly-iii-dev@v34 | ||||
|         uses: JC5/firefly-iii-dev@main | ||||
|         with: | ||||
|           action: 'ff3:changelog' | ||||
|           output: '' | ||||
| @@ -69,7 +69,7 @@ jobs: | ||||
|           GH_TOKEN: ${{ secrets.CHANGELOG_TOKEN }} | ||||
|       - name: Extract changelog | ||||
|         id: extract-changelog | ||||
|         uses: JC5/firefly-iii-dev@v34 | ||||
|         uses: JC5/firefly-iii-dev@main | ||||
|         with: | ||||
|           action: 'ff3:extract-changelog' | ||||
|           output: 'output' | ||||
| @@ -78,7 +78,7 @@ jobs: | ||||
|           GH_TOKEN: "" | ||||
|       - name: Replace version | ||||
|         id: replace-version | ||||
|         uses: JC5/firefly-iii-dev@v34 | ||||
|         uses: JC5/firefly-iii-dev@main | ||||
|         with: | ||||
|           action: 'ff3:version' | ||||
|           output: '' | ||||
| @@ -88,7 +88,7 @@ jobs: | ||||
|           FF_III_VERSION: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }} | ||||
|       - name: Generate JSON v1 | ||||
|         id: json-v1 | ||||
|         uses: JC5/firefly-iii-dev@v34 | ||||
|         uses: JC5/firefly-iii-dev@main | ||||
|         with: | ||||
|           action: 'ff3:json-translations v1' | ||||
|           output: '' | ||||
| @@ -97,7 +97,7 @@ jobs: | ||||
|           GH_TOKEN: '' | ||||
|       - name: Generate JSON v2 | ||||
|         id: json-v2 | ||||
|         uses: JC5/firefly-iii-dev@v34 | ||||
|         uses: JC5/firefly-iii-dev@main | ||||
|         with: | ||||
|           action: 'ff3:json-translations v2' | ||||
|           output: '' | ||||
| @@ -106,45 +106,76 @@ jobs: | ||||
|           GH_TOKEN: '' | ||||
|       - name: Code cleanup | ||||
|         id: code-cleanup | ||||
|         uses: JC5/firefly-iii-dev@v34 | ||||
|         uses: JC5/firefly-iii-dev@main | ||||
|         with: | ||||
|           action: 'ff3:code' | ||||
|           output: '' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: '' | ||||
|       - name: Build new JS | ||||
|       - name: Build JS | ||||
|         run: | | ||||
|           pwd | ||||
|           npm install | ||||
|           npm run build | ||||
|       - name: Build old JS | ||||
|         id: old-js | ||||
|         uses: JC5/firefly-iii-dev@v34 | ||||
|         with: | ||||
|           action: 'ff3:old-js' | ||||
|           output: '' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: '' | ||||
|           npm update | ||||
|           npm run prod  --workspace=v1 | ||||
|           npm run build --workspace=v2 | ||||
|       - name: Run CI | ||||
|         run: | | ||||
|           rm -rf vendor composer.lock | ||||
|           composer validate --strict | ||||
|           composer update --no-dev --no-scripts --no-plugins -q | ||||
|           sudo chown -R runner:docker resources/lang | ||||
|           .ci/phpcs.sh | ||||
|       - name: Import GPG key | ||||
|         uses: crazy-max/ghaction-import-gpg@v6 | ||||
|         with: | ||||
|           gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} | ||||
|           passphrase: ${{ secrets.PASSPHRASE }} | ||||
|       - name: Release | ||||
|         run: | | ||||
|           # do some configuration | ||||
|           sudo timedatectl set-timezone Europe/Amsterdam | ||||
|           git config user.name github-actions | ||||
|           git config user.email 41898282+github-actions[bot]@users.noreply.github.com | ||||
|           git config advice.addIgnoredFile false | ||||
|  | ||||
|           # set some variables | ||||
|           releaseName=$version | ||||
|           originalName=$version | ||||
|           zipName=FireflyIII-$version.zip | ||||
|           tarName=FireflyIII-$version.tar.gz | ||||
|  | ||||
|           # update composer (again) | ||||
|           composer update --no-dev --no-scripts --no-plugins | ||||
|           composer dump-autoload | ||||
|  | ||||
|           # if this is a develop build, slightly different variable names. | ||||
|           if [[ "develop" == "$version" ]]; then | ||||
|             [[ -z $(git status --untracked-files=normal --porcelain) ]] && echo "this branch is clean, no need to push..." && exit 0; | ||||
|             releaseName=$version-$(date +'%Y%m%d') | ||||
|             originalName=$releaseName | ||||
|             zipName=FireflyIII-develop.zip | ||||
|             tarName=FireflyIII-develop.tar.gz | ||||
|           fi | ||||
|  | ||||
|           # in both cases, if the release or tag already exists, add ".1" until it no longer exists. | ||||
|           tagFound=true | ||||
|           tagCount=1 | ||||
|           while [ "$tagFound" = true ] | ||||
|           do | ||||
|             if [ $(git tag -l "$releaseName") ]; then | ||||
|               echo "Tag $releaseName exists already." | ||||
|               releaseName="$originalName"."$tagCount" | ||||
|               echo "Tag for release is now $releaseName" | ||||
|               tagCount=$((tagCount+1)) | ||||
|             else | ||||
|              echo "Tag $releaseName does not exist, can continue" | ||||
|              tagFound=false | ||||
|             fi | ||||
|           done | ||||
|           echo "Will use tag and release name $releaseName." | ||||
|  | ||||
|           # add all content, except output.txt (this contains the changelog and/or the download instructions) | ||||
|           echo 'Add all and reset output.txt' | ||||
|           git add -A | ||||
|           if test -f "output.txt"; then | ||||
|             git reset output.txt | ||||
| @@ -152,19 +183,101 @@ jobs: | ||||
|           git commit -m "Auto commit for release '$version' on $(date +'%Y-%m-%d')" || true | ||||
|           git push | ||||
|  | ||||
|           # zip and tar everything | ||||
|           echo 'Zip and tar...' | ||||
|           zip -rq $zipName . -x "*.git*" "*.ci*" "*.github*" "*node_modules*" "*output.txt*" | ||||
|           touch $tarName | ||||
|           tar --exclude=$tarName --exclude=$zipName --exclude='./.git' --exclude='./.ci' --exclude='./.github' --exclude='./node_modules' --exclude='./output.txt' -czf $tarName . | ||||
|  | ||||
|           # add sha256 sum | ||||
|           echo 'Sha sum ...' | ||||
|           sha256sum -b $zipName > $zipName.sha256 | ||||
|           sha256sum -b $tarName > $tarName.sha256 | ||||
|  | ||||
|           # add signatures: | ||||
|           gpg --armor --detach-sign $zipName | ||||
|           gpg --armor --detach-sign $tarName | ||||
|  | ||||
|           # create a development (nightly) release: | ||||
|           if [[ "develop" == "$version" ]]; then | ||||
|             echo "Create nightly release." | ||||
|             git tag -a $version-$(date +'%Y%m%d') -m "Nightly development release '$version' on $(date +'%Y-%m-%d')" | ||||
|             git push origin $version-$(date +'%Y%m%d') | ||||
|             gh release create $version-$(date +'%Y%m%d') -p --verify-tag \ | ||||
|               -t "Development release for $(date +'%Y-%m-%d')" \ | ||||
|               -n "Bi-weekly development release of Firefly III with the latest fixes, translations and features. This release was created on **$(date +'%Y-%m-%d')** and may contain bugs. Use at your own risk. Docker users can find this release under the \`develop\` tag." | ||||
|           else | ||||
|             echo "Create default release." | ||||
|             git tag -a $version -m "Here be changelog" | ||||
|             git push origin $version | ||||
|             gh release create $version -F output.txt -t "$version" --verify-tag | ||||
|             echo 'Develop release.' | ||||
|             # add text to output.txt (instructions) | ||||
|             rm output.txt | ||||
|             echo "Bi-weekly development release of Firefly III with the latest fixes, translations and features. Docker users can find this release under the \`develop\` tag." >> output.txt | ||||
|             echo "" >> output.txt | ||||
|             echo "This release was created on **$(date +'%Y-%m-%d')** and may contain unexpected bugs. Data loss is rare but is not impossible. The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt | ||||
|             echo "" >> output.txt | ||||
|             echo "* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt | ||||
|             echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt | ||||
|             echo "" >> output.txt | ||||
|             echo ":warning: Please be careful with this pre-release, as it may not work as expected." >> output.txt | ||||
|  | ||||
|             # create the release: | ||||
|             echo "Create nightly release." | ||||
|             git tag -a $releaseName -m "Nightly development release '$version' on $(date +'%Y-%m-%d')" | ||||
|             git push origin $releaseName | ||||
|             gh release create $releaseName -p --verify-tag \ | ||||
|               -t "Development release for $(date +'%Y-%m-%d')" \ | ||||
|               -F output.txt | ||||
|  | ||||
|             # add zip file to release. | ||||
|             gh release upload $releaseName $zipName | ||||
|             gh release upload $releaseName $tarName | ||||
|  | ||||
|             # add sha256 sum to release | ||||
|             gh release upload $releaseName $zipName.sha256 | ||||
|             gh release upload $releaseName $tarName.sha256 | ||||
|  | ||||
|             # add signatures to release | ||||
|             gh release upload $releaseName $zipName.asc | ||||
|             gh release upload $releaseName $tarName.asc | ||||
|  | ||||
|             # get current HEAD and add as file to the release | ||||
|             HEAD=$(git rev-parse HEAD) | ||||
|             echo $HEAD > HEAD.txt | ||||
|             gh release upload $releaseName HEAD.txt | ||||
|           else | ||||
|             echo 'MAIN (real) release' | ||||
|             sudo chown -R runner:docker output.txt | ||||
|             # add text to output.txt (more instructions) | ||||
|             echo '' >> output.txt | ||||
|             echo '### Instructions' >> output.txt | ||||
|             echo '' >> output.txt | ||||
|             echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt | ||||
|             echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt | ||||
|             echo "* The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." | ||||
|  | ||||
|             echo "Create default release." | ||||
|             git tag -a $releaseName -m "Here be changelog" | ||||
|             git push origin $releaseName | ||||
|             gh release create $releaseName -F output.txt -t "$releaseName" --verify-tag | ||||
|  | ||||
|             # add archive files to release | ||||
|             gh release upload $releaseName $zipName | ||||
|             gh release upload $releaseName $tarName | ||||
|  | ||||
|             # add sha256 sums to release | ||||
|             gh release upload $releaseName $zipName.sha256 | ||||
|             gh release upload $releaseName $tarName.sha256 | ||||
|  | ||||
|             # add signatures to release | ||||
|             gh release upload $releaseName $zipName.asc | ||||
|             gh release upload $releaseName $tarName.asc | ||||
|  | ||||
|             # get current HEAD and add as file to the release | ||||
|             HEAD=$(git rev-parse HEAD) | ||||
|             echo $HEAD > HEAD.txt | ||||
|             gh release upload $releaseName HEAD.txt | ||||
|  | ||||
|             # remove all temporary files | ||||
|             rm output.txt | ||||
|             rm HEAD.txt | ||||
|             rm $zipName | ||||
|             rm $zipName.sha256 | ||||
|             rm $tarName | ||||
|             rm $tarName.sha256 | ||||
|  | ||||
|             # merge main back into develop | ||||
|             git checkout develop | ||||
|             git merge main | ||||
|             git push | ||||
|   | ||||
							
								
								
									
										9
									
								
								.github/workflows/sonarcloud.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/sonarcloud.yml
									
									
									
									
										vendored
									
									
								
							| @@ -45,15 +45,6 @@ jobs: | ||||
|       - name: Install Composer dependencies | ||||
|         run: composer install --prefer-dist --no-interaction --no-progress --no-scripts | ||||
|  | ||||
|       - name: PHPStan | ||||
|         run: .ci/phpstan.sh | ||||
|  | ||||
|       - name: PHPMD | ||||
|         run: .ci/phpmd.sh | ||||
|  | ||||
|       - name: PHP CS Fixer | ||||
|         run: .ci/phpcs.sh | ||||
|  | ||||
|       - name: "Create database file" | ||||
|         run: touch storage/database/database.sqlite | ||||
|  | ||||
|   | ||||
							
								
								
									
										14
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| name: "Issues - Mark and close stale issues" | ||||
| on: | ||||
|   schedule: | ||||
|     - cron: "30 1 * * *" | ||||
|     - cron: "0 4 * * *" | ||||
|   workflow_dispatch: | ||||
|  | ||||
| permissions: | ||||
| @@ -17,17 +17,17 @@ jobs: | ||||
|       - uses: actions/stale@v9 | ||||
|         with: | ||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           stale-issue-message: > | ||||
|             Hi there!  | ||||
|              | ||||
|           stale-issue-message: | | ||||
|             Hi there! | ||||
|  | ||||
|             This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|             This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. | ||||
|  | ||||
|             Thank you for your contributions. | ||||
|           stale-pr-message: > | ||||
|             Hi there!  | ||||
|              | ||||
|           stale-pr-message: | | ||||
|             Hi there! | ||||
|  | ||||
|             This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|             This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. | ||||
|   | ||||
							
								
								
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -7,3 +7,14 @@ yarn-error.log | ||||
| .env | ||||
| /.ci/php-cs-fixer/vendor | ||||
| coverage.xml | ||||
|  | ||||
| # ignore generated files. | ||||
| public/build | ||||
|  | ||||
| # ignore v1 build files | ||||
| resources/assets/v1/node_modules | ||||
| resources/assets/v1/build | ||||
|  | ||||
| # ignore v2 build files | ||||
| resources/assets/v2/node_modules | ||||
| resources/assets/v2/build | ||||
|   | ||||
							
								
								
									
										193
									
								
								THANKS.md
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										193
									
								
								THANKS.md
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,193 @@ | ||||
| # Thank you! :tada: :heart: :tada: | ||||
| 
 | ||||
| Over time, many people have contributed to Firefly III. Their efforts are not always visible, but always remembered and appreciated. | ||||
| Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution. | ||||
| 
 | ||||
| ## 2024 | ||||
| - imlonghao | ||||
| - Rahman Yusuf | ||||
| - Michael Thomas | ||||
| - WardenJakx | ||||
| - kuilin | ||||
| - Stevie Robinson | ||||
| - luzpaz | ||||
| - Lemuel Roberto Bonifácio | ||||
| - maureenferreira | ||||
| 
 | ||||
| ## 2023 | ||||
| - tieu1991 | ||||
| - Maxco10 | ||||
| - zqye | ||||
| - Mateus Pereira | ||||
| - josephbadow | ||||
| - Christian Desktop | ||||
| - Edgars | ||||
| - Hannah K | ||||
| - noxonad | ||||
| - Kaijia Feng | ||||
| - Marc Ordinas i Llopis | ||||
| - Kuba Turek | ||||
| - Julien Stébenne | ||||
| 
 | ||||
| ## 2022 | ||||
| - Johannes Zellner | ||||
| - Janne Heß | ||||
| - charlesteets | ||||
| - Nathan PERIER | ||||
| - Jan Willhaus | ||||
| - canoine | ||||
| - Rick Cuddy | ||||
| - James | ||||
| - Hugo Meyronneinc | ||||
| - naveen | ||||
| - neilnaveen | ||||
| - naveensrinivasan | ||||
| - Federico Micelli | ||||
| - George Hahn | ||||
| 
 | ||||
| ## 2021 | ||||
| - StillLoading | ||||
| - Igor Rzegocki | ||||
| - Lorenzo Breda | ||||
| - Hosh | ||||
| - Flightkick | ||||
| - alex6480 | ||||
| - VREEdom | ||||
| - Hamza FADIL | ||||
| - Kasper Læssø Sørensen | ||||
| - Alex | ||||
| - Jeroen De Meerleer | ||||
| - Ruben van Erk | ||||
| - Fabian Zimmermann | ||||
| - Mirko Berger | ||||
| - KaihatsuOnline | ||||
| - MihataBG | ||||
| 
 | ||||
| ## 2020 | ||||
| - Hannes Körber | ||||
| - Julien Cassagne | ||||
| - bu4ak | ||||
| - Viktor Yakovlev | ||||
| - Oliver Kaufmann | ||||
| - Arvind Chembarpu | ||||
| - GrayStrider | ||||
| - psychowood | ||||
| - Hosh Sadiq | ||||
| - emansih | ||||
| - Aniruddha Maru | ||||
| - johnny | ||||
| - sephrat | ||||
| - bpatath | ||||
| - Florian Dupret | ||||
| - Maxim Kurbatov | ||||
| - Lucas Guima | ||||
| - Sandro | ||||
| - Ruben Verhoef | ||||
| - Daniel Idzerda | ||||
| - Calum Smith | ||||
| - Agraphie | ||||
| - Tomer Shvueli | ||||
| - Tomer S | ||||
| 
 | ||||
| ## 2019 | ||||
| - Pascal Jungblut | ||||
| - Justyn Shull | ||||
| - Timendum | ||||
| - Nicolas Lœuillet | ||||
| - Dominic Guhl | ||||
| - Melroy van den Berg | ||||
| - Henning Stein | ||||
| - Jan Klepek | ||||
| - Jonathan | ||||
| - Geoffrey “Frogeye” Preud'homme | ||||
| - Michael Fix | ||||
| - Juraj Mlich | ||||
| - Eddybrando Vásquez | ||||
| - hulloanson | ||||
| - Will Rouesnel | ||||
| - lastlink | ||||
| - Mr. Funk | ||||
| - Simon Taddiken | ||||
| - Joris | ||||
| - Bastiaan Nijkamp | ||||
| 
 | ||||
| ## 2018 | ||||
| - a1ex4 | ||||
| - Daniel Quah | ||||
| - Marco Lourenço | ||||
| - Dennis Enderink | ||||
| - Luca Bognolo | ||||
| - Mike Conway | ||||
| - Ben | ||||
| - Mathieu Post | ||||
| - George Hertz | ||||
| - HamuZ HamuZ | ||||
| - David Meiseles | ||||
| - Erik Gelderblom | ||||
| - Luca Vallerini | ||||
| - Clemens Wijnekus | ||||
| - Jacob Weisz | ||||
| - Mateusz Gozdek | ||||
| - anmol26s | ||||
| - Kevin Hellemun | ||||
| - Shashank M Chakravarthy | ||||
| - Nico Schreiner | ||||
| - Paul Sohier | ||||
| - Brenden Conte | ||||
| - Ben Yanke | ||||
| - Andrew Prokhorenkov | ||||
| - devlearner | ||||
| - Kelvin | ||||
| - J'informatique | ||||
| 
 | ||||
| ## 2017 | ||||
| - Victor Mosin | ||||
| - Justin | ||||
| - Hugo van Duijn | ||||
| - Lukas Winkler | ||||
| - Marcin Szymanski | ||||
| - Jens Kat | ||||
| - koziolek | ||||
| - jleeong | ||||
| - Simon Hanna | ||||
| - richard & xeli.eu | ||||
| - Sergey Besedin | ||||
| - Welbert Serra | ||||
| - Joris de Vries | ||||
| - Patrick Kostjens | ||||
| - Enrico Lamperti | ||||
| - Christian Musa | ||||
| - Enno Lohmeier | ||||
| 
 | ||||
| ## 2016 | ||||
| - Sander | ||||
| - Toon Schoenmakers | ||||
| - Telyn | ||||
| - Sander Kleykens | ||||
| - Tom van der Werf | ||||
| - Matthew Peck | ||||
| - Sander Mulders | ||||
| - Bonno Nachtegaal-Karels | ||||
| - Niek Haarman | ||||
| - Edwin | ||||
| - Thijs Alkemade | ||||
| - zjean | ||||
| - Graham Miller | ||||
| - Robert Horlings | ||||
| - leander091 | ||||
| 
 | ||||
| ## 2015 | ||||
| - Antonio Spinelli | ||||
| - Colin O'Dell | ||||
| - RonaldvanMeer | ||||
| - Richard Ebbers | ||||
| - Balazs Varkonyi | ||||
| - Niek van der Kooy | ||||
| - Ilya Kil | ||||
| 
 | ||||
| ## 2014 | ||||
| - Stewart Malik | ||||
| - Graham Campbell | ||||
| 
 | ||||
| 
 | ||||
| Thank you for all your support! | ||||
| @@ -83,17 +83,17 @@ class AccountController extends Controller | ||||
|         // user's preferences
 | ||||
|         $defaultSet = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray(); | ||||
| 
 | ||||
|         /** @var Preference $frontPage */ | ||||
|         $frontPage  = app('preferences')->get('frontPageAccounts', $defaultSet); | ||||
|         /** @var Preference $frontpage */ | ||||
|         $frontpage  = app('preferences')->get('frontpageAccounts', $defaultSet); | ||||
|         $default    = app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|         if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) { | ||||
|             $frontPage->data = $defaultSet; | ||||
|             $frontPage->save(); | ||||
|         if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) { | ||||
|             $frontpage->data = $defaultSet; | ||||
|             $frontpage->save(); | ||||
|         } | ||||
| 
 | ||||
|         // get accounts:
 | ||||
|         $accounts   = $this->repository->getAccountsById($frontPage->data); | ||||
|         $accounts   = $this->repository->getAccountsById($frontpage->data); | ||||
|         $chartData  = []; | ||||
| 
 | ||||
|         /** @var Account $account */ | ||||
|   | ||||
| @@ -72,6 +72,12 @@ class UpdateController extends Controller | ||||
|         app('log')->debug('Now in update routine for transaction group!'); | ||||
|         $data             = $request->getAll(); | ||||
| 
 | ||||
|         // Fixes 8750.
 | ||||
|         $transactions     = $data['transactions'] ?? []; | ||||
|         foreach ($transactions as $index => $info) { | ||||
|             unset($data['transactions'][$index]['type']); | ||||
|         } | ||||
| 
 | ||||
|         $transactionGroup = $this->groupRepository->update($transactionGroup, $data); | ||||
|         $manager          = $this->getManager(); | ||||
| 
 | ||||
|   | ||||
| @@ -281,7 +281,7 @@ class BasicController extends Controller | ||||
|             $spentInCurrency = $row['sum']; | ||||
|             $leftToSpend     = bcadd($amount, $spentInCurrency); | ||||
| 
 | ||||
|             $days            = $today->diffInDays($end) + 1; | ||||
|             $days            = (int)$today->diffInDays($end, true) + 1; | ||||
|             $perDay          = '0'; | ||||
|             if (0 !== $days && bccomp($leftToSpend, '0') > -1) { | ||||
|                 $perDay = bcdiv($leftToSpend, (string)$days); | ||||
|   | ||||
| @@ -32,6 +32,7 @@ use FireflyIII\Models\Preference; | ||||
| use FireflyIII\Transformers\PreferenceTransformer; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Pagination\LengthAwarePaginator; | ||||
| use Illuminate\Support\Collection; | ||||
| use League\Fractal\Pagination\IlluminatePaginatorAdapter; | ||||
| use League\Fractal\Resource\Collection as FractalCollection; | ||||
| use League\Fractal\Resource\Item; | ||||
| @@ -84,6 +85,10 @@ class PreferencesController extends Controller | ||||
|     { | ||||
|         $manager     = $this->getManager(); | ||||
| 
 | ||||
|         if ('currencyPreference' === $preference->name) { | ||||
|             throw new FireflyException('Please use api/v1/currencies/default instead.'); | ||||
|         } | ||||
| 
 | ||||
|         /** @var PreferenceTransformer $transformer */ | ||||
|         $transformer = app(PreferenceTransformer::class); | ||||
|         $transformer->setParameters($this->parameters); | ||||
| @@ -93,6 +98,32 @@ class PreferencesController extends Controller | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * TODO This endpoint is not documented. | ||||
|      * | ||||
|      * Return a single preference by name. | ||||
|      */ | ||||
|     public function showList(Collection $collection): JsonResponse | ||||
|     { | ||||
|         $manager     = $this->getManager(); | ||||
|         $count       = $collection->count(); | ||||
|         $pageSize    = $this->parameters->get('limit'); | ||||
|         $preferences = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
| 
 | ||||
|         // make paginator:
 | ||||
|         $paginator   = new LengthAwarePaginator($preferences, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $paginator->setPath(route('api.v1.preferences.show-list').$this->buildParams()); | ||||
| 
 | ||||
|         /** @var PreferenceTransformer $transformer */ | ||||
|         $transformer = app(PreferenceTransformer::class); | ||||
|         $transformer->setParameters($this->parameters); | ||||
| 
 | ||||
|         $resource    = new FractalCollection($preferences, $transformer, self::RESOURCE_KEY); | ||||
|         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); | ||||
| 
 | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This endpoint is documented at: | ||||
|      * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/preferences/storePreference
 | ||||
| @@ -103,6 +134,11 @@ class PreferencesController extends Controller | ||||
|     { | ||||
|         $manager     = $this->getManager(); | ||||
|         $data        = $request->getAll(); | ||||
| 
 | ||||
|         if ('currencyPreference' === $data['name']) { | ||||
|             throw new FireflyException('Please use api/v1/currencies/default instead.'); | ||||
|         } | ||||
| 
 | ||||
|         $pref        = app('preferences')->set($data['name'], $data['data']); | ||||
| 
 | ||||
|         /** @var PreferenceTransformer $transformer */ | ||||
| @@ -122,6 +158,10 @@ class PreferencesController extends Controller | ||||
|      */ | ||||
|     public function update(PreferenceUpdateRequest $request, Preference $preference): JsonResponse | ||||
|     { | ||||
|         if ('currencyPreference' === $preference->name) { | ||||
|             throw new FireflyException('Please use api/v1/currencies/default instead.'); | ||||
|         } | ||||
| 
 | ||||
|         $manager     = $this->getManager(); | ||||
|         $data        = $request->getAll(); | ||||
|         $pref        = app('preferences')->set($preference->name, $data['data']); | ||||
|   | ||||
| @@ -46,7 +46,7 @@ class DateRequest extends FormRequest | ||||
|     { | ||||
|         $start = $this->getCarbonDate('start'); | ||||
|         $end   = $this->getCarbonDate('end'); | ||||
|         if ($start->diffInYears($end) > 5) { | ||||
|         if ($start->diffInYears($end, true) > 5) { | ||||
|             throw new FireflyException('Date range out of range.'); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -71,8 +71,9 @@ class StoreRequest extends FormRequest | ||||
|         if (is_array($triggers)) { | ||||
|             foreach ($triggers as $trigger) { | ||||
|                 $return[] = [ | ||||
|                     'type'            => $trigger['type'], | ||||
|                     'value'           => $trigger['value'], | ||||
|                     'type'            => $trigger['type'] ?? '', | ||||
|                     'value'           => $trigger['value'] ?? null, | ||||
|                     'prohibited'      => $this->convertBoolean((string)($trigger['prohibited'] ?? 'false')), | ||||
|                     'active'          => $this->convertBoolean((string)($trigger['active'] ?? 'true')), | ||||
|                     'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')), | ||||
|                 ]; | ||||
|   | ||||
| @@ -54,17 +54,13 @@ class TestRequest extends FormRequest | ||||
| 
 | ||||
|     private function getDate(string $field): ?Carbon | ||||
|     { | ||||
|         $value  = $this->query($field); | ||||
|         $value = $this->query($field); | ||||
|         if (is_array($value)) { | ||||
|             return null; | ||||
|         } | ||||
|         $value  = (string)$value; | ||||
|         $result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); | ||||
|         if (false === $result) { | ||||
|             return null; | ||||
|         } | ||||
|         $value = (string)$value; | ||||
| 
 | ||||
|         return $result; | ||||
|         return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); | ||||
|     } | ||||
| 
 | ||||
|     private function getAccounts(): array | ||||
|   | ||||
| @@ -48,17 +48,13 @@ class TriggerRequest extends FormRequest | ||||
| 
 | ||||
|     private function getDate(string $field): ?Carbon | ||||
|     { | ||||
|         $value  = $this->query($field); | ||||
|         $value = $this->query($field); | ||||
|         if (is_array($value)) { | ||||
|             return null; | ||||
|         } | ||||
|         $value  = (string)$value; | ||||
|         $result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); | ||||
|         if (false === $result) { | ||||
|             return null; | ||||
|         } | ||||
|         $value = (string)$value; | ||||
| 
 | ||||
|         return $result; | ||||
|         return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); | ||||
|     } | ||||
| 
 | ||||
|     private function getAccounts(): array | ||||
|   | ||||
| @@ -81,10 +81,12 @@ class UpdateRequest extends FormRequest | ||||
|         if (is_array($triggers)) { | ||||
|             foreach ($triggers as $trigger) { | ||||
|                 $active         = array_key_exists('active', $trigger) ? $trigger['active'] : true; | ||||
|                 $prohibited     = array_key_exists('prohibited', $trigger) ? $trigger['prohibited'] : false; | ||||
|                 $stopProcessing = array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false; | ||||
|                 $return[]       = [ | ||||
|                     'type'            => $trigger['type'], | ||||
|                     'value'           => $trigger['value'], | ||||
|                     'prohibited'      => $prohibited, | ||||
|                     'active'          => $active, | ||||
|                     'stop_processing' => $stopProcessing, | ||||
|                 ]; | ||||
|   | ||||
| @@ -48,17 +48,13 @@ class TestRequest extends FormRequest | ||||
| 
 | ||||
|     private function getDate(string $field): ?Carbon | ||||
|     { | ||||
|         $value  = $this->query($field); | ||||
|         $value = $this->query($field); | ||||
|         if (is_array($value)) { | ||||
|             return null; | ||||
|         } | ||||
|         $value  = (string)$value; | ||||
|         $result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); | ||||
|         if (false === $result) { | ||||
|             return null; | ||||
|         } | ||||
|         $value = (string)$value; | ||||
| 
 | ||||
|         return $result; | ||||
|         return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); | ||||
|     } | ||||
| 
 | ||||
|     private function getAccounts(): array | ||||
|   | ||||
| @@ -48,17 +48,13 @@ class TriggerRequest extends FormRequest | ||||
| 
 | ||||
|     private function getDate(string $field): ?Carbon | ||||
|     { | ||||
|         $value  = $this->query($field); | ||||
|         $value = $this->query($field); | ||||
|         if (is_array($value)) { | ||||
|             return null; | ||||
|         } | ||||
|         $value  = (string)$value; | ||||
|         $result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); | ||||
|         if (false === $result) { | ||||
|             return null; | ||||
|         } | ||||
|         $value = (string)$value; | ||||
| 
 | ||||
|         return $result; | ||||
|         return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); | ||||
|     } | ||||
| 
 | ||||
|     private function getAccounts(): array | ||||
|   | ||||
| @@ -55,11 +55,7 @@ class AccountController extends Controller | ||||
|             function ($request, $next) { | ||||
|                 $this->repository      = app(AccountRepositoryInterface::class); | ||||
|                 $this->adminRepository = app(AdminAccountRepositoryInterface::class); | ||||
| 
 | ||||
|                 $userGroup             = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->adminRepository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->adminRepository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
| @@ -85,7 +81,7 @@ class AccountController extends Controller | ||||
|         $types           = $data['types']; | ||||
|         $query           = $data['query']; | ||||
|         $date            = $this->parameters->get('date') ?? today(config('app.timezone')); | ||||
|         $result          = $this->adminRepository->searchAccount((string)$query, $types, $data['limit']); | ||||
|         $result          = $this->adminRepository->searchAccount((string) $query, $types, $data['limit']); | ||||
|         $defaultCurrency = app('amount')->getDefaultCurrency(); | ||||
|         $groupedResult   = []; | ||||
|         $allItems        = []; | ||||
| @@ -99,19 +95,19 @@ class AccountController extends Controller | ||||
|                 $balance         = app('steam')->balance($account, $date); | ||||
|                 $nameWithBalance = sprintf('%s (%s)', $account->name, app('amount')->formatAnything($currency, $balance, false)); | ||||
|             } | ||||
|             $type            = (string)trans(sprintf('firefly.%s', $account->accountType->type)); | ||||
|             $type            = (string) trans(sprintf('firefly.%s', $account->accountType->type)); | ||||
|             $groupedResult[$type] ??= [ | ||||
|                 'group ' => $type, | ||||
|                 'items'  => [], | ||||
|             ]; | ||||
|             $allItems[]      = [ | ||||
|                 'id'                      => (string)$account->id, | ||||
|                 'value'                   => (string)$account->id, | ||||
|                 'id'                      => (string) $account->id, | ||||
|                 'value'                   => (string) $account->id, | ||||
|                 'name'                    => $account->name, | ||||
|                 'name_with_balance'       => $nameWithBalance, | ||||
|                 'label'                   => $nameWithBalance, | ||||
|                 'type'                    => $account->accountType->type, | ||||
|                 'currency_id'             => (string)$currency->id, | ||||
|                 'currency_id'             => (string) $currency->id, | ||||
|                 'currency_name'           => $currency->name, | ||||
|                 'currency_code'           => $currency->code, | ||||
|                 'currency_symbol'         => $currency->symbol, | ||||
| @@ -123,8 +119,8 @@ class AccountController extends Controller | ||||
|             $allItems, | ||||
|             static function (array $left, array $right): int { | ||||
|                 $order    = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE]; | ||||
|                 $posLeft  = (int)array_search($left['type'], $order, true); | ||||
|                 $posRight = (int)array_search($right['type'], $order, true); | ||||
|                 $posLeft  = (int) array_search($left['type'], $order, true); | ||||
|                 $posRight = (int) array_search($right['type'], $order, true); | ||||
| 
 | ||||
|                 return $posLeft - $posRight; | ||||
|             } | ||||
|   | ||||
| @@ -45,11 +45,7 @@ class CategoryController extends Controller | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(CategoryRepositoryInterface::class); | ||||
| 
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|   | ||||
| @@ -45,11 +45,7 @@ class TagController extends Controller | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(TagRepositoryInterface::class); | ||||
| 
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|   | ||||
| @@ -45,11 +45,7 @@ class TransactionController extends Controller | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(JournalRepositoryInterface::class); | ||||
| 
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|   | ||||
| @@ -27,6 +27,7 @@ namespace FireflyIII\Api\V2\Controllers\Chart; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Api\V2\Request\Chart\DashboardChartRequest; | ||||
| use FireflyIII\Enums\UserRoleEnum; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\AccountType; | ||||
| @@ -46,6 +47,7 @@ class AccountController extends Controller | ||||
|     use ValidatesUserGroupTrait; | ||||
| 
 | ||||
|     private AccountRepositoryInterface $repository; | ||||
|     protected array                    $acceptedRoles = [UserRoleEnum::READ_ONLY]; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
| @@ -53,10 +55,7 @@ class AccountController extends Controller | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(AccountRepositoryInterface::class); | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
| @@ -98,14 +97,14 @@ class AccountController extends Controller | ||||
|         // user's preferences
 | ||||
|         if (0 === $accounts->count()) { | ||||
|             $defaultSet = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])->pluck('id')->toArray(); | ||||
|             $frontPage  = app('preferences')->get('frontPageAccounts', $defaultSet); | ||||
|             $frontpage  = app('preferences')->get('frontpageAccounts', $defaultSet); | ||||
| 
 | ||||
|             if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) { | ||||
|                 $frontPage->data = $defaultSet; | ||||
|                 $frontPage->save(); | ||||
|             if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) { | ||||
|                 $frontpage->data = $defaultSet; | ||||
|                 $frontpage->save(); | ||||
|             } | ||||
| 
 | ||||
|             $accounts   = $this->repository->getAccountsById($frontPage->data); | ||||
|             $accounts   = $this->repository->getAccountsById($frontpage->data); | ||||
|         } | ||||
| 
 | ||||
|         // both options are overruled by "preselected"
 | ||||
|   | ||||
| @@ -27,6 +27,7 @@ namespace FireflyIII\Api\V2\Controllers\Chart; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Api\V2\Request\Chart\BalanceChartRequest; | ||||
| use FireflyIII\Enums\UserRoleEnum; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| @@ -42,6 +43,7 @@ use Illuminate\Support\Collection; | ||||
| class BalanceController extends Controller | ||||
| { | ||||
|     use CleansChartData; | ||||
|     protected array                    $acceptedRoles = [UserRoleEnum::READ_ONLY]; | ||||
| 
 | ||||
|     /** | ||||
|      * The code is practically a duplicate of ReportController::operations. | ||||
|   | ||||
| @@ -64,12 +64,9 @@ class BudgetController extends Controller | ||||
|                 $this->blRepository  = app(BudgetLimitRepositoryInterface::class); | ||||
|                 $this->opsRepository = app(OperationsRepositoryInterface::class); | ||||
|                 $this->currency      = app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|                 $userGroup           = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                     $this->opsRepository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($userGroup); | ||||
|                 $this->opsRepository->setUserGroup($userGroup); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
| @@ -124,11 +121,11 @@ class BudgetController extends Controller | ||||
|         foreach ($rows as $row) { | ||||
|             $current  = [ | ||||
|                 'label'                          => $budget->name, | ||||
|                 'currency_id'                    => (string)$row['currency_id'], | ||||
|                 'currency_id'                    => (string) $row['currency_id'], | ||||
|                 'currency_code'                  => $row['currency_code'], | ||||
|                 'currency_name'                  => $row['currency_name'], | ||||
|                 'currency_decimal_places'        => $row['currency_decimal_places'], | ||||
|                 'native_currency_id'             => (string)$row['native_currency_id'], | ||||
|                 'native_currency_id'             => (string) $row['native_currency_id'], | ||||
|                 'native_currency_code'           => $row['native_currency_code'], | ||||
|                 'native_currency_name'           => $row['native_currency_name'], | ||||
|                 'native_currency_decimal_places' => $row['native_currency_decimal_places'], | ||||
| @@ -189,12 +186,12 @@ class BudgetController extends Controller | ||||
|         foreach ($array as $currencyId => $block) { | ||||
|             $this->currencies[$currencyId] ??= TransactionCurrency::find($currencyId); | ||||
|             $return[$currencyId]           ??= [ | ||||
|                 'currency_id'                    => (string)$currencyId, | ||||
|                 'currency_id'                    => (string) $currencyId, | ||||
|                 'currency_code'                  => $block['currency_code'], | ||||
|                 'currency_name'                  => $block['currency_name'], | ||||
|                 'currency_symbol'                => $block['currency_symbol'], | ||||
|                 'currency_decimal_places'        => (int)$block['currency_decimal_places'], | ||||
|                 'native_currency_id'             => (string)$this->currency->id, | ||||
|                 'currency_decimal_places'        => (int) $block['currency_decimal_places'], | ||||
|                 'native_currency_id'             => (string) $this->currency->id, | ||||
|                 'native_currency_code'           => $this->currency->code, | ||||
|                 'native_currency_name'           => $this->currency->name, | ||||
|                 'native_currency_symbol'         => $this->currency->symbol, | ||||
|   | ||||
| @@ -57,10 +57,7 @@ class CategoryController extends Controller | ||||
|             function ($request, $next) { | ||||
|                 $this->accountRepos  = app(AccountRepositoryInterface::class); | ||||
|                 $this->currencyRepos = app(CurrencyRepositoryInterface::class); | ||||
|                 $userGroup           = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->accountRepos->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->accountRepos->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
| @@ -100,25 +97,25 @@ class CategoryController extends Controller | ||||
| 
 | ||||
|         /** @var array $journal */ | ||||
|         foreach ($journals as $journal) { | ||||
|             $currencyId                    = (int)$journal['currency_id']; | ||||
|             $currencyId                    = (int) $journal['currency_id']; | ||||
|             $currency                      = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId); | ||||
|             $currencies[$currencyId]       = $currency; | ||||
|             $categoryName                  = null === $journal['category_name'] ? (string)trans('firefly.no_category') : $journal['category_name']; | ||||
|             $categoryName                  = null === $journal['category_name'] ? (string) trans('firefly.no_category') : $journal['category_name']; | ||||
|             $amount                        = app('steam')->positive($journal['amount']); | ||||
|             $nativeAmount                  = $converter->convert($default, $currency, $journal['date'], $amount); | ||||
|             $key                           = sprintf('%s-%s', $categoryName, $currency->code); | ||||
|             if ((int)$journal['foreign_currency_id'] === $default->id) { | ||||
|             if ((int) $journal['foreign_currency_id'] === $default->id) { | ||||
|                 $nativeAmount = app('steam')->positive($journal['foreign_amount']); | ||||
|             } | ||||
|             // create arrays
 | ||||
|             $return[$key] ??= [ | ||||
|                 'label'                          => $categoryName, | ||||
|                 'currency_id'                    => (string)$currency->id, | ||||
|                 'currency_id'                    => (string) $currency->id, | ||||
|                 'currency_code'                  => $currency->code, | ||||
|                 'currency_name'                  => $currency->name, | ||||
|                 'currency_symbol'                => $currency->symbol, | ||||
|                 'currency_decimal_places'        => $currency->decimal_places, | ||||
|                 'native_currency_id'             => (string)$default->id, | ||||
|                 'native_currency_id'             => (string) $default->id, | ||||
|                 'native_currency_code'           => $default->code, | ||||
|                 'native_currency_name'           => $default->name, | ||||
|                 'native_currency_symbol'         => $default->symbol, | ||||
| @@ -138,7 +135,7 @@ class CategoryController extends Controller | ||||
| 
 | ||||
|         // order by native amount
 | ||||
|         usort($return, static function (array $a, array $b) { | ||||
|             return (float)$a['native_amount'] < (float)$b['native_amount'] ? 1 : -1; | ||||
|             return (float) $a['native_amount'] < (float) $b['native_amount'] ? 1 : -1; | ||||
|         }); | ||||
|         $converter->summarize(); | ||||
| 
 | ||||
|   | ||||
| @@ -27,6 +27,7 @@ namespace FireflyIII\Api\V2\Controllers; | ||||
| use Carbon\Carbon; | ||||
| use Carbon\Exceptions\InvalidDateException; | ||||
| use Carbon\Exceptions\InvalidFormatException; | ||||
| use FireflyIII\Enums\UserRoleEnum; | ||||
| use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; | ||||
| use FireflyIII\Transformers\V2\AbstractTransformer; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| @@ -55,6 +56,7 @@ class Controller extends BaseController | ||||
| 
 | ||||
|     protected const string CONTENT_TYPE = 'application/vnd.api+json'; | ||||
|     protected ParameterBag $parameters; | ||||
|     protected array $acceptedRoles      = [UserRoleEnum::READ_ONLY]; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
| @@ -120,6 +122,9 @@ class Controller extends BaseController | ||||
|                     $obj = null; | ||||
|                 } | ||||
|             } | ||||
|             if (null !== $date && 'end' === $field) { | ||||
|                 $obj->endOfDay(); | ||||
|             } | ||||
|             $bag->set($field, $obj); | ||||
|         } | ||||
| 
 | ||||
| @@ -152,6 +157,9 @@ class Controller extends BaseController | ||||
|     { | ||||
|         $manager  = new Manager(); | ||||
|         $baseUrl  = request()->getSchemeAndHttpHost().'/api/v2'; | ||||
| 
 | ||||
|         // TODO add stuff to path?
 | ||||
| 
 | ||||
|         $manager->setSerializer(new JsonApiSerializer($baseUrl)); | ||||
| 
 | ||||
|         $objects  = $paginator->getCollection(); | ||||
|   | ||||
| @@ -25,17 +25,19 @@ namespace FireflyIII\Api\V2\Controllers\Model\Account; | ||||
| 
 | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Api\V2\Request\Model\Account\IndexRequest; | ||||
| use FireflyIII\Api\V2\Request\Model\Transaction\InfiniteListRequest; | ||||
| use FireflyIII\Enums\UserRoleEnum; | ||||
| use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Transformers\V2\AccountTransformer; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Pagination\LengthAwarePaginator; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| 
 | ||||
| class IndexController extends Controller | ||||
| { | ||||
|     public const string RESOURCE_KEY = 'accounts'; | ||||
|     public const string RESOURCE_KEY                  = 'accounts'; | ||||
| 
 | ||||
|     private AccountRepositoryInterface $repository; | ||||
|     protected array                    $acceptedRoles = [UserRoleEnum::READ_ONLY, UserRoleEnum::MANAGE_TRANSACTIONS]; | ||||
| 
 | ||||
|     /** | ||||
|      * AccountController constructor. | ||||
| @@ -48,9 +50,7 @@ class IndexController extends Controller | ||||
|                 $this->repository = app(AccountRepositoryInterface::class); | ||||
|                 // new way of user group validation
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($userGroup); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
| @@ -58,21 +58,35 @@ class IndexController extends Controller | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * TODO see autocomplete/accountcontroller for list. | ||||
|      * TODO the sort instructions need proper repeatable documentation. | ||||
|      * TODO see autocomplete/account controller for list. | ||||
|      */ | ||||
|     public function index(IndexRequest $request): JsonResponse | ||||
|     { | ||||
|         $this->repository->resetAccountOrder(); | ||||
|         $types        = $request->getAccountTypes(); | ||||
|         $instructions = $request->getSortInstructions('accounts'); | ||||
|         $accounts     = $this->repository->getAccountsByType($types, $instructions); | ||||
|         $pageSize     = $this->parameters->get('limit'); | ||||
|         $count        = $accounts->count(); | ||||
|         $accounts     = $accounts->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|         $paginator    = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $transformer  = new AccountTransformer(); | ||||
|         $types             = $request->getAccountTypes(); | ||||
|         $sorting           = $request->getSortInstructions('accounts'); | ||||
|         $filters           = $request->getFilterInstructions('accounts'); | ||||
|         $accounts          = $this->repository->getAccountsByType($types, $sorting, $filters); | ||||
|         $pageSize          = $this->parameters->get('limit'); | ||||
|         $count             = $accounts->count(); | ||||
| 
 | ||||
|         $this->parameters->set('sort', $instructions); | ||||
|         // depending on the sort parameters, this list must not be split, because the
 | ||||
|         // order is calculated in the account transformer and by that time it's too late.
 | ||||
|         $first             = array_key_first($sorting); | ||||
|         $disablePagination = in_array($first, ['last_activity', 'balance', 'balance_difference'], true); | ||||
|         Log::debug(sprintf('Will disable pagination in account index v2? %s', var_export($disablePagination, true))); | ||||
|         if (!$disablePagination) { | ||||
|             $accounts = $accounts->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|         } | ||||
|         $paginator         = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $transformer       = new AccountTransformer(); | ||||
| 
 | ||||
|         $this->parameters->set('disablePagination', $disablePagination); | ||||
|         $this->parameters->set('pageSize', $pageSize); | ||||
|         $this->parameters->set('sort', $sorting); | ||||
| 
 | ||||
|         $this->parameters->set('filters', $filters); | ||||
|         $transformer->setParameters($this->parameters); // give params to transformer
 | ||||
| 
 | ||||
|         return response() | ||||
| @@ -80,25 +94,4 @@ class IndexController extends Controller | ||||
|             ->header('Content-Type', self::CONTENT_TYPE) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     public function infiniteList(InfiniteListRequest $request): JsonResponse | ||||
|     { | ||||
|         $this->repository->resetAccountOrder(); | ||||
| 
 | ||||
|         // get accounts of the specified type, and return.
 | ||||
|         $types       = $request->getAccountTypes(); | ||||
| 
 | ||||
|         // get from repository
 | ||||
|         $accounts    = $this->repository->getAccountsInOrder($types, $request->getSortInstructions('accounts'), $request->getStartRow(), $request->getEndRow()); | ||||
|         $total       = $this->repository->countAccounts($types); | ||||
|         $count       = $request->getEndRow() - $request->getStartRow(); | ||||
|         $paginator   = new LengthAwarePaginator($accounts, $total, $count, $this->parameters->get('page')); | ||||
|         $transformer = new AccountTransformer(); | ||||
|         $transformer->setParameters($this->parameters); // give params to transformer
 | ||||
| 
 | ||||
|         return response() | ||||
|             ->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer)) | ||||
|             ->header('Content-Type', self::CONTENT_TYPE) | ||||
|         ; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,7 +25,9 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Api\V2\Controllers\Model\Account; | ||||
| 
 | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Enums\UserRoleEnum; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Transformers\V2\AccountTransformer; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| 
 | ||||
| @@ -36,6 +38,29 @@ use Illuminate\Http\JsonResponse; | ||||
|  */ | ||||
| class ShowController extends Controller | ||||
| { | ||||
|     public const string RESOURCE_KEY                  = 'accounts'; | ||||
| 
 | ||||
|     private AccountRepositoryInterface $repository; | ||||
|     protected array                    $acceptedRoles = [UserRoleEnum::READ_ONLY, UserRoleEnum::MANAGE_TRANSACTIONS]; | ||||
| 
 | ||||
|     /** | ||||
|      * AccountController constructor. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(AccountRepositoryInterface::class); | ||||
|                 // new way of user group validation
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 $this->repository->setUserGroup($userGroup); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * TODO this endpoint is not yet reachable. | ||||
|      */ | ||||
|   | ||||
							
								
								
									
										75
									
								
								app/Api/V2/Controllers/Model/Account/UpdateController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								app/Api/V2/Controllers/Model/Account/UpdateController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| <?php | ||||
| /* | ||||
|  * UpdateController.php | ||||
|  * Copyright (c) 2024 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\Api\V2\Controllers\Model\Account; | ||||
| 
 | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Api\V2\Request\Model\Account\UpdateRequest; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Transformers\V2\AccountTransformer; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| 
 | ||||
| class UpdateController extends Controller | ||||
| { | ||||
|     public const string RESOURCE_KEY = 'accounts'; | ||||
| 
 | ||||
|     private AccountRepositoryInterface $repository; | ||||
| 
 | ||||
|     /** | ||||
|      * AccountController constructor. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(AccountRepositoryInterface::class); | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * TODO this endpoint is not yet reachable. | ||||
|      */ | ||||
|     public function update(UpdateRequest $request, Account $account): JsonResponse | ||||
|     { | ||||
|         app('log')->debug(sprintf('Now in %s', __METHOD__)); | ||||
|         $data         = $request->getUpdateData(); | ||||
|         $data['type'] = config('firefly.shortNamesByFullName.'.$account->accountType->type); | ||||
|         $account      = $this->repository->update($account, $data); | ||||
|         $account->refresh(); | ||||
|         app('preferences')->mark(); | ||||
| 
 | ||||
|         $transformer  = new AccountTransformer(); | ||||
|         $transformer->setParameters($this->parameters); | ||||
| 
 | ||||
|         return response() | ||||
|             ->api($this->jsonApiObject('accounts', $account, $transformer)) | ||||
|             ->header('Content-Type', self::CONTENT_TYPE) | ||||
|         ; | ||||
|     } | ||||
| } | ||||
| @@ -46,12 +46,7 @@ class IndexController extends Controller | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(BillRepositoryInterface::class); | ||||
| 
 | ||||
|                 // new way of user group validation
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|   | ||||
| @@ -46,12 +46,7 @@ class ShowController extends Controller | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(BillRepositoryInterface::class); | ||||
| 
 | ||||
|                 // new way of user group validation
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|   | ||||
| @@ -45,11 +45,7 @@ class SumController extends Controller | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(BillRepositoryInterface::class); | ||||
| 
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|   | ||||
| @@ -46,11 +46,7 @@ class IndexController extends Controller | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(PiggyBankRepositoryInterface::class); | ||||
| 
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|   | ||||
| @@ -77,13 +77,11 @@ class BasicController extends Controller | ||||
|                 $this->opsRepository     = app(OperationsRepositoryInterface::class); | ||||
| 
 | ||||
|                 $userGroup               = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->abRepository->setUserGroup($userGroup); | ||||
|                     $this->accountRepository->setUserGroup($userGroup); | ||||
|                     $this->billRepository->setUserGroup($userGroup); | ||||
|                     $this->budgetRepository->setUserGroup($userGroup); | ||||
|                     $this->opsRepository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->abRepository->setUserGroup($userGroup); | ||||
|                 $this->accountRepository->setUserGroup($userGroup); | ||||
|                 $this->billRepository->setUserGroup($userGroup); | ||||
|                 $this->budgetRepository->setUserGroup($userGroup); | ||||
|                 $this->opsRepository->setUserGroup($userGroup); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
| @@ -298,7 +296,7 @@ class BasicController extends Controller | ||||
|             app('log')->debug(sprintf('Amount left is %s', $left)); | ||||
| 
 | ||||
|             // how much left per day?
 | ||||
|             $days                    = $today->diffInDays($end) + 1; | ||||
|             $days                    = (int)$today->diffInDays($end, true) + 1; | ||||
|             $perDay                  = '0'; | ||||
|             $perDayNative            = '0'; | ||||
|             if (0 !== $days && bccomp($left, '0') > -1) { | ||||
|   | ||||
| @@ -52,10 +52,8 @@ class NetWorthController extends Controller | ||||
|                 $this->repository = app(AccountRepositoryInterface::class); | ||||
|                 // new way of user group validation
 | ||||
|                 $userGroup        = $this->validateUserGroup($request); | ||||
|                 if (null !== $userGroup) { | ||||
|                     $this->netWorth->setUserGroup($userGroup); | ||||
|                     $this->repository->setUserGroup($userGroup); | ||||
|                 } | ||||
|                 $this->netWorth->setUserGroup($userGroup); | ||||
|                 $this->repository->setUserGroup($userGroup); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|   | ||||
							
								
								
									
										73
									
								
								app/Api/V2/Controllers/UserGroup/IndexController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								app/Api/V2/Controllers/UserGroup/IndexController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| <?php | ||||
| /* | ||||
|  * IndexController.php | ||||
|  * Copyright (c) 2024 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\Api\V2\Controllers\UserGroup; | ||||
| 
 | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Api\V2\Request\Model\Account\IndexRequest; | ||||
| use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface; | ||||
| use FireflyIII\Transformers\V2\UserGroupTransformer; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Pagination\LengthAwarePaginator; | ||||
| 
 | ||||
| class IndexController extends Controller | ||||
| { | ||||
|     public const string RESOURCE_KEY = 'user_groups'; | ||||
| 
 | ||||
|     private UserGroupRepositoryInterface $repository; | ||||
| 
 | ||||
|     /** | ||||
|      * AccountController constructor. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(UserGroupRepositoryInterface::class); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * TODO see autocomplete/accountcontroller for list. | ||||
|      */ | ||||
|     public function index(IndexRequest $request): JsonResponse | ||||
|     { | ||||
|         $administrations = $this->repository->get(); | ||||
|         $pageSize        = $this->parameters->get('limit'); | ||||
|         $count           = $administrations->count(); | ||||
|         $administrations = $administrations->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|         $paginator       = new LengthAwarePaginator($administrations, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $transformer     = new UserGroupTransformer(); | ||||
| 
 | ||||
|         $transformer->setParameters($this->parameters); // give params to transformer
 | ||||
| 
 | ||||
|         return response() | ||||
|             ->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer)) | ||||
|             ->header('Content-Type', self::CONTENT_TYPE) | ||||
|         ; | ||||
|     } | ||||
| } | ||||
| @@ -79,4 +79,12 @@ class UpdateController extends Controller | ||||
|             ->header('Content-Type', self::CONTENT_TYPE) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     public function useUserGroup(UserGroup $userGroup): JsonResponse | ||||
|     { | ||||
|         // group validation is already in place, so can just update the user.
 | ||||
|         $this->repository->useUserGroup($userGroup); | ||||
| 
 | ||||
|         return response()->json([], 204); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,6 +24,7 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Request\Chart; | ||||
| 
 | ||||
| use FireflyIII\Enums\UserRoleEnum; | ||||
| use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| @@ -39,6 +40,7 @@ class BalanceChartRequest extends FormRequest | ||||
|     use ChecksLogin; | ||||
|     use ConvertsDataTypes; | ||||
|     use ValidatesUserGroupTrait; | ||||
|     protected array                    $acceptedRoles = [UserRoleEnum::READ_ONLY]; | ||||
| 
 | ||||
|     /** | ||||
|      * Get all data from the request. | ||||
|   | ||||
| @@ -23,6 +23,7 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Request\Chart; | ||||
| 
 | ||||
| use FireflyIII\Enums\UserRoleEnum; | ||||
| use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| @@ -39,6 +40,8 @@ class DashboardChartRequest extends FormRequest | ||||
|     use ConvertsDataTypes; | ||||
|     use ValidatesUserGroupTrait; | ||||
| 
 | ||||
|     protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; | ||||
| 
 | ||||
|     /** | ||||
|      * Get all data from the request. | ||||
|      */ | ||||
|   | ||||
| @@ -27,6 +27,7 @@ use Carbon\Carbon; | ||||
| use FireflyIII\Support\Http\Api\AccountFilter; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\Support\Request\GetFilterInstructions; | ||||
| use FireflyIII\Support\Request\GetSortInstructions; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| 
 | ||||
| @@ -40,18 +41,16 @@ class IndexRequest extends FormRequest | ||||
|     use AccountFilter; | ||||
|     use ChecksLogin; | ||||
|     use ConvertsDataTypes; | ||||
|     use GetFilterInstructions; | ||||
|     use GetSortInstructions; | ||||
| 
 | ||||
|     public function getAccountTypes(): array | ||||
|     { | ||||
|         $type = (string)$this->get('type', 'default'); | ||||
|         $type = (string) $this->get('type', 'default'); | ||||
| 
 | ||||
|         return $this->mapAccountTypes($type); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get all data from the request. | ||||
|      */ | ||||
|     public function getDate(): Carbon | ||||
|     { | ||||
|         return $this->getCarbonDate('date'); | ||||
| @@ -63,7 +62,9 @@ class IndexRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'date' => 'date|after:1900-01-01|before:2099-12-31', | ||||
|             'date'  => 'date|after:1900-01-01|before:2099-12-31', | ||||
|             'start' => 'date|after:1900-01-01|before:2099-12-31|before:end|required_with:end', | ||||
|             'end'   => 'date|after:1900-01-01|before:2099-12-31|after:start|required_with:start', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										116
									
								
								app/Api/V2/Request/Model/Account/UpdateRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								app/Api/V2/Request/Model/Account/UpdateRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| <?php | ||||
| /* | ||||
|  * UpdateRequest.php | ||||
|  * Copyright (c) 2024 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\Api\V2\Request\Model\Account; | ||||
| 
 | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Location; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use FireflyIII\Rules\UniqueAccountNumber; | ||||
| use FireflyIII\Rules\UniqueIban; | ||||
| use FireflyIII\Support\Request\AppendsLocationData; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| 
 | ||||
| class UpdateRequest extends FormRequest | ||||
| { | ||||
|     use AppendsLocationData; | ||||
|     use ChecksLogin; | ||||
|     use ConvertsDataTypes; | ||||
| 
 | ||||
|     /** | ||||
|      * TODO is a duplicate of the v1 update thing. | ||||
|      */ | ||||
|     public function getUpdateData(): array | ||||
|     { | ||||
|         $fields = [ | ||||
|             'name'                    => ['name', 'convertString'], | ||||
|             'active'                  => ['active', 'boolean'], | ||||
|             'include_net_worth'       => ['include_net_worth', 'boolean'], | ||||
|             'account_type_name'       => ['type', 'convertString'], | ||||
|             'virtual_balance'         => ['virtual_balance', 'convertString'], | ||||
|             'iban'                    => ['iban', 'convertString'], | ||||
|             'BIC'                     => ['bic', 'convertString'], | ||||
|             'account_number'          => ['account_number', 'convertString'], | ||||
|             'account_role'            => ['account_role', 'convertString'], | ||||
|             'liability_type'          => ['liability_type', 'convertString'], | ||||
|             'opening_balance'         => ['opening_balance', 'convertString'], | ||||
|             'opening_balance_date'    => ['opening_balance_date', 'convertDateTime'], | ||||
|             'cc_type'                 => ['credit_card_type', 'convertString'], | ||||
|             'cc_monthly_payment_date' => ['monthly_payment_date', 'convertDateTime'], | ||||
|             'notes'                   => ['notes', 'stringWithNewlines'], | ||||
|             'interest'                => ['interest', 'convertString'], | ||||
|             'interest_period'         => ['interest_period', 'convertString'], | ||||
|             'order'                   => ['order', 'convertInteger'], | ||||
|             'currency_id'             => ['currency_id', 'convertInteger'], | ||||
|             'currency_code'           => ['currency_code', 'convertString'], | ||||
|             'liability_direction'     => ['liability_direction', 'convertString'], | ||||
|             'liability_amount'        => ['liability_amount', 'convertString'], | ||||
|             'liability_start_date'    => ['liability_start_date', 'date'], | ||||
|         ]; | ||||
|         $data   = $this->getAllData($fields); | ||||
| 
 | ||||
|         return $this->appendLocationData($data, null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * TODO is a duplicate of the v1 UpdateRequest method. | ||||
|      * | ||||
|      * The rules that the incoming request must be matched against. | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         /** @var Account $account */ | ||||
|         $account        = $this->route()->parameter('account'); | ||||
|         $accountRoles   = implode(',', config('firefly.accountRoles')); | ||||
|         $types          = implode(',', array_keys(config('firefly.subTitlesByIdentifier'))); | ||||
|         $ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes'))); | ||||
| 
 | ||||
|         $rules          = [ | ||||
|             'name'                 => sprintf('min:1|max:1024|uniqueAccountForUser:%d', $account->id), | ||||
|             'type'                 => sprintf('in:%s', $types), | ||||
|             'iban'                 => ['iban', 'nullable', new UniqueIban($account, $this->convertString('type'))], | ||||
|             'bic'                  => 'bic|nullable', | ||||
|             'account_number'       => ['min:1', 'max:255', 'nullable', new UniqueAccountNumber($account, $this->convertString('type'))], | ||||
|             'opening_balance'      => 'numeric|required_with:opening_balance_date|nullable', | ||||
|             'opening_balance_date' => 'date|required_with:opening_balance|nullable', | ||||
|             'virtual_balance'      => 'numeric|nullable', | ||||
|             'order'                => 'numeric|nullable', | ||||
|             'currency_id'          => 'numeric|exists:transaction_currencies,id', | ||||
|             'currency_code'        => 'min:3|max:51|exists:transaction_currencies,code', | ||||
|             'active'               => [new IsBoolean()], | ||||
|             'include_net_worth'    => [new IsBoolean()], | ||||
|             'account_role'         => sprintf('in:%s|nullable|required_if:type,asset', $accountRoles), | ||||
|             'credit_card_type'     => sprintf('in:%s|nullable|required_if:account_role,ccAsset', $ccPaymentTypes), | ||||
|             'monthly_payment_date' => 'date|nullable|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull', | ||||
|             'liability_type'       => 'required_if:type,liability|in:loan,debt,mortgage', | ||||
|             'liability_direction'  => 'required_if:type,liability|in:credit,debit', | ||||
|             'interest'             => 'required_if:type,liability|min:0|max:100|numeric', | ||||
|             'interest_period'      => 'required_if:type,liability|in:daily,monthly,yearly', | ||||
|             'notes'                => 'min:0|max:32768', | ||||
|         ]; | ||||
| 
 | ||||
|         return Location::requestRules($rules); | ||||
|     } | ||||
| } | ||||
| @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V2\Request\UserGroup; | ||||
| 
 | ||||
| use FireflyIII\Enums\UserRoleEnum; | ||||
| use FireflyIII\Models\UserGroup; | ||||
| use FireflyIII\Rules\IsDefaultUserGroupName; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| @@ -53,7 +54,7 @@ class UpdateRequest extends FormRequest | ||||
|         $userGroup = $this->route()->parameter('userGroup'); | ||||
| 
 | ||||
|         return [ | ||||
|             'title' => sprintf('required|min:1|max:255|unique:user_groups,title,%d', $userGroup->id), | ||||
|             'title' => ['required', 'min:1', 'max:255', sprintf('unique:user_groups,title,%d', $userGroup->id), new IsDefaultUserGroupName($userGroup)], | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -73,6 +73,7 @@ class CorrectDatabase extends Command | ||||
|             // new!
 | ||||
|             'firefly-iii:unify-group-accounts', | ||||
|             'firefly-iii:trigger-credit-recalculation', | ||||
|             'firefly-iii:migrate-preferences', | ||||
|         ]; | ||||
|         foreach ($commands as $command) { | ||||
|             $this->friendlyLine(sprintf('Now executing command "%s"', $command)); | ||||
|   | ||||
| @@ -50,7 +50,7 @@ class FixFrontpageAccounts extends Command | ||||
| 
 | ||||
|         /** @var User $user */ | ||||
|         foreach ($users as $user) { | ||||
|             $preference = app('preferences')->getForUser($user, 'frontPageAccounts'); | ||||
|             $preference = app('preferences')->getForUser($user, 'frontpageAccounts'); | ||||
|             if (null !== $preference) { | ||||
|                 $this->fixPreference($preference); | ||||
|             } | ||||
| @@ -83,6 +83,6 @@ class FixFrontpageAccounts extends Command | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         app('preferences')->setForUser($preference->user, 'frontPageAccounts', $fixed); | ||||
|         app('preferences')->setForUser($preference->user, 'frontpageAccounts', $fixed); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										66
									
								
								app/Console/Commands/Correction/MigratePreferences.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								app/Console/Commands/Correction/MigratePreferences.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| /* | ||||
|  * MigratePreferences.php | ||||
|  * Copyright (c) 2024 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\Console\Commands\Correction; | ||||
| 
 | ||||
| use FireflyIII\Models\Preference; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Console\Command; | ||||
| use Symfony\Component\Console\Command\Command as CommandAlias; | ||||
| 
 | ||||
| class MigratePreferences extends Command | ||||
| { | ||||
|     protected $description = 'Give Firefly III preferences a user group ID so they can be made administration specific.'; | ||||
| 
 | ||||
|     protected $signature   = 'firefly-iii:migrate-preferences'; | ||||
| 
 | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|         $items = config('firefly.admin_specific_prefs'); | ||||
|         $users = User::get(); | ||||
| 
 | ||||
|         /** @var User $user */ | ||||
|         foreach ($users as $user) { | ||||
|             $count = 0; | ||||
|             foreach ($items as $item) { | ||||
|                 $preference = Preference::where('name', $item)->where('user_id', $user->id)->first(); | ||||
|                 if (null === $preference) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (null !== $preference->user_group_id) { | ||||
|                     $preference->user_group_id = $user->user_group_id; | ||||
|                     $preference->save(); | ||||
|                     ++$count; | ||||
|                 } | ||||
|             } | ||||
|             if ($count > 0) { | ||||
|                 $this->info(sprintf('Migrated %d preference(s) for user #%d ("%s").', $count, $user->id, $user->email)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return CommandAlias::SUCCESS; | ||||
|     } | ||||
| } | ||||
| @@ -191,7 +191,7 @@ class ExportData extends Command | ||||
|                 $this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start'))); | ||||
|                 $error = true; | ||||
|             } | ||||
|             if (false === $date) { | ||||
|             if (null === $date) { | ||||
|                 $this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD.', $field, $this->option('start'))); | ||||
| 
 | ||||
|                 throw new FireflyException(sprintf('%s date "%s" must be formatted YYYY-MM-DD.', $field, $this->option('start'))); | ||||
|   | ||||
| @@ -53,40 +53,40 @@ class ForceDecimalSize extends Command | ||||
| { | ||||
|     use ShowsFriendlyMessages; | ||||
| 
 | ||||
|     protected $description      = 'This command resizes DECIMAL columns in MySQL or PostgreSQL and correct amounts (only MySQL).'; | ||||
|     protected $signature        = 'firefly-iii:force-decimal-size'; | ||||
|     protected $description = 'This command resizes DECIMAL columns in MySQL or PostgreSQL and correct amounts (only MySQL).'; | ||||
|     protected $signature   = 'firefly-iii:force-decimal-size'; | ||||
|     private string $cast; | ||||
|     private array  $classes | ||||
|                                 = [ | ||||
|                                     'accounts'                 => Account::class, | ||||
|                                     'auto_budgets'             => AutoBudget::class, | ||||
|                                     'available_budgets'        => AvailableBudget::class, | ||||
|                                     'bills'                    => Bill::class, | ||||
|                                     'budget_limits'            => BudgetLimit::class, | ||||
|                                     'piggy_bank_events'        => PiggyBankEvent::class, | ||||
|                                     'piggy_bank_repetitions'   => PiggyBankRepetition::class, | ||||
|                                     'piggy_banks'              => PiggyBank::class, | ||||
|                                     'recurrences_transactions' => RecurrenceTransaction::class, | ||||
|                                     'transactions'             => Transaction::class, | ||||
|                                 ]; | ||||
|                            = [ | ||||
|             'accounts'                 => Account::class, | ||||
|             'auto_budgets'             => AutoBudget::class, | ||||
|             'available_budgets'        => AvailableBudget::class, | ||||
|             'bills'                    => Bill::class, | ||||
|             'budget_limits'            => BudgetLimit::class, | ||||
|             'piggy_bank_events'        => PiggyBankEvent::class, | ||||
|             'piggy_bank_repetitions'   => PiggyBankRepetition::class, | ||||
|             'piggy_banks'              => PiggyBank::class, | ||||
|             'recurrences_transactions' => RecurrenceTransaction::class, | ||||
|             'transactions'             => Transaction::class, | ||||
|         ]; | ||||
| 
 | ||||
|     private string $operator; | ||||
|     private string $regularExpression; | ||||
|     private array  $tables | ||||
|                                 = [ | ||||
|                                     'accounts'                 => ['virtual_balance'], | ||||
|                                     'auto_budgets'             => ['amount'], | ||||
|                                     'available_budgets'        => ['amount'], | ||||
|                                     'bills'                    => ['amount_min', 'amount_max'], | ||||
|                                     'budget_limits'            => ['amount'], | ||||
|                                     'currency_exchange_rates'  => ['rate', 'user_rate'], | ||||
|                                     'limit_repetitions'        => ['amount'], | ||||
|                                     'piggy_bank_events'        => ['amount'], | ||||
|                                     'piggy_bank_repetitions'   => ['currentamount'], | ||||
|                                     'piggy_banks'              => ['targetamount'], | ||||
|                                     'recurrences_transactions' => ['amount', 'foreign_amount'], | ||||
|                                     'transactions'             => ['amount', 'foreign_amount'], | ||||
|                                 ]; | ||||
|                            = [ | ||||
|             'accounts'                 => ['virtual_balance'], | ||||
|             'auto_budgets'             => ['amount'], | ||||
|             'available_budgets'        => ['amount'], | ||||
|             'bills'                    => ['amount_min', 'amount_max'], | ||||
|             'budget_limits'            => ['amount'], | ||||
|             'currency_exchange_rates'  => ['rate', 'user_rate'], | ||||
|             'limit_repetitions'        => ['amount'], | ||||
|             'piggy_bank_events'        => ['amount'], | ||||
|             'piggy_bank_repetitions'   => ['currentamount'], | ||||
|             'piggy_banks'              => ['targetamount'], | ||||
|             'recurrences_transactions' => ['amount', 'foreign_amount'], | ||||
|             'transactions'             => ['amount', 'foreign_amount'], | ||||
|         ]; | ||||
| 
 | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|   | ||||
							
								
								
									
										54
									
								
								app/Console/Commands/System/LaravelPassportKeys.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								app/Console/Commands/System/LaravelPassportKeys.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| /* | ||||
|  * LaravelPassportKeys.php | ||||
|  * Copyright (c) 2024 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\Console\Commands\System; | ||||
| 
 | ||||
| use FireflyIII\Console\Commands\ShowsFriendlyMessages; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Support\Facades\Artisan; | ||||
| use Symfony\Component\Console\Command\Command as CommandAlias; | ||||
| 
 | ||||
| class LaravelPassportKeys extends Command | ||||
| { | ||||
|     use ShowsFriendlyMessages; | ||||
| 
 | ||||
|     protected $description = 'Calls the Laravel "passport:keys" but doesn\'t exit 1.'; | ||||
|     protected $signature   = 'firefly-iii:laravel-passport-keys'; | ||||
| 
 | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|         Artisan::call('passport:keys --no-interaction', []); | ||||
|         $result = Artisan::output(); | ||||
|         if (str_contains($result, 'Encryption keys already exist')) { | ||||
|             $this->friendlyInfo('Encryption keys exist already.'); | ||||
| 
 | ||||
|             return CommandAlias::SUCCESS; | ||||
|         } | ||||
|         $this->friendlyPositive('Encryption keys have been created, nice!'); | ||||
| 
 | ||||
|         return CommandAlias::SUCCESS; | ||||
|     } | ||||
| } | ||||
| @@ -1,7 +1,7 @@ | ||||
| <?php | ||||
| /** | ||||
| /* | ||||
|  * ApplyRules.php | ||||
|  * Copyright (c) 2020 james@firefly-iii.org | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
| @@ -16,7 +16,7 @@ | ||||
|  * 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/>. | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| @@ -285,7 +285,7 @@ class ApplyRules extends Command | ||||
|         if (null !== $endString && '' !== $endString) { | ||||
|             $inputEnd = Carbon::createFromFormat('Y-m-d', $endString); | ||||
|         } | ||||
|         if (false === $inputEnd || false === $inputStart) { | ||||
|         if (null === $inputEnd || null === $inputStart) { | ||||
|             Log::error('Could not parse start or end date in verifyInputDate().'); | ||||
| 
 | ||||
|             return; | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <?php | ||||
| 
 | ||||
| /** | ||||
| /* | ||||
|  * Cron.php | ||||
|  * Copyright (c) 2020 james@firefly-iii.org | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
| @@ -17,7 +17,7 @@ | ||||
|  * 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/>. | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
|   | ||||
| @@ -110,7 +110,7 @@ class AppendBudgetLimitPeriods extends Command | ||||
|             return 'daily'; | ||||
|         } | ||||
|         // is weekly
 | ||||
|         if ('1' === $limit->start_date->format('N') && '7' === $limit->end_date->format('N') && 6 === $limit->end_date->diffInDays($limit->start_date)) { | ||||
|         if ('1' === $limit->start_date->format('N') && '7' === $limit->end_date->format('N') && 6 === (int)$limit->end_date->diffInDays($limit->start_date, true)) { | ||||
|             return 'weekly'; | ||||
|         } | ||||
| 
 | ||||
| @@ -129,7 +129,7 @@ class AppendBudgetLimitPeriods extends Command | ||||
|         if ( | ||||
|             in_array($limit->start_date->format('j-n'), $start, true) // start of quarter
 | ||||
|             && in_array($limit->end_date->format('j-n'), $end, true) // end of quarter
 | ||||
|             && 2 === $limit->start_date->diffInMonths($limit->end_date) | ||||
|             && 2 === (int)$limit->start_date->diffInMonths($limit->end_date, true) | ||||
|         ) { | ||||
|             return 'quarterly'; | ||||
|         } | ||||
| @@ -139,7 +139,7 @@ class AppendBudgetLimitPeriods extends Command | ||||
|         if ( | ||||
|             in_array($limit->start_date->format('j-n'), $start, true) // start of quarter
 | ||||
|             && in_array($limit->end_date->format('j-n'), $end, true) // end of quarter
 | ||||
|             && 5 === $limit->start_date->diffInMonths($limit->end_date) | ||||
|             && 5 === (int)$limit->start_date->diffInMonths($limit->end_date, true) | ||||
|         ) { | ||||
|             return 'half_year'; | ||||
|         } | ||||
|   | ||||
							
								
								
									
										182
									
								
								app/Console/Commands/Upgrade/MigrateRuleActions.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								app/Console/Commands/Upgrade/MigrateRuleActions.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,182 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| /* | ||||
|  * MigrateRuleActions.php | ||||
|  * Copyright (c) 2024 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\Console\Commands\Upgrade; | ||||
| 
 | ||||
| use FireflyIII\Console\Commands\ShowsFriendlyMessages; | ||||
| use FireflyIII\Models\RuleAction; | ||||
| use Illuminate\Console\Command; | ||||
| 
 | ||||
| class MigrateRuleActions extends Command | ||||
| { | ||||
|     use ShowsFriendlyMessages; | ||||
| 
 | ||||
|     public const string CONFIG_NAME = '610_migrate_rule_actions'; | ||||
| 
 | ||||
|     protected $description          = 'Migrate rule actions away from expression engine'; | ||||
| 
 | ||||
|     protected $signature            = 'firefly-iii:migrate-rule-actions {--F|force : Force the execution of this command.}'; | ||||
| 
 | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|         if ($this->isExecuted() && true !== $this->option('force')) { | ||||
|             $this->friendlyInfo('This command has already been executed.'); | ||||
| 
 | ||||
|             return 0; | ||||
|         } | ||||
|         if (false === config('firefly.feature_flags.expression_engine')) { | ||||
|             $this->friendlyInfo('Expression engine is not enabled. Nothing to do.'); | ||||
| 
 | ||||
|             return 0; | ||||
|         } | ||||
|         $this->replaceEqualSign(); | ||||
|         $this->replaceObsoleteActions(); | ||||
| 
 | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     private function isExecuted(): bool | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     private function replaceEqualSign(): void | ||||
|     { | ||||
|         $count   = 0; | ||||
|         $actions = RuleAction::get(); | ||||
| 
 | ||||
|         /** @var RuleAction $action */ | ||||
|         foreach ($actions as $action) { | ||||
|             if (str_starts_with($action->action_value, '=')) { | ||||
|                 $action->action_value = sprintf('%s%s', '\=', substr($action->action_value, 1)); | ||||
|                 $action->save(); | ||||
|                 ++$count; | ||||
|             } | ||||
|         } | ||||
|         if ($count > 0) { | ||||
|             $this->friendlyInfo(sprintf('Upgrading %d rule action(s) for the new expression engine.', $count)); | ||||
|         } | ||||
|         if (0 === $count) { | ||||
|             $this->friendlyInfo('All rule actions are up to date.'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function replaceObsoleteActions(): void | ||||
|     { | ||||
|         $obsolete = [ | ||||
|             'append_description', | ||||
|             'prepend_description', | ||||
|             'append_notes', | ||||
|             'prepend_notes', | ||||
|             'append_descr_to_notes', | ||||
|             'append_notes_to_descr', | ||||
|             'move_descr_to_notes', | ||||
|             'move_notes_to_descr', | ||||
|         ]; | ||||
|         $actions  = RuleAction::whereIn('action_type', $obsolete)->get(); | ||||
| 
 | ||||
|         /** @var RuleAction $action */ | ||||
|         foreach ($actions as $action) { | ||||
|             $oldType = $action->action_type; | ||||
| 
 | ||||
|             switch ($action->action_type) { | ||||
|                 default: | ||||
|                     $this->friendlyError(sprintf('Cannot deal with action type "%s", skip it.', $action->action_type)); | ||||
| 
 | ||||
|                     break; | ||||
| 
 | ||||
|                 case 'append_description': | ||||
|                     $action->action_type  = 'set_description'; | ||||
|                     $action->action_value = sprintf('=description~"%s"', str_replace('"', '\"', $action->action_value)); | ||||
|                     $action->save(); | ||||
|                     $this->friendlyInfo(sprintf('Upgraded action #%d from "%s" to "%s".', $action->id, $oldType, $action->action_type)); | ||||
| 
 | ||||
|                     break; | ||||
| 
 | ||||
|                 case 'prepend_description': | ||||
|                     $action->action_type  = 'set_description'; | ||||
|                     $action->action_value = sprintf('="%s"~description', str_replace('"', '\"', $action->action_value)); | ||||
|                     $action->save(); | ||||
|                     $this->friendlyInfo(sprintf('Upgraded action #%d from "%s" to "%s".', $action->id, $oldType, $action->action_type)); | ||||
| 
 | ||||
|                     break; | ||||
| 
 | ||||
|                 case 'append_notes': | ||||
|                     $action->action_type  = 'set_notes'; | ||||
|                     $action->action_value = sprintf('=notes~"%s"', str_replace('"', '\"', $action->action_value)); | ||||
|                     $action->save(); | ||||
|                     $this->friendlyInfo(sprintf('Upgraded action #%d from "%s" to "%s".', $action->id, $oldType, $action->action_type)); | ||||
| 
 | ||||
|                     break; | ||||
| 
 | ||||
|                 case 'prepend_notes': | ||||
|                     $action->action_type  = 'set_notes'; | ||||
|                     $action->action_value = sprintf('="%s"~notes', str_replace('"', '\"', $action->action_value)); | ||||
|                     $action->save(); | ||||
|                     $this->friendlyInfo(sprintf('Upgraded action #%d from "%s" to "%s".', $action->id, $oldType, $action->action_type)); | ||||
| 
 | ||||
|                     break; | ||||
| 
 | ||||
|                 case 'append_descr_to_notes': | ||||
|                     $action->action_type  = 'set_notes'; | ||||
|                     $action->action_value = '=notes~" "~description'; | ||||
|                     $action->save(); | ||||
|                     $this->friendlyInfo(sprintf('Upgraded action #%d from "%s" to "%s".', $action->id, $oldType, $action->action_type)); | ||||
| 
 | ||||
|                     break; | ||||
| 
 | ||||
|                 case 'append_notes_to_descr': | ||||
|                     $action->action_type  = 'set_description'; | ||||
|                     $action->action_value = '=description~" "~notes'; | ||||
|                     $action->save(); | ||||
|                     $this->friendlyInfo(sprintf('Upgraded action #%d from "%s" to "%s".', $action->id, $oldType, $action->action_type)); | ||||
| 
 | ||||
|                     break; | ||||
| 
 | ||||
|                 case 'move_descr_to_notes': | ||||
|                     $action->action_type  = 'set_notes'; | ||||
|                     $action->action_value = '=description'; | ||||
|                     $action->save(); | ||||
|                     $this->friendlyInfo(sprintf('Upgraded action #%d from "%s" to "%s".', $action->id, $oldType, $action->action_type)); | ||||
| 
 | ||||
|                     break; | ||||
| 
 | ||||
|                 case 'move_notes_to_descr': | ||||
|                     $action->action_type  = 'set_description'; | ||||
|                     $action->action_value = '=notes'; | ||||
|                     $action->save(); | ||||
|                     $this->friendlyInfo(sprintf('Upgraded action #%d from "%s" to "%s".', $action->id, $oldType, $action->action_type)); | ||||
| 
 | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -63,11 +63,13 @@ class UpgradeDatabase extends Command | ||||
|             'firefly-iii:upgrade-liabilities', | ||||
|             'firefly-iii:liabilities-600', | ||||
|             'firefly-iii:budget-limit-periods', | ||||
|             'firefly-iii:migrate-rule-actions', | ||||
|             'firefly-iii:restore-oauth-keys', | ||||
|             // also just in case, some integrity commands:
 | ||||
|             'firefly-iii:create-group-memberships', | ||||
|             'firefly-iii:upgrade-group-information', | ||||
|             'firefly-iii:upgrade-currency-preferences', | ||||
|             'firefly-iii:correct-database', | ||||
|         ]; | ||||
|         $args     = []; | ||||
|         if ($this->option('force')) { | ||||
|   | ||||
| @@ -26,11 +26,11 @@ class UpgradeSkeleton extends Command | ||||
|     { | ||||
|         $start = microtime(true); | ||||
|         if ($this->isExecuted() && true !== $this->option('force')) { | ||||
|             $this->info('FRIENDLY This command has already been executed.'); | ||||
|             $this->friendlyInfo('This command has already been executed.'); | ||||
|  | ||||
|             return 0; | ||||
|         } | ||||
|         $this->warn('Congrats, you found the skeleton command. Boo!'); | ||||
|         $this->friendlyWarning('Congrats, you found the skeleton command. Boo!'); | ||||
|  | ||||
|         //$this->markAsExecuted(); | ||||
|  | ||||
|   | ||||
| @@ -41,7 +41,6 @@ enum UserRoleEnum: string | ||||
|     // manage other financial objects:
 | ||||
|     case MANAGE_BUDGETS       = 'mng_budgets'; | ||||
|     case MANAGE_PIGGY_BANKS   = 'mng_piggies'; | ||||
|     case MANAGE_REPETITIONS   = 'mng_reps'; | ||||
|     case MANAGE_SUBSCRIPTIONS = 'mng_subscriptions'; | ||||
|     case MANAGE_RULES         = 'mng_rules'; | ||||
|     case MANAGE_RECURRING     = 'mng_recurring'; | ||||
| @@ -51,7 +50,7 @@ enum UserRoleEnum: string | ||||
|     // view and generate reports
 | ||||
|     case VIEW_REPORTS         = 'view_reports'; | ||||
| 
 | ||||
|     // view memberships. needs FULL to manage them.
 | ||||
|     // view memberships AND roles. needs FULL to manage them.
 | ||||
|     case VIEW_MEMBERSHIPS     = 'view_memberships'; | ||||
| 
 | ||||
|     // everything the creator can, except remove/change original creator and delete group
 | ||||
|   | ||||
| @@ -25,6 +25,7 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Exceptions; | ||||
| 
 | ||||
| use FireflyIII\Jobs\MailError; | ||||
| use Illuminate\Auth\Access\AuthorizationException; | ||||
| use Illuminate\Auth\AuthenticationException; | ||||
| use Illuminate\Database\QueryException; | ||||
| use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; | ||||
| @@ -98,6 +99,13 @@ class Handler extends ExceptionHandler | ||||
|             return response()->json(['message' => 'Resource not found', 'exception' => 'NotFoundHttpException'], 404); | ||||
|         } | ||||
| 
 | ||||
|         if ($e instanceof AuthorizationException && $expectsJson) { | ||||
|             // somehow Laravel handler does not catch this:
 | ||||
|             app('log')->debug('Return JSON unauthorized error.'); | ||||
| 
 | ||||
|             return response()->json(['message' => $e->getMessage(), 'exception' => 'AuthorizationException'], 401); | ||||
|         } | ||||
| 
 | ||||
|         if ($e instanceof AuthenticationException && $expectsJson) { | ||||
|             // somehow Laravel handler does not catch this:
 | ||||
|             app('log')->debug('Return JSON unauthenticated error.'); | ||||
|   | ||||
| @@ -40,12 +40,12 @@ class ReportGeneratorFactory | ||||
|     { | ||||
|         $period = 'Month'; | ||||
|         // more than two months date difference means year report.
 | ||||
|         if ($start->diffInMonths($end) > 1) { | ||||
|         if ($start->diffInMonths($end, true) > 1) { | ||||
|             $period = 'Year'; | ||||
|         } | ||||
| 
 | ||||
|         // more than one year date difference means multi year report.
 | ||||
|         if ($start->diffInMonths($end) > 12) { | ||||
|         // more than one year date difference means multi-year report.
 | ||||
|         if ($start->diffInMonths($end, true) > 12) { | ||||
|             $period = 'MultiYear'; | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -405,7 +405,7 @@ class UserEventHandler | ||||
|             } | ||||
|             // clean up old entries (6 months)
 | ||||
|             $carbon = Carbon::createFromFormat('Y-m-d H:i:s', $preference[$index]['time']); | ||||
|             if (false !== $carbon && $carbon->diffInMonths(today()) > 6) { | ||||
|             if (null !== $carbon && $carbon->diffInMonths(today(), true) > 6) { | ||||
|                 app('log')->debug(sprintf('Entry for %s is very old, remove it.', $row['ip'])); | ||||
|                 unset($preference[$index]); | ||||
|             } | ||||
|   | ||||
| @@ -236,7 +236,9 @@ class AttachmentHelper implements AttachmentHelperInterface | ||||
|             $fileObject->rewind(); | ||||
| 
 | ||||
|             if (0 === $file->getSize()) { | ||||
|                 throw new FireflyException('Cannot upload empty or non-existent file.'); | ||||
|                 $this->errors->add('attachments', trans('validation.file_zero_length')); | ||||
| 
 | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             $content              = (string)$fileObject->fread($file->getSize()); | ||||
|   | ||||
| @@ -145,13 +145,13 @@ class CreateController extends Controller | ||||
|         Log::channel('audit')->info('Stored new account.', $data); | ||||
| 
 | ||||
|         // update preferences if necessary:
 | ||||
|         $frontPage = app('preferences')->get('frontPageAccounts', [])->data; | ||||
|         if (!is_array($frontPage)) { | ||||
|             $frontPage = []; | ||||
|         $frontpage = app('preferences')->get('frontpageAccounts', [])->data; | ||||
|         if (!is_array($frontpage)) { | ||||
|             $frontpage = []; | ||||
|         } | ||||
|         if (AccountType::ASSET === $account->accountType->type) { | ||||
|             $frontPage[] = $account->id; | ||||
|             app('preferences')->set('frontPageAccounts', $frontPage); | ||||
|             $frontpage[] = $account->id; | ||||
|             app('preferences')->set('frontpageAccounts', $frontpage); | ||||
|         } | ||||
| 
 | ||||
|         // store attachment(s):
 | ||||
|   | ||||
| @@ -75,7 +75,7 @@ class ReconcileController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      *                                              */ | ||||
|     public function reconcile(Account $account, Carbon $start = null, Carbon $end = null) | ||||
|     public function reconcile(Account $account, ?Carbon $start = null, ?Carbon $end = null) | ||||
|     { | ||||
|         if (!$this->isEditableAccount($account)) { | ||||
|             return $this->redirectAccountToAccount($account); | ||||
|   | ||||
| @@ -75,7 +75,7 @@ class ShowController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      *                                              */ | ||||
|     public function show(Request $request, Account $account, Carbon $start = null, Carbon $end = null) | ||||
|     public function show(Request $request, Account $account, ?Carbon $start = null, ?Carbon $end = null) | ||||
|     { | ||||
|         $objectType       = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type)); | ||||
| 
 | ||||
| @@ -106,7 +106,7 @@ class ShowController extends Controller | ||||
|         $periods          = $this->getAccountPeriodOverview($account, $firstTransaction, $end); | ||||
| 
 | ||||
|         // if layout = v2, overrule the page title.
 | ||||
|         if ('v1' !== config('firefly.layout')) { | ||||
|         if ('v1' !== config('view.layout')) { | ||||
|             $subTitle = (string)trans('firefly.all_journals_for_account', ['name' => $account->name]); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -31,6 +31,7 @@ use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Foundation\Auth\SendsPasswordResetEmails; | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\View\View; | ||||
| 
 | ||||
| /** | ||||
| @@ -106,6 +107,8 @@ class ForgotPasswordController extends Controller | ||||
|         } | ||||
|         $host           = request()->host(); | ||||
|         if ($configuredHost !== $host) { | ||||
|             Log::error(sprintf('Host header is "%s", APP_URL is "%s".', $host, $configuredHost)); | ||||
| 
 | ||||
|             throw new FireflyException('The Host-header does not match the host in the APP_URL environment variable. Please make sure these match. See also: https://bit.ly/FF3-host-header'); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -128,7 +128,7 @@ class BudgetLimitController extends Controller | ||||
|         $start    = Carbon::createFromFormat('Y-m-d', $request->get('start')); | ||||
|         $end      = Carbon::createFromFormat('Y-m-d', $request->get('end')); | ||||
| 
 | ||||
|         if (false === $start || false === $end) { | ||||
|         if (null === $start || null === $end) { | ||||
|             return response()->json([]); | ||||
|         } | ||||
| 
 | ||||
| @@ -235,12 +235,12 @@ class BudgetLimitController extends Controller | ||||
|             new Collection([$budgetLimit->budget]), | ||||
|             $budgetLimit->transactionCurrency | ||||
|         ); | ||||
|         $daysLeft                        = $this->activeDaysLeft($limit->start_date, $limit->end_date); | ||||
|         $array['spent']                  = $spentArr[$budgetLimit->transactionCurrency->id]['sum'] ?? '0'; | ||||
|         $array['left_formatted']         = app('amount')->formatAnything($limit->transactionCurrency, bcadd($array['spent'], $array['amount'])); | ||||
|         $array['amount_formatted']       = app('amount')->formatAnything($limit->transactionCurrency, $limit['amount']); | ||||
|         $array['days_left']              = (string)$this->activeDaysLeft($limit->start_date, $limit->end_date); | ||||
|         // left per day:
 | ||||
|         $array['left_per_day']           = bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']); | ||||
|         $array['days_left']              = (string)$daysLeft; | ||||
|         $array['left_per_day']           = 0 === $daysLeft ? bcadd($array['spent'], $array['amount']) : bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']); | ||||
| 
 | ||||
|         // left per day formatted.
 | ||||
|         $array['amount']                 = app('steam')->bcround($limit['amount'], $limit->transactionCurrency->decimal_places); | ||||
|   | ||||
| @@ -85,7 +85,7 @@ class IndexController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      *                                              */ | ||||
|     public function index(Carbon $start = null, Carbon $end = null) | ||||
|     public function index(?Carbon $start = null, ?Carbon $end = null) | ||||
|     { | ||||
|         $this->abRepository->cleanup(); | ||||
|         app('log')->debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d'))); | ||||
|   | ||||
| @@ -75,7 +75,7 @@ class ShowController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function noBudget(Request $request, Carbon $start = null, Carbon $end = null) | ||||
|     public function noBudget(Request $request, ?Carbon $start = null, ?Carbon $end = null) | ||||
|     { | ||||
|         // @var Carbon $start
 | ||||
|         $start ??= session('start'); | ||||
|   | ||||
| @@ -70,7 +70,7 @@ class NoCategoryController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function show(Request $request, Carbon $start = null, Carbon $end = null) | ||||
|     public function show(Request $request, ?Carbon $start = null, ?Carbon $end = null) | ||||
|     { | ||||
|         app('log')->debug('Start of noCategory()'); | ||||
|         // @var Carbon $start
 | ||||
|   | ||||
| @@ -71,7 +71,7 @@ class ShowController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function show(Request $request, Category $category, Carbon $start = null, Carbon $end = null) | ||||
|     public function show(Request $request, Category $category, ?Carbon $start = null, ?Carbon $end = null) | ||||
|     { | ||||
|         // @var Carbon $start
 | ||||
|         $start ??= session('start', today(config('app.timezone'))->startOfMonth()); | ||||
|   | ||||
| @@ -301,14 +301,14 @@ class AccountController extends Controller | ||||
|         $end            = clone session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $defaultSet     = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray(); | ||||
|         app('log')->debug('Default set is ', $defaultSet); | ||||
|         $frontPage      = app('preferences')->get('frontPageAccounts', $defaultSet); | ||||
|         $frontPageArray = !is_array($frontPage->data) ? [] : $frontPage->data; | ||||
|         app('log')->debug('Frontpage preference set is ', $frontPageArray); | ||||
|         if (0 === count($frontPageArray)) { | ||||
|             app('preferences')->set('frontPageAccounts', $defaultSet); | ||||
|         $frontpage      = app('preferences')->get('frontpageAccounts', $defaultSet); | ||||
|         $frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data; | ||||
|         app('log')->debug('Frontpage preference set is ', $frontpageArray); | ||||
|         if (0 === count($frontpageArray)) { | ||||
|             app('preferences')->set('frontpageAccounts', $defaultSet); | ||||
|             app('log')->debug('frontpage set is empty!'); | ||||
|         } | ||||
|         $accounts       = $repository->getAccountsById($frontPageArray); | ||||
|         $accounts       = $repository->getAccountsById($frontpageArray); | ||||
| 
 | ||||
|         return response()->json($this->accountBalanceChart($accounts, $start, $end)); | ||||
|     } | ||||
|   | ||||
| @@ -116,8 +116,8 @@ class CategoryController extends Controller | ||||
|             return response()->json($cache->get()); | ||||
|         } | ||||
| 
 | ||||
|         $frontPageGenerator = new FrontpageChartGenerator($start, $end); | ||||
|         $chartData          = $frontPageGenerator->generate(); | ||||
|         $frontpageGenerator = new FrontpageChartGenerator($start, $end); | ||||
|         $chartData          = $frontpageGenerator->generate(); | ||||
|         $data               = $this->generator->multiSet($chartData); | ||||
|         $cache->store($data); | ||||
| 
 | ||||
|   | ||||
| @@ -143,7 +143,7 @@ class ReportController extends Controller | ||||
|         $cache->addProperty($accounts); | ||||
|         $cache->addProperty($end); | ||||
|         if ($cache->has()) { | ||||
|             return response()->json($cache->get()); | ||||
|             // return response()->json($cache->get());
 | ||||
|         } | ||||
| 
 | ||||
|         app('log')->debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray()); | ||||
| @@ -220,11 +220,14 @@ class ReportController extends Controller | ||||
|                 $currentEnd = app('navigation')->endOfPeriod($currentEnd, $preferredRange); | ||||
|             } | ||||
|             while ($currentStart <= $currentEnd) { | ||||
|                 $key                        = $currentStart->format($format); | ||||
|                 $title                      = $currentStart->isoFormat($titleFormat); | ||||
|                 $income['entries'][$title]  = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); | ||||
|                 $expense['entries'][$title] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); | ||||
|                 $currentStart               = app('navigation')->addPeriod($currentStart, $preferredRange, 0); | ||||
|                 $key          = $currentStart->format($format); | ||||
|                 $title        = $currentStart->isoFormat($titleFormat); | ||||
|                 // #8663 make sure the period exists in the data previously collected.
 | ||||
|                 if (array_key_exists($key, $currency)) { | ||||
|                     $income['entries'][$title]  = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); | ||||
|                     $expense['entries'][$title] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); | ||||
|                 } | ||||
|                 $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0); | ||||
|             } | ||||
| 
 | ||||
|             $chartData[]  = $income; | ||||
|   | ||||
| @@ -70,7 +70,7 @@ abstract class Controller extends BaseController | ||||
|         $logoutUrl        = config('firefly.custom_logout_url'); | ||||
| 
 | ||||
|         // overrule v2 layout back to v1.
 | ||||
|         if ('true' === request()->get('force_default_layout') && 'v2' === config('firefly.layout')) { | ||||
|         if ('true' === request()->get('force_default_layout') && 'v2' === config('view.layout')) { | ||||
|             app('view')->getFinder()->setPaths([realpath(base_path('resources/views'))]); // @phpstan-ignore-line
 | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -79,17 +79,17 @@ class HomeController extends Controller | ||||
|             app('log')->error(sprintf('End could not parse date string "%s" so ignore it.', $stringEnd)); | ||||
|             $end = Carbon::now()->endOfMonth(); | ||||
|         } | ||||
|         if (false === $start) { | ||||
|         if (null === $start) { | ||||
|             $start = Carbon::now()->startOfMonth(); | ||||
|         } | ||||
|         if (false === $end) { | ||||
|         if (null === $end) { | ||||
|             $end = Carbon::now()->endOfMonth(); | ||||
|         } | ||||
| 
 | ||||
|         $label         = $request->get('label'); | ||||
|         $isCustomRange = false; | ||||
| 
 | ||||
|         app('log')->debug('Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]); | ||||
|         app('log')->debug('dateRange: Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]); | ||||
|         // check if the label is "everything" or "Custom range" which will betray
 | ||||
|         // a possible problem with the budgets.
 | ||||
|         if ($label === (string)trans('firefly.everything') || $label === (string)trans('firefly.customRange')) { | ||||
| @@ -97,10 +97,10 @@ class HomeController extends Controller | ||||
|             app('log')->debug('Range is now marked as "custom".'); | ||||
|         } | ||||
| 
 | ||||
|         $diff          = $start->diffInDays($end) + 1; | ||||
|         $diff          = $start->diffInDays($end, true) + 1; | ||||
| 
 | ||||
|         if ($diff > 50) { | ||||
|             $request->session()->flash('warning', (string)trans('firefly.warning_much_data', ['days' => $diff])); | ||||
|         if ($diff > 366) { | ||||
|             $request->session()->flash('warning', (string)trans('firefly.warning_much_data', ['days' => (int)$diff])); | ||||
|         } | ||||
| 
 | ||||
|         $request->session()->put('is_custom_range', $isCustomRange); | ||||
| @@ -120,19 +120,34 @@ class HomeController extends Controller | ||||
|      */ | ||||
|     public function index(AccountRepositoryInterface $repository): mixed | ||||
|     { | ||||
|         $types          = config('firefly.accountTypesByIdentifier.asset'); | ||||
|         $count          = $repository->count($types); | ||||
|         $types = config('firefly.accountTypesByIdentifier.asset'); | ||||
|         $count = $repository->count($types); | ||||
|         Log::channel('audit')->info('User visits homepage.'); | ||||
| 
 | ||||
|         if (0 === $count) { | ||||
|             return redirect(route('new-user.index')); | ||||
|         } | ||||
| 
 | ||||
|         if ('v1' === (string)config('view.layout')) { | ||||
|             return $this->indexV1($repository); | ||||
|         } | ||||
|         if ('v2' === (string)config('view.layout')) { | ||||
|             return $this->indexV2(); | ||||
|         } | ||||
| 
 | ||||
|         throw new FireflyException('Invalid layout configuration'); | ||||
|     } | ||||
| 
 | ||||
|     private function indexV1(AccountRepositoryInterface $repository): mixed | ||||
|     { | ||||
|         $types          = config('firefly.accountTypesByIdentifier.asset'); | ||||
|         $count          = $repository->count($types); | ||||
|         $subTitle       = (string)trans('firefly.welcome_back'); | ||||
|         $transactions   = []; | ||||
|         $frontPage      = app('preferences')->getFresh('frontPageAccounts', $repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray()); | ||||
|         $frontPageArray = $frontPage->data; | ||||
|         if (!is_array($frontPageArray)) { | ||||
|             $frontPageArray = []; | ||||
|         $frontpage      = app('preferences')->getFresh('frontpageAccounts', $repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray()); | ||||
|         $frontpageArray = $frontpage->data; | ||||
|         if (!is_array($frontpageArray)) { | ||||
|             $frontpageArray = []; | ||||
|         } | ||||
| 
 | ||||
|         /** @var Carbon $start */ | ||||
| @@ -140,13 +155,11 @@ class HomeController extends Controller | ||||
| 
 | ||||
|         /** @var Carbon $end */ | ||||
|         $end            = session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $accounts       = $repository->getAccountsById($frontPageArray); | ||||
|         $accounts       = $repository->getAccountsById($frontpageArray); | ||||
|         $today          = today(config('app.timezone')); | ||||
|         $accounts       = $accounts->sortBy('order'); // sort frontpage accounts by order
 | ||||
| 
 | ||||
|         // sort frontpage accounts by order
 | ||||
|         $accounts       = $accounts->sortBy('order'); | ||||
| 
 | ||||
|         app('log')->debug('Frontpage accounts are ', $frontPageArray); | ||||
|         app('log')->debug('Frontpage accounts are ', $frontpageArray); | ||||
| 
 | ||||
|         /** @var BillRepositoryInterface $billRepository */ | ||||
|         $billRepository = app(BillRepositoryInterface::class); | ||||
| @@ -166,4 +179,18 @@ class HomeController extends Controller | ||||
| 
 | ||||
|         return view('index', compact('count', 'subTitle', 'transactions', 'billCount', 'start', 'end', 'today')); | ||||
|     } | ||||
| 
 | ||||
|     private function indexV2(): mixed | ||||
|     { | ||||
|         $subTitle = (string)trans('firefly.welcome_back'); | ||||
| 
 | ||||
|         $start    = session('start', today(config('app.timezone'))->startOfMonth()); | ||||
|         $end      = session('end', today(config('app.timezone'))->endOfMonth()); | ||||
| 
 | ||||
|         /** @var User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         event(new RequestedVersionCheckStatus($user)); | ||||
| 
 | ||||
|         return view('index', compact('subTitle', 'start', 'end')); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -113,7 +113,7 @@ class BoxController extends Controller | ||||
|         $spentAmount       = $spent[$currency->id]['sum'] ?? '0'; | ||||
|         app('log')->debug(sprintf('Spent for default currency for all budgets in this period: %s', $spentAmount)); | ||||
| 
 | ||||
|         $days              = $today->between($start, $end) ? $today->diffInDays($start) + 1 : $end->diffInDays($start) + 1; | ||||
|         $days              = (int)($today->between($start, $end) ? $today->diffInDays($start, true) + 1 : $end->diffInDays($start, true) + 1); | ||||
|         app('log')->debug(sprintf('Number of days left: %d', $days)); | ||||
|         $spentPerDay       = bcdiv($spentAmount, (string)$days); | ||||
|         app('log')->debug(sprintf('Available to spend per day: %s', $spentPerDay)); | ||||
| @@ -130,7 +130,7 @@ class BoxController extends Controller | ||||
|                 $boxTitle         = (string)trans('firefly.left_to_spend'); | ||||
|                 $activeDaysLeft   = $this->activeDaysLeft($start, $end);   // see method description.
 | ||||
|                 $display          = 1;                                     // not overspent
 | ||||
|                 $leftPerDayAmount = bcdiv($leftToSpendAmount, (string)$activeDaysLeft); | ||||
|                 $leftPerDayAmount = 0 === $activeDaysLeft ? $leftToSpendAmount : bcdiv($leftToSpendAmount, (string)$activeDaysLeft); | ||||
|                 app('log')->debug(sprintf('Left to spend per day is %s', $leftPerDayAmount)); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -38,7 +38,7 @@ class IntroController extends Controller | ||||
|     /** | ||||
|      * Returns the introduction wizard for a page. | ||||
|      */ | ||||
|     public function getIntroSteps(string $route, string $specificPage = null): JsonResponse | ||||
|     public function getIntroSteps(string $route, ?string $specificPage = null): JsonResponse | ||||
|     { | ||||
|         app('log')->debug(sprintf('getIntroSteps for route "%s" and page "%s"', $route, $specificPage)); | ||||
|         $specificPage ??= ''; | ||||
| @@ -91,7 +91,7 @@ class IntroController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function postEnable(string $route, string $specialPage = null): JsonResponse | ||||
|     public function postEnable(string $route, ?string $specialPage = null): JsonResponse | ||||
|     { | ||||
|         $specialPage ??= ''; | ||||
|         $route = str_replace('.', '_', $route); | ||||
| @@ -111,7 +111,7 @@ class IntroController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function postFinished(string $route, string $specialPage = null): JsonResponse | ||||
|     public function postFinished(string $route, ?string $specialPage = null): JsonResponse | ||||
|     { | ||||
|         $specialPage ??= ''; | ||||
|         $key = 'shown_demo_'.$route; | ||||
|   | ||||
| @@ -67,7 +67,7 @@ class ReconcileController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function overview(Request $request, Account $account = null, Carbon $start = null, Carbon $end = null): JsonResponse | ||||
|     public function overview(Request $request, ?Account $account = null, ?Carbon $start = null, ?Carbon $end = null): JsonResponse | ||||
|     { | ||||
|         $startBalance    = $request->get('startBalance'); | ||||
|         $endBalance      = $request->get('endBalance'); | ||||
| @@ -177,7 +177,7 @@ class ReconcileController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function transactions(Account $account, Carbon $start = null, Carbon $end = null) | ||||
|     public function transactions(Account $account, ?Carbon $start = null, ?Carbon $end = null) | ||||
|     { | ||||
|         if (null === $start || null === $end) { | ||||
|             throw new FireflyException('Invalid dates submitted.'); | ||||
|   | ||||
| @@ -81,7 +81,7 @@ class RecurrenceController extends Controller | ||||
|         $skip                          = $skip < 0 || $skip > 31 ? 0 : $skip; | ||||
|         $weekend                       = $weekend < 1 || $weekend > 4 ? 1 : $weekend; | ||||
| 
 | ||||
|         if (false === $start || false === $end || false === $firstDate || false === $endDate) { | ||||
|         if (null === $start || null === $end || null === $firstDate || null === $endDate) { | ||||
|             return response()->json(); | ||||
|         } | ||||
| 
 | ||||
| @@ -112,7 +112,7 @@ class RecurrenceController extends Controller | ||||
|         $actualEnd                     = clone $end; | ||||
| 
 | ||||
|         if ('until_date' === $endsAt) { | ||||
|             $actualEnd   = $endDate ?? clone $end; | ||||
|             $actualEnd   = $endDate; | ||||
|             $occurrences = $this->recurring->getOccurrencesInRange($repetition, $actualStart, $actualEnd); | ||||
|         } | ||||
|         if ('times' === $endsAt) { | ||||
| @@ -155,7 +155,7 @@ class RecurrenceController extends Controller | ||||
|         } catch (InvalidFormatException $e) { | ||||
|             $date = Carbon::today(config('app.timezone')); | ||||
|         } | ||||
|         if (false === $date) { | ||||
|         if (null === $date) { | ||||
|             return response()->json(); | ||||
|         } | ||||
|         $date->startOfDay(); | ||||
|   | ||||
| @@ -115,7 +115,7 @@ class NewUserController extends Controller | ||||
| 
 | ||||
|         // store frontpage preferences:
 | ||||
|         $accounts      = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray(); | ||||
|         app('preferences')->set('frontPageAccounts', $accounts); | ||||
|         app('preferences')->set('frontpageAccounts', $accounts); | ||||
| 
 | ||||
|         // mark.
 | ||||
|         app('preferences')->mark(); | ||||
|   | ||||
| @@ -89,10 +89,10 @@ class PreferencesController extends Controller | ||||
|         /** @var array<int, int> $accountIds */ | ||||
|         $accountIds            = $accounts->pluck('id')->toArray(); | ||||
|         $viewRange             = app('navigation')->getViewRange(false); | ||||
|         $frontPageAccountsPref = app('preferences')->get('frontPageAccounts', $accountIds); | ||||
|         $frontPageAccounts     = $frontPageAccountsPref->data; | ||||
|         if (!is_array($frontPageAccounts)) { | ||||
|             $frontPageAccounts = $accountIds; | ||||
|         $frontpageAccountsPref = app('preferences')->get('frontpageAccounts', $accountIds); | ||||
|         $frontpageAccounts     = $frontpageAccountsPref->data; | ||||
|         if (!is_array($frontpageAccounts)) { | ||||
|             $frontpageAccounts = $accountIds; | ||||
|         } | ||||
|         $language              = app('steam')->getLanguage(); | ||||
|         $languages             = config('firefly.languages'); | ||||
| @@ -128,8 +128,8 @@ class PreferencesController extends Controller | ||||
|         $locales               = ['equal' => (string)trans('firefly.equal_to_language')] + $locales; | ||||
|         // an important fallback is that the frontPageAccount array gets refilled automatically
 | ||||
|         // when it turns up empty.
 | ||||
|         if (0 === count($frontPageAccounts)) { | ||||
|             $frontPageAccounts = $accountIds; | ||||
|         if (0 === count($frontpageAccounts)) { | ||||
|             $frontpageAccounts = $accountIds; | ||||
|         } | ||||
| 
 | ||||
|         // for the demo user, the slackUrl is automatically emptied.
 | ||||
| @@ -139,7 +139,7 @@ class PreferencesController extends Controller | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
| 
 | ||||
|         return view('preferences.index', compact('language', 'groupedAccounts', 'isDocker', 'frontPageAccounts', 'languages', 'darkMode', 'availableDarkModes', 'notifications', 'slackUrl', 'locales', 'locale', 'tjOptionalFields', 'viewRange', 'customFiscalYear', 'listPageSize', 'fiscalYearStart')); | ||||
|         return view('preferences.index', compact('language', 'groupedAccounts', 'isDocker', 'frontpageAccounts', 'languages', 'darkMode', 'availableDarkModes', 'notifications', 'slackUrl', 'locales', 'locale', 'tjOptionalFields', 'viewRange', 'customFiscalYear', 'listPageSize', 'fiscalYearStart')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -155,12 +155,12 @@ class PreferencesController extends Controller | ||||
|     public function postIndex(Request $request) | ||||
|     { | ||||
|         // front page accounts
 | ||||
|         $frontPageAccounts = []; | ||||
|         if (is_array($request->get('frontPageAccounts')) && count($request->get('frontPageAccounts')) > 0) { | ||||
|             foreach ($request->get('frontPageAccounts') as $id) { | ||||
|                 $frontPageAccounts[] = (int)$id; | ||||
|         $frontpageAccounts = []; | ||||
|         if (is_array($request->get('frontpageAccounts')) && count($request->get('frontpageAccounts')) > 0) { | ||||
|             foreach ($request->get('frontpageAccounts') as $id) { | ||||
|                 $frontpageAccounts[] = (int)$id; | ||||
|             } | ||||
|             app('preferences')->set('frontPageAccounts', $frontPageAccounts); | ||||
|             app('preferences')->set('frontpageAccounts', $frontpageAccounts); | ||||
|         } | ||||
| 
 | ||||
|         // extract notifications:
 | ||||
| @@ -223,8 +223,8 @@ class PreferencesController extends Controller | ||||
| 
 | ||||
|         // same for locale:
 | ||||
|         if (!auth()->user()->hasRole('demo')) { | ||||
|             /** @var Preference $locale */ | ||||
|             $locale = $request->get('locale'); | ||||
|             $locale = (string) $request->get('locale'); | ||||
|             $locale = '' === $locale ? null : $locale; | ||||
|             app('preferences')->set('locale', $locale); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -23,7 +23,6 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Http\Controllers; | ||||
| 
 | ||||
| use Auth; | ||||
| use FireflyIII\Events\UserChangedEmail; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Exceptions\ValidationException; | ||||
| @@ -467,9 +466,7 @@ class ProfileController extends Controller | ||||
|         if (is_array($secret)) { | ||||
|             $secret = null; | ||||
|         } | ||||
|         if (is_int($secret)) { | ||||
|             $secret = (string)$secret; | ||||
|         } | ||||
|         $secret     = (string)$secret; | ||||
| 
 | ||||
|         $repository->setMFACode($user, $secret); | ||||
| 
 | ||||
|   | ||||
| @@ -76,7 +76,7 @@ class CreateController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function create(Request $request, RuleGroup $ruleGroup = null) | ||||
|     public function create(Request $request, ?RuleGroup $ruleGroup = null) | ||||
|     { | ||||
|         $this->createDefaultRuleGroup(); | ||||
|         $preFilled    = [ | ||||
|   | ||||
| @@ -215,7 +215,7 @@ class TagController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function show(Request $request, Tag $tag, Carbon $start = null, Carbon $end = null) | ||||
|     public function show(Request $request, Tag $tag, ?Carbon $start = null, ?Carbon $end = null) | ||||
|     { | ||||
|         // default values:
 | ||||
|         $subTitleIcon = 'fa-tag'; | ||||
| @@ -312,6 +312,9 @@ class TagController extends Controller | ||||
|         if (count($this->attachmentsHelper->getMessages()->get('attachments')) > 0) { | ||||
|             $request->session()->flash('info', $this->attachmentsHelper->getMessages()->get('attachments')); | ||||
|         } | ||||
|         if (count($this->attachmentsHelper->getErrors()->get('attachments')) > 0) { | ||||
|             $request->session()->flash('error', $this->attachmentsHelper->getErrors()->get('attachments')); | ||||
|         } | ||||
|         $redirect = redirect($this->getPreviousUrl('tags.create.url')); | ||||
|         if (1 === (int)$request->get('create_another')) { | ||||
|             session()->put('tags.create.fromStore', true); | ||||
| @@ -347,6 +350,9 @@ class TagController extends Controller | ||||
|         if (count($this->attachmentsHelper->getMessages()->get('attachments')) > 0) { | ||||
|             $request->session()->flash('info', $this->attachmentsHelper->getMessages()->get('attachments')); | ||||
|         } | ||||
|         if (count($this->attachmentsHelper->getErrors()->get('attachments')) > 0) { | ||||
|             $request->session()->flash('error', $this->attachmentsHelper->getErrors()->get('attachments')); | ||||
|         } | ||||
|         $redirect = redirect($this->getPreviousUrl('tags.edit.url')); | ||||
|         if (1 === (int)$request->get('return_to_edit')) { | ||||
|             session()->put('tags.edit.fromUpdate', true); | ||||
|   | ||||
| @@ -69,7 +69,7 @@ class IndexController extends Controller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function index(Request $request, string $objectType, Carbon $start = null, Carbon $end = null) | ||||
|     public function index(Request $request, string $objectType, ?Carbon $start = null, ?Carbon $end = null) | ||||
|     { | ||||
|         if ('transfers' === $objectType) { | ||||
|             $objectType = 'transfer'; | ||||
|   | ||||
							
								
								
									
										45
									
								
								app/Http/Controllers/UserGroup/CreateController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								app/Http/Controllers/UserGroup/CreateController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| <?php | ||||
| /* | ||||
|  * CreateController.php | ||||
|  * Copyright (c) 2024 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\Controllers\UserGroup; | ||||
| 
 | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Contracts\View\View; | ||||
| use Illuminate\Foundation\Application; | ||||
| 
 | ||||
| class CreateController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * @return Application|Factory|\Illuminate\Contracts\Foundation\Application|View | ||||
|      */ | ||||
|     public function create() | ||||
|     { | ||||
|         $title         = (string)trans('firefly.administrations_page_title'); | ||||
|         $subTitle      = (string)trans('firefly.administrations_page_create_sub_title'); | ||||
|         $mainTitleIcon = 'fa-book'; | ||||
|         app('log')->debug(sprintf('Now at %s', __METHOD__)); | ||||
| 
 | ||||
|         return view('administrations.create')->with(compact('title', 'subTitle', 'mainTitleIcon')); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										46
									
								
								app/Http/Controllers/UserGroup/EditController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								app/Http/Controllers/UserGroup/EditController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <?php | ||||
| /* | ||||
|  * CreateController.php | ||||
|  * Copyright (c) 2024 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\Controllers\UserGroup; | ||||
| 
 | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\UserGroup; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Contracts\View\View; | ||||
| use Illuminate\Foundation\Application; | ||||
| 
 | ||||
| class EditController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * @return Application|Factory|\Illuminate\Contracts\Foundation\Application|View | ||||
|      */ | ||||
|     public function edit(UserGroup $userGroup) | ||||
|     { | ||||
|         $title         = (string)trans('firefly.administrations_page_title'); | ||||
|         $subTitle      = (string)trans('firefly.administrations_page_edit_sub_title', ['title' => $userGroup->title]); | ||||
|         $mainTitleIcon = 'fa-book'; | ||||
|         app('log')->debug(sprintf('Now at %s', __METHOD__)); | ||||
| 
 | ||||
|         return view('administrations.edit')->with(compact('title', 'subTitle', 'mainTitleIcon')); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										47
									
								
								app/Http/Controllers/UserGroup/IndexController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								app/Http/Controllers/UserGroup/IndexController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <?php | ||||
| /* | ||||
|  * IndexController.php | ||||
|  * Copyright (c) 2024 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\Controllers\UserGroup; | ||||
| 
 | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\View\View; | ||||
| 
 | ||||
| class IndexController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * Show all administrations. | ||||
|      * | ||||
|      * @return Factory|View | ||||
|      */ | ||||
|     public function index(Request $request) | ||||
|     { | ||||
|         $title         = (string)trans('firefly.administrations_page_title'); | ||||
|         $subTitle      = (string)trans('firefly.administrations_page_sub_title'); | ||||
|         $mainTitleIcon = 'fa-book'; | ||||
|         app('log')->debug(sprintf('Now at %s', __METHOD__)); | ||||
| 
 | ||||
|         return view('administrations.index')->with(compact('title', 'subTitle', 'mainTitleIcon')); | ||||
|     } | ||||
| } | ||||
| @@ -25,6 +25,7 @@ namespace FireflyIII\Http\Middleware; | ||||
| 
 | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Vite; | ||||
| use Barryvdh\Debugbar\Facades\Debugbar; | ||||
| 
 | ||||
| /** | ||||
|  * Class SecureHeaders | ||||
| @@ -43,6 +44,9 @@ class SecureHeaders | ||||
|         // generate and share nonce.
 | ||||
|         $nonce              = base64_encode(random_bytes(16)); | ||||
|         Vite::useCspNonce($nonce); | ||||
|         if (class_exists('Barryvdh\Debugbar\Facades\Debugbar')) { | ||||
|             Debugbar::getJavascriptRenderer()->setCspNonce($nonce); | ||||
|         } | ||||
|         app('view')->share('JS_NONCE', $nonce); | ||||
| 
 | ||||
|         $response           = $next($request); | ||||
| @@ -55,14 +59,29 @@ class SecureHeaders | ||||
|             "base-uri 'self'", | ||||
|             "font-src 'self' data:", | ||||
|             sprintf("connect-src 'self' %s", $trackingScriptSrc), | ||||
|             sprintf("img-src 'self' 'nonce-%1s'", $nonce), | ||||
|             sprintf("img-src 'self' data: 'nonce-%1s' ", $nonce), | ||||
|             "manifest-src 'self'", | ||||
|         ]; | ||||
| 
 | ||||
|         // overrule in development mode
 | ||||
|         if (true === env('IS_LOCAL_DEV')) { | ||||
|             $csp = [ | ||||
|                 "default-src 'none'", | ||||
|                 "object-src 'none'", | ||||
|                 sprintf("script-src 'unsafe-eval' 'strict-dynamic' 'nonce-%1s' https://firefly.sd.internal/_debugbar/assets", $nonce), | ||||
|                 "style-src 'unsafe-inline' 'self' https://10.0.0.15:5173/", | ||||
|                 "base-uri 'self'", | ||||
|                 "font-src 'self' data: https://10.0.0.15:5173/", | ||||
|                 sprintf("connect-src 'self' %s https://10.0.0.15:5173/ wss://10.0.0.15:5173/", $trackingScriptSrc), | ||||
|                 sprintf("img-src 'self' data: 'nonce-%1s'", $nonce), | ||||
|                 "manifest-src 'self'", | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         $route              = $request->route(); | ||||
|         $customUrl          = ''; | ||||
|         $authGuard          = (string)config('firefly.authentication_guard'); | ||||
|         $logoutUrl          = (string)config('firefly.custom_logout_url'); | ||||
|         $authGuard          = (string) config('firefly.authentication_guard'); | ||||
|         $logoutUrl          = (string) config('firefly.custom_logout_url'); | ||||
|         if ('remote_user_guard' === $authGuard && '' !== $logoutUrl) { | ||||
|             $customUrl = $logoutUrl; | ||||
|         } | ||||
| @@ -110,8 +129,8 @@ class SecureHeaders | ||||
|      */ | ||||
|     private function getTrackingScriptSource(): string | ||||
|     { | ||||
|         if ('' !== (string)config('firefly.tracker_site_id') && '' !== (string)config('firefly.tracker_url')) { | ||||
|             return (string)config('firefly.tracker_url'); | ||||
|         if ('' !== (string) config('firefly.tracker_site_id') && '' !== (string) config('firefly.tracker_url')) { | ||||
|             return (string) config('firefly.tracker_url'); | ||||
|         } | ||||
| 
 | ||||
|         return ''; | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user