mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 10:47:00 +00:00 
			
		
		
		
	Compare commits
	
		
			24 Commits
		
	
	
		
			develop-20
			...
			develop-20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | d93732e451 | ||
|  | e2f1fc307f | ||
|  | fe6021a3d6 | ||
|  | 563c54702b | ||
|  | 4d67d27ba0 | ||
|  | edf3876a57 | ||
|  | f197e6623b | ||
|  | 42a9809450 | ||
|  | 444f80a933 | ||
|  | 4b985c818a | ||
|  | 2e62fe7b72 | ||
|  | ccd182aed9 | ||
|  | 615eef3fdd | ||
|  | 3117c8846e | ||
|  | f1c859aaa3 | ||
|  | f782979d6c | ||
|  | 53252b84fd | ||
|  | 69b2c1f4d2 | ||
|  | 1ccda0b598 | ||
|  | eb3b67ffd6 | ||
|  | 591b795aa3 | ||
|  | ac21ed7d18 | ||
|  | d9c66a2db0 | ||
|  | 6f02eff020 | 
							
								
								
									
										6
									
								
								.ci/php-cs-fixer/composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.ci/php-cs-fixer/composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -97,13 +97,13 @@ | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "3.x-dev" | ||||
|                 }, | ||||
|                 "phpstan": { | ||||
|                     "includes": [ | ||||
|                         "extension.neon" | ||||
|                     ] | ||||
|                 }, | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "3.x-dev" | ||||
|                 } | ||||
|             }, | ||||
|             "autoload": { | ||||
|   | ||||
| @@ -26,7 +26,8 @@ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) | ||||
| cd $SCRIPT_DIR/php-cs-fixer | ||||
| composer update --quiet | ||||
| rm -f .php-cs-fixer.cache | ||||
| PHP_CS_FIXER_IGNORE_ENV=true ./vendor/bin/php-cs-fixer fix \ | ||||
| PHP_CS_FIXER_IGNORE_ENV=true | ||||
| ./vendor/bin/php-cs-fixer fix \ | ||||
|     --config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php \ | ||||
|     --format=txt \ | ||||
|     --allow-risky=yes | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/close-duplicates.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/close-duplicates.yml
									
									
									
									
										vendored
									
									
								
							| @@ -13,7 +13,7 @@ jobs: | ||||
|   close_duplicates: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: github/command@v1.2.2 | ||||
|       - uses: github/command@v1.3.0 | ||||
|         id: command | ||||
|         with: | ||||
|           allowed_contexts: "issue" | ||||
|   | ||||
							
								
								
									
										76
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										76
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -179,10 +179,10 @@ jobs: | ||||
|           if [[ "$version" == branch* ]]; then | ||||
|             [[ -z $(git status --untracked-files=normal --porcelain) ]] && echo "this branch is clean, no need to push..." && exit 0; | ||||
|             # branch builds overrule develop | ||||
|             releaseName=develop-$(date +'%Y%m%d') | ||||
|             releaseName=$version-$(date +'%Y%m%d') | ||||
|             originalName=$releaseName | ||||
|             zipName=FireflyIII-develop.zip | ||||
|             tarName=FireflyIII-develop.tar.gz | ||||
|             zipName=FireflyIII-$version.zip | ||||
|             tarName=FireflyIII-$version.tar.gz | ||||
|           fi | ||||
|  | ||||
|           # in both cases, if the release or tag already exists, add ".1" until it no longer exists. | ||||
| @@ -226,12 +226,13 @@ jobs: | ||||
|           gpg --armor --detach-sign $zipName | ||||
|           gpg --armor --detach-sign $tarName | ||||
|  | ||||
|           # create a development (nightly) release: | ||||
|           if [[ "develop" == "$version" ]] || [[ "$version" == branch* ]]; then | ||||
|           # describe the development release. | ||||
|           if [[ "develop" == "$version" ]]; then | ||||
|             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 | ||||
|             touch output.txt | ||||
|             sudo chown -R runner:docker output.txt | ||||
|             echo "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 | ||||
| @@ -239,15 +240,62 @@ jobs: | ||||
|             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 | ||||
|           fi | ||||
|           # describe a branch release | ||||
|           if [[ "$version" == branch* ]]; then | ||||
|             echo 'Branch release.' | ||||
|             rm output.txt | ||||
|             touch output.txt | ||||
|             sudo chown -R runner:docker output.txt | ||||
|             echo "Irregular BRANCH release of Firefly III. This release contains specific features or changes. Docker users can find this release under the \`$version\` 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 branch pre-release, as it may not work as expected." >> output.txt | ||||
|           fi | ||||
|           # describe the main release | ||||
|           if [[ "develop" != "$version" ]] && [[ "$version" != branch* ]]; then | ||||
|             sudo chown -R runner:docker output.txt | ||||
|             echo 'Main release.' | ||||
|             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/)." >> output.txt | ||||
|  | ||||
|           fi | ||||
|  | ||||
|           # create a development release: | ||||
|           if [[ "develop" == "$version" ]]; then | ||||
|             # create the release: | ||||
|             echo "Create nightly release." | ||||
|             git tag -a $releaseName -m "Nightly development release '$version' on $(date +'%Y-%m-%d')" | ||||
|             echo "Create develop release." | ||||
|             git tag -a $releaseName -m "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 | ||||
|           fi | ||||
|  | ||||
|           # create a branch release: | ||||
|           if [[ "$version" == branch* ]]; then | ||||
|  | ||||
|             # create the release: | ||||
|             echo "Create branch release." | ||||
|             git tag -a $releaseName -m "Branch release '$version' on $(date +'%Y-%m-%d')" | ||||
|  | ||||
|             git push origin $releaseName | ||||
|             gh release create $releaseName -p --verify-tag \ | ||||
|               -t "Branch release for $(date +'%Y-%m-%d')" \ | ||||
|               -F output.txt | ||||
|           fi | ||||
|  | ||||
|           # create a development (nightly) release: | ||||
|           if [[ "develop" == "$version" ]] || [[ "$version" == branch* ]]; then | ||||
|             # add zip file to release. | ||||
|             gh release upload $releaseName $zipName | ||||
|             gh release upload $releaseName $tarName | ||||
| @@ -266,16 +314,6 @@ jobs: | ||||
|             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/)." >> output.txt | ||||
|  | ||||
|             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 | ||||
|   | ||||
| @@ -66,13 +66,14 @@ class PiggyBankController extends Controller | ||||
|      */ | ||||
|     public function piggyBanks(AutocompleteRequest $request): JsonResponse | ||||
|     { | ||||
|         $data     = $request->getData(); | ||||
|         $piggies  = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit')); | ||||
|         $response = []; | ||||
|         $data            = $request->getData(); | ||||
|         $piggies         = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit')); | ||||
|         $defaultCurrency = app('amount')->getDefaultCurrency(); | ||||
|         $response        = []; | ||||
| 
 | ||||
|         /** @var PiggyBank $piggy */ | ||||
|         foreach ($piggies as $piggy) { | ||||
|             $currency    = $piggy->transactionCurrency; | ||||
|             $currency    = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency; | ||||
|             $objectGroup = $piggy->objectGroups()->first(); | ||||
|             $response[]  = [ | ||||
|                 'id'                      => (string)$piggy->id, | ||||
| @@ -96,14 +97,15 @@ class PiggyBankController extends Controller | ||||
|      */ | ||||
|     public function piggyBanksWithBalance(AutocompleteRequest $request): JsonResponse | ||||
|     { | ||||
|         $data     = $request->getData(); | ||||
|         $piggies  = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit')); | ||||
|         $response = []; | ||||
|         $data            = $request->getData(); | ||||
|         $piggies         = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit')); | ||||
|         $defaultCurrency = app('amount')->getDefaultCurrency(); | ||||
|         $response        = []; | ||||
| 
 | ||||
|         /** @var PiggyBank $piggy */ | ||||
|         foreach ($piggies as $piggy) { | ||||
|             $currency      = $piggy->transactionCurrency; | ||||
|             $currentAmount = $this->piggyRepository->getRepetition($piggy)->current_amount ?? '0'; | ||||
|             $currency      = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency; | ||||
|             $currentAmount = $this->piggyRepository->getRepetition($piggy)->currentamount ?? '0'; | ||||
|             $objectGroup   = $piggy->objectGroups()->first(); | ||||
|             $response[]    = [ | ||||
|                 'id'                      => (string)$piggy->id, | ||||
| @@ -112,7 +114,7 @@ class PiggyBankController extends Controller | ||||
|                     '%s (%s / %s)', | ||||
|                     $piggy->name, | ||||
|                     app('amount')->formatAnything($currency, $currentAmount, false), | ||||
|                     app('amount')->formatAnything($currency, $piggy->target_amount, false), | ||||
|                     app('amount')->formatAnything($currency, $piggy->targetamount, false), | ||||
|                 ), | ||||
|                 'currency_id'             => (string)$currency->id, | ||||
|                 'currency_name'           => $currency->name, | ||||
|   | ||||
| @@ -36,7 +36,6 @@ use FireflyIII\Models\RuleGroup; | ||||
| use FireflyIII\Models\Tag; | ||||
| use FireflyIII\Models\TransactionGroup; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| 
 | ||||
| @@ -53,7 +52,7 @@ class PurgeController extends Controller | ||||
|     public function purge(): JsonResponse | ||||
|     { | ||||
|         /** @var User $user */ | ||||
|         $user       = auth()->user(); | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         // some manual code, too lazy to call all repositories.
 | ||||
| 
 | ||||
| @@ -64,17 +63,14 @@ class PurgeController extends Controller | ||||
|         Bill::whereUserId($user->id)->onlyTrashed()->forceDelete(); | ||||
| 
 | ||||
|         // piggies
 | ||||
|         $repository = app(PiggyBankRepositoryInterface::class); | ||||
|         $repository->setUser($user); | ||||
|         $repository->purgeAll(); | ||||
|         //        $set  = PiggyBank::leftJoin('accounts', 'accounts.id', 'piggy_banks.account_id')
 | ||||
|         //            ->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*'])
 | ||||
|         //        ;
 | ||||
|         //
 | ||||
|         //        /** @var PiggyBank $piggy */
 | ||||
|         //        foreach ($set as $piggy) {
 | ||||
|         //            $piggy->forceDelete();
 | ||||
|         //        }
 | ||||
|         $set  = PiggyBank::leftJoin('accounts', 'accounts.id', 'piggy_banks.account_id') | ||||
|             ->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*']) | ||||
|         ; | ||||
| 
 | ||||
|         /** @var PiggyBank $piggy */ | ||||
|         foreach ($set as $piggy) { | ||||
|             $piggy->forceDelete(); | ||||
|         } | ||||
| 
 | ||||
|         // rule group
 | ||||
|         RuleGroup::whereUserId($user->id)->onlyTrashed()->forceDelete(); | ||||
|   | ||||
| @@ -111,7 +111,7 @@ class ListController extends Controller | ||||
|         // types to get, page size:
 | ||||
|         $pageSize    = $this->parameters->get('limit'); | ||||
| 
 | ||||
|         // get list of piggy banks. Count it and split it.
 | ||||
|         // get list of budgets. Count it and split it.
 | ||||
|         $collection  = $this->repository->getPiggyBanks($account); | ||||
|         $count       = $collection->count(); | ||||
|         $piggyBanks  = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|   | ||||
| @@ -69,7 +69,6 @@ class StoreController extends Controller | ||||
|         $data               = $request->getAll(); | ||||
|         $data['start_date'] = $data['start']; | ||||
|         $data['end_date']   = $data['end']; | ||||
|         $data['notes']      = $data['notes']; | ||||
|         $data['budget_id']  = $budget->id; | ||||
| 
 | ||||
|         $budgetLimit        = $this->blRepository->store($data); | ||||
|   | ||||
| @@ -28,7 +28,6 @@ use FireflyIII\Api\V1\Controllers\Controller; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| use FireflyIII\Transformers\AccountTransformer; | ||||
| use FireflyIII\Transformers\AttachmentTransformer; | ||||
| use FireflyIII\Transformers\PiggyBankEventTransformer; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| @@ -119,36 +118,4 @@ class ListController extends Controller | ||||
| 
 | ||||
|         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)#/piggy_banks/listAccountByPiggyBank
 | ||||
|      * | ||||
|      * List single resource. | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function accounts(PiggyBank $piggyBank): JsonResponse | ||||
|     { | ||||
|         // types to get, page size:
 | ||||
|         $pageSize    = $this->parameters->get('limit'); | ||||
|         $manager     = $this->getManager(); | ||||
| 
 | ||||
|         $collection  = $piggyBank->accounts; | ||||
|         $count       = $collection->count(); | ||||
|         $events      = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
| 
 | ||||
|         // make paginator:
 | ||||
|         $paginator   = new LengthAwarePaginator($events, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $paginator->setPath(route('api.v1.piggy-banks.accounts', [$piggyBank->id]).$this->buildParams()); | ||||
| 
 | ||||
|         /** @var AccountTransformer $transformer */ | ||||
|         $transformer = app(AccountTransformer::class); | ||||
|         $transformer->setParameters($this->parameters); | ||||
| 
 | ||||
|         $resource    = new FractalCollection($events, $transformer, 'accounts'); | ||||
|         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); | ||||
| 
 | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -72,7 +72,7 @@ class ShowController extends Controller | ||||
|         // types to get, page size:
 | ||||
|         $pageSize    = $this->parameters->get('limit'); | ||||
| 
 | ||||
|         // get list of piggy banks. Count it and split it.
 | ||||
|         // get list of budgets. Count it and split it.
 | ||||
|         $collection  = $this->repository->getPiggyBanks(); | ||||
|         $count       = $collection->count(); | ||||
|         $piggyBanks  = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|   | ||||
| @@ -48,7 +48,6 @@ class StoreRequest extends FormRequest | ||||
|             'amount'        => $this->convertString('amount'), | ||||
|             'currency_id'   => $this->convertInteger('currency_id'), | ||||
|             'currency_code' => $this->convertString('currency_code'), | ||||
|             'notes'         => $this->stringWithNewlines('notes'), | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -63,7 +62,6 @@ class StoreRequest extends FormRequest | ||||
|             'amount'        => ['required', new IsValidPositiveAmount()], | ||||
|             'currency_id'   => 'numeric|exists:transaction_currencies,id', | ||||
|             'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', | ||||
|             'notes'         => 'nullable|min:0|max:32768', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -51,12 +51,7 @@ class UpdateRequest extends FormRequest | ||||
|             'amount'        => ['amount', 'convertString'], | ||||
|             'currency_id'   => ['currency_id', 'convertInteger'], | ||||
|             'currency_code' => ['currency_code', 'convertString'], | ||||
|             'notes'         => ['notes', 'stringWithNewlines'], | ||||
|         ]; | ||||
|         if (false === $this->has('notes')) { | ||||
|             // ignore notes, not submitted.
 | ||||
|             unset($fields['notes']); | ||||
|         } | ||||
| 
 | ||||
|         return $this->getAllData($fields); | ||||
|     } | ||||
| @@ -72,7 +67,6 @@ class UpdateRequest extends FormRequest | ||||
|             'amount'        => ['nullable', new IsValidPositiveAmount()], | ||||
|             'currency_id'   => 'numeric|exists:transaction_currencies,id', | ||||
|             'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', | ||||
|             'notes'         => 'nullable|min:0|max:32768', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -90,7 +84,7 @@ class UpdateRequest extends FormRequest | ||||
|                     $start = new Carbon($data['start']); | ||||
|                     $end   = new Carbon($data['end']); | ||||
|                     if ($end->isBefore($start)) { | ||||
|                         $validator->errors()->add('end', (string) trans('validation.date_after')); | ||||
|                         $validator->errors()->add('end', (string)trans('validation.date_after')); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -24,15 +24,10 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V1\Requests\Models\PiggyBank; | ||||
| 
 | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Rules\IsValidZeroOrMoreAmount; | ||||
| use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
|  * Class StoreRequest | ||||
| @@ -47,20 +42,19 @@ class StoreRequest extends FormRequest | ||||
|      */ | ||||
|     public function getAll(): array | ||||
|     { | ||||
|         $fields                            = [ | ||||
|         $fields                     = [ | ||||
|             'order' => ['order', 'convertInteger'], | ||||
|         ]; | ||||
|         $data                              = $this->getAllData($fields); | ||||
|         $data['name']                      = $this->convertString('name'); | ||||
|         $data['accounts']                  = $this->parseAccounts($this->get('accounts')); | ||||
|         $data['target_amount']             = $this->convertString('target_amount'); | ||||
|         $data['start_date']                = $this->getCarbonDate('start_date'); | ||||
|         $data['target_date']               = $this->getCarbonDate('target_date'); | ||||
|         $data['notes']                     = $this->stringWithNewlines('notes'); | ||||
|         $data['object_group_id']           = $this->convertInteger('object_group_id'); | ||||
|         $data['transaction_currency_id']   = $this->convertInteger('transaction_currency_id'); | ||||
|         $data['transaction_currency_code'] = $this->convertString('transaction_currency_code'); | ||||
|         $data['object_group_title']        = $this->convertString('object_group_title'); | ||||
|         $data                       = $this->getAllData($fields); | ||||
|         $data['name']               = $this->convertString('name'); | ||||
|         $data['account_id']         = $this->convertInteger('account_id'); | ||||
|         $data['targetamount']       = $this->convertString('target_amount'); | ||||
|         $data['current_amount']     = $this->convertString('current_amount'); | ||||
|         $data['startdate']          = $this->getCarbonDate('start_date'); | ||||
|         $data['targetdate']         = $this->getCarbonDate('target_date'); | ||||
|         $data['notes']              = $this->stringWithNewlines('notes'); | ||||
|         $data['object_group_id']    = $this->convertInteger('object_group_id'); | ||||
|         $data['object_group_title'] = $this->convertString('object_group_title'); | ||||
| 
 | ||||
|         return $data; | ||||
|     } | ||||
| @@ -71,99 +65,15 @@ class StoreRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name'                      => 'required|min:1|max:255|uniquePiggyBankForUser', | ||||
|             'accounts'                  => 'required', | ||||
|             'accounts.*'                => 'array|required', | ||||
|             'accounts.*.account_id'     => 'required|numeric|belongsToUser:accounts,id', | ||||
|             'accounts.*.current_amount' => ['numeric', new IsValidZeroOrMoreAmount()], | ||||
|             'object_group_id'           => 'numeric|belongsToUser:object_groups,id', | ||||
|             'object_group_title'        => ['min:1', 'max:255'], | ||||
|             'target_amount'             => ['required', new IsValidZeroOrMoreAmount()], | ||||
|             'start_date'                => 'date|nullable', | ||||
|             'transaction_currency_id'   => 'exists:transaction_currencies,id|required_without:transaction_currency_code', | ||||
|             'transaction_currency_code' => 'exists:transaction_currencies,code|required_without:transaction_currency_id', | ||||
|             'target_date'               => 'date|nullable|after:start_date', | ||||
|             'notes'                     => 'max:65000', | ||||
|             'name'               => 'required|min:1|max:255|uniquePiggyBankForUser', | ||||
|             'current_amount'     => ['nullable', new IsValidPositiveAmount()], | ||||
|             'account_id'         => 'required|numeric|belongsToUser:accounts,id', | ||||
|             'object_group_id'    => 'numeric|belongsToUser:object_groups,id', | ||||
|             'object_group_title' => ['min:1', 'max:255'], | ||||
|             'target_amount'      => ['required', new IsValidPositiveAmount()], | ||||
|             'start_date'         => 'date|nullable', | ||||
|             'target_date'        => 'date|nullable|after:start_date', | ||||
|             'notes'              => 'max:65000', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Can only store money on liabilities and asset accounts. | ||||
|      */ | ||||
|     public function withValidator(Validator $validator): void | ||||
|     { | ||||
|         $validator->after( | ||||
|             function (Validator $validator): void { | ||||
|                 // validate start before end only if both are there.
 | ||||
|                 $data          = $validator->getData(); | ||||
|                 $currency      = $this->getCurrencyFromData($data); | ||||
|                 $targetAmount  = (string) ($data['target_amount'] ?? '0'); | ||||
|                 $currentAmount = '0'; | ||||
|                 if (array_key_exists('accounts', $data) && is_array($data['accounts'])) { | ||||
|                     $repository = app(AccountRepositoryInterface::class); | ||||
|                     $types      = config('firefly.piggy_bank_account_types'); | ||||
|                     foreach ($data['accounts'] as $index => $array) { | ||||
|                         $accountId = (int) ($array['account_id'] ?? 0); | ||||
|                         $account   = $repository->find($accountId); | ||||
|                         if (null !== $account) { | ||||
|                             // check currency here.
 | ||||
|                             $accountCurrency = $repository->getAccountCurrency($account); | ||||
|                             $isMultiCurrency = $repository->getMetaValue($account, 'is_multi_currency'); | ||||
|                             $currentAmount   = bcadd($currentAmount, (string)($array['current_amount'] ?? '0')); | ||||
|                             if ($accountCurrency->id !== $currency->id && 'true' !== $isMultiCurrency) { | ||||
|                                 $validator->errors()->add(sprintf('accounts.%d', $index), trans('validation.invalid_account_currency')); | ||||
|                             } | ||||
|                             $type            = $account->accountType->type; | ||||
|                             if (!in_array($type, $types, true)) { | ||||
|                                 $validator->errors()->add(sprintf('accounts.%d', $index), trans('validation.invalid_account_type')); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (-1 === bccomp($targetAmount, $currentAmount) && 1 === bccomp($targetAmount, '0')) { | ||||
|                     $validator->errors()->add('target_amount', trans('validation.current_amount_too_much')); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         if ($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function parseAccounts(mixed $array): array | ||||
|     { | ||||
|         if (!is_array($array)) { | ||||
|             return []; | ||||
|         } | ||||
|         $return = []; | ||||
|         foreach ($array as $entry) { | ||||
|             if (!is_array($entry)) { | ||||
|                 continue; | ||||
|             } | ||||
|             $return[] = [ | ||||
|                 'account_id'     => $this->integerFromValue((string) ($entry['account_id'] ?? '0')), | ||||
|                 'current_amount' => $this->clearString((string) ($entry['current_amount'] ?? '0')), | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $return; | ||||
|     } | ||||
| 
 | ||||
|     private function getCurrencyFromData(array $data): TransactionCurrency | ||||
|     { | ||||
|         if (array_key_exists('transaction_currency_code', $data) && '' !== (string) $data['transaction_currency_code']) { | ||||
|             $currency = TransactionCurrency::whereCode($data['transaction_currency_code'])->first(); | ||||
|             if (null !== $currency) { | ||||
|                 return $currency; | ||||
|             } | ||||
|         } | ||||
|         if (array_key_exists('transaction_currency_id', $data) && '' !== (string) $data['transaction_currency_id']) { | ||||
|             $currency = TransactionCurrency::find((int) $data['transaction_currency_id']); | ||||
|             if (null !== $currency) { | ||||
|                 return $currency; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         throw new FireflyException('Unexpected empty currency.'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -163,7 +163,7 @@ class CorrectAmounts extends Command | ||||
| 
 | ||||
|     private function fixRepetitions(): void | ||||
|     { | ||||
|         $set   = PiggyBankRepetition::where('current_amount', '<', 0)->get(); | ||||
|         $set   = PiggyBankRepetition::where('currentamount', '<', 0)->get(); | ||||
|         $count = $set->count(); | ||||
|         if (0 === $count) { | ||||
|             $this->friendlyPositive('All piggy bank repetition amounts are positive.'); | ||||
| @@ -173,7 +173,7 @@ class CorrectAmounts extends Command | ||||
| 
 | ||||
|         /** @var PiggyBankRepetition $item */ | ||||
|         foreach ($set as $item) { | ||||
|             $item->current_amount = app('steam')->positive($item->current_amount); | ||||
|             $item->currentamount = app('steam')->positive($item->currentamount); | ||||
|             $item->save(); | ||||
|         } | ||||
|         $this->friendlyInfo(sprintf('Corrected %d piggy bank repetition amount(s).', $count)); | ||||
| @@ -181,7 +181,7 @@ class CorrectAmounts extends Command | ||||
| 
 | ||||
|     private function fixPiggyBanks(): void | ||||
|     { | ||||
|         $set   = PiggyBank::where('target_amount', '<', 0)->get(); | ||||
|         $set   = PiggyBank::where('targetamount', '<', 0)->get(); | ||||
|         $count = $set->count(); | ||||
|         if (0 === $count) { | ||||
|             $this->friendlyPositive('All piggy bank amounts are positive.'); | ||||
| @@ -191,7 +191,7 @@ class CorrectAmounts extends Command | ||||
| 
 | ||||
|         /** @var PiggyBank $item */ | ||||
|         foreach ($set as $item) { | ||||
|             $item->target_amount = app('steam')->positive($item->target_amount); | ||||
|             $item->targetamount = app('steam')->positive($item->targetamount); | ||||
|             $item->save(); | ||||
|         } | ||||
|         $this->friendlyInfo(sprintf('Corrected %d piggy bank amount(s).', $count)); | ||||
|   | ||||
| @@ -67,8 +67,8 @@ class AddTimezonesToDates extends Command | ||||
|         CurrencyExchangeRate::class => ['date'], // done
 | ||||
|         InvitedUser::class          => ['expires'], | ||||
|         PiggyBankEvent::class       => ['date'], | ||||
|         PiggyBankRepetition::class  => ['start_date', 'target_date'], | ||||
|         PiggyBank::class            => ['start_date', 'target_date'], // done
 | ||||
|         PiggyBankRepetition::class  => ['startdate', 'targetdate'], | ||||
|         PiggyBank::class            => ['startdate', 'targetdate'], // done
 | ||||
|         Recurrence::class           => ['first_date', 'repeat_until', 'latest_date'], | ||||
|         Tag::class                  => ['date'], | ||||
|         TransactionJournal::class   => ['date'], | ||||
|   | ||||
| @@ -59,22 +59,18 @@ class ReportSum extends Command | ||||
| 
 | ||||
|         /** @var User $user */ | ||||
|         foreach ($userRepository->all() as $user) { | ||||
|             $sum     = (string) $user->transactions()->selectRaw('SUM(amount) as total')->value('total'); | ||||
|             $foreign = (string) $user->transactions()->selectRaw('SUM(foreign_amount) as total')->value('total'); | ||||
|             $sum     = '' === $sum ? '0' : $sum; | ||||
|             $foreign = '' === $foreign ? '0' : $foreign; | ||||
|             $total   = bcadd($sum, $foreign); | ||||
|             if (!is_numeric($total)) { | ||||
|                 $message = sprintf('Error: Transactions for user #%d (%s) have an invalid sum ("%s").', $user->id, $user->email, $total); | ||||
|             $sum = (string)$user->transactions()->selectRaw('SUM(amount) + SUM(foreign_amount) as total')->value('total'); | ||||
|             if (!is_numeric($sum)) { | ||||
|                 $message = sprintf('Error: Transactions for user #%d (%s) have an invalid sum ("%s").', $user->id, $user->email, $sum); | ||||
|                 $this->friendlyError($message); | ||||
| 
 | ||||
|                 continue; | ||||
|             } | ||||
|             if (0 !== bccomp($total, '0')) { | ||||
|                 $message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $total); | ||||
|             if (0 !== bccomp($sum, '0')) { | ||||
|                 $message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum); | ||||
|                 $this->friendlyError($message); | ||||
|             } | ||||
|             if (0 === bccomp($total, '0')) { | ||||
|             if (0 === bccomp($sum, '0')) { | ||||
|                 $this->friendlyPositive(sprintf('Amount integrity OK for user #%d', $user->id)); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -83,8 +83,8 @@ class ForceDecimalSize extends Command | ||||
|             'currency_exchange_rates'  => ['rate', 'user_rate'], | ||||
|             'limit_repetitions'        => ['amount'], | ||||
|             'piggy_bank_events'        => ['amount'], | ||||
|             'piggy_bank_repetitions'   => ['current_amount'], | ||||
|             'piggy_banks'              => ['target_amount'], | ||||
|             'piggy_bank_repetitions'   => ['currentamount'], | ||||
|             'piggy_banks'              => ['targetamount'], | ||||
|             'recurrences_transactions' => ['amount', 'foreign_amount'], | ||||
|             'transactions'             => ['amount', 'foreign_amount'], | ||||
|         ]; | ||||
|   | ||||
| @@ -128,7 +128,7 @@ class ApplyRules extends Command | ||||
|         $ruleEngine->addOperator(['type' => 'account_id', 'value' => $list]); | ||||
| 
 | ||||
|         // add the date as a filter:
 | ||||
|         $ruleEngine->addOperator(['type' => 'date_after', 'value' => $this->start_date->format('Y-m-d')]); | ||||
|         $ruleEngine->addOperator(['type' => 'date_after', 'value' => $this->startDate->format('Y-m-d')]); | ||||
|         $ruleEngine->addOperator(['type' => 'date_before', 'value' => $this->endDate->format('Y-m-d')]); | ||||
| 
 | ||||
|         // start running rules.
 | ||||
| @@ -265,8 +265,8 @@ class ApplyRules extends Command | ||||
|     private function verifyInputDates(): void | ||||
|     { | ||||
|         // parse start date.
 | ||||
|         $inputStart       = today(config('app.timezone'))->startOfMonth(); | ||||
|         $startString      = $this->option('start_date'); | ||||
|         $inputStart      = today(config('app.timezone'))->startOfMonth(); | ||||
|         $startString     = $this->option('start_date'); | ||||
|         if (null === $startString) { | ||||
|             /** @var JournalRepositoryInterface $repository */ | ||||
|             $repository = app(JournalRepositoryInterface::class); | ||||
| @@ -281,8 +281,8 @@ class ApplyRules extends Command | ||||
|         } | ||||
| 
 | ||||
|         // parse end date
 | ||||
|         $inputEnd         = today(config('app.timezone')); | ||||
|         $endString        = $this->option('end_date'); | ||||
|         $inputEnd        = today(config('app.timezone')); | ||||
|         $endString       = $this->option('end_date'); | ||||
|         if (null !== $endString && '' !== $endString) { | ||||
|             $inputEnd = Carbon::createFromFormat('Y-m-d', $endString); | ||||
|         } | ||||
| @@ -296,8 +296,8 @@ class ApplyRules extends Command | ||||
|             [$inputEnd, $inputStart] = [$inputStart, $inputEnd]; | ||||
|         } | ||||
| 
 | ||||
|         $this->start_date = $inputStart; | ||||
|         $this->endDate    = $inputEnd; | ||||
|         $this->startDate = $inputStart; | ||||
|         $this->endDate   = $inputEnd; | ||||
|     } | ||||
| 
 | ||||
|     private function grabAllRules(): void | ||||
|   | ||||
| @@ -72,7 +72,6 @@ class UpgradeDatabase extends Command | ||||
|             'firefly-iii:create-group-memberships', | ||||
|             'firefly-iii:upgrade-group-information', | ||||
|             'firefly-iii:upgrade-currency-preferences', | ||||
|             'firefly-iii:upgrade-multi-piggies', | ||||
|             'firefly-iii:correct-database', | ||||
|         ]; | ||||
|         $args     = []; | ||||
|   | ||||
| @@ -1,112 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| /* | ||||
|  * UpgradeMultiPiggyBanks.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\PiggyBank; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| 
 | ||||
| class UpgradeMultiPiggyBanks extends Command | ||||
| { | ||||
|     use ShowsFriendlyMessages; | ||||
| 
 | ||||
|     public const string CONFIG_NAME = '620_make_multi_piggies'; | ||||
| 
 | ||||
|     protected $description          = 'Upgrade piggybanks so they can use multiple accounts.'; | ||||
| 
 | ||||
|     protected $signature            = 'firefly-iii:upgrade-multi-piggies {--F|force : Force the execution of this command.}'; | ||||
| 
 | ||||
|     private PiggyBankRepositoryInterface $repository; | ||||
|     private AccountRepositoryInterface   $accountRepository; | ||||
| 
 | ||||
|     /** | ||||
|      * 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; | ||||
|         } | ||||
|         $this->upgradePiggyBanks(); | ||||
|         $this->friendlyInfo('Upgraded all piggy banks.'); | ||||
| 
 | ||||
|         $this->markAsExecuted(); | ||||
| 
 | ||||
|         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 markAsExecuted(): void | ||||
|     { | ||||
|         app('fireflyconfig')->set(self::CONFIG_NAME, true); | ||||
|     } | ||||
| 
 | ||||
|     private function upgradePiggyBanks(): void | ||||
|     { | ||||
|         $this->repository        = app(PiggyBankRepositoryInterface::class); | ||||
|         $this->accountRepository = app(AccountRepositoryInterface::class); | ||||
|         $set                     = PiggyBank::whereNotNull('account_id')->get(); | ||||
|         Log::debug(sprintf('Will update %d piggy banks(s).', $set->count())); | ||||
| 
 | ||||
|         /** @var PiggyBank $piggyBank */ | ||||
|         foreach ($set as $piggyBank) { | ||||
|             $this->upgradePiggyBank($piggyBank); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function upgradePiggyBank(PiggyBank $piggyBank): void | ||||
|     { | ||||
|         $this->repository->setUser($piggyBank->account->user); | ||||
|         $this->accountRepository->setUser($piggyBank->account->user); | ||||
|         $repetition                         = $this->repository->getRepetition($piggyBank, true); | ||||
|         $currency                           = $this->accountRepository->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrencyByUserGroup($piggyBank->account->user->userGroup); | ||||
| 
 | ||||
|         // update piggy bank to have a currency.
 | ||||
|         $piggyBank->transaction_currency_id = $currency->id; | ||||
|         $piggyBank->save(); | ||||
| 
 | ||||
|         // store current amount in account association.
 | ||||
|         $piggyBank->accounts()->sync([$piggyBank->account->id => ['current_amount' => $repetition->current_amount]]); | ||||
|         $piggyBank->account_id              = null; | ||||
|         $piggyBank->save(); | ||||
| 
 | ||||
|         // remove all repetitions (no longer used)
 | ||||
|         $piggyBank->piggyBankRepetitions()->delete(); | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @@ -6,6 +6,7 @@ use Illuminate\Console\Command; | ||||
|  | ||||
| /** | ||||
|  * Class UpgradeSkeleton. | ||||
|  * TODO DONT FORGET TO ADD THIS TO THE DOCKER BUILD | ||||
|  */ | ||||
| class UpgradeSkeleton extends Command | ||||
| { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestEmailChannel.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
| /** | ||||
|  * AdminRequestedTestMessage.php | ||||
|  * Copyright (c) 2019 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
| @@ -17,30 +17,31 @@ | ||||
|  * 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); | ||||
| 
 | ||||
| namespace FireflyIII\Events\Test; | ||||
| namespace FireflyIII\Events; | ||||
| 
 | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class UserTestNotificationChannel | ||||
| /** | ||||
|  * Class AdminRequestedTestMessage. | ||||
|  */ | ||||
| class AdminRequestedTestMessage extends Event | ||||
| { | ||||
|     use SerializesModels; | ||||
| 
 | ||||
|     public User   $user; | ||||
|     public string $channel; | ||||
|     public User $user; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      */ | ||||
|     public function __construct(string $channel, User $user) | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         app('log')->debug(sprintf('Triggered UserTestNotificationChannel("%s")', $channel)); | ||||
|         $this->user    = $user; | ||||
|         $this->channel = $channel; | ||||
|         app('log')->debug(sprintf('Triggered AdminRequestedTestMessage for user #%d (%s)', $user->id, $user->email)); | ||||
|         $this->user = $user; | ||||
|     } | ||||
| } | ||||
| @@ -25,7 +25,6 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Events; | ||||
| 
 | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| 
 | ||||
| /** | ||||
|  * Class NewVersionAvailable | ||||
| @@ -41,7 +40,6 @@ class NewVersionAvailable extends Event | ||||
|      */ | ||||
|     public function __construct(string $message) | ||||
|     { | ||||
|         Log::debug(__METHOD__); | ||||
|         $this->message = $message; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,6 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Events; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| @@ -36,14 +35,12 @@ class RegisteredUser extends Event | ||||
|     use SerializesModels; | ||||
| 
 | ||||
|     public User $user; | ||||
|     public OwnerNotifiable $owner; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new event instance. This event is triggered when a new user registers. | ||||
|      */ | ||||
|     public function __construct(OwnerNotifiable $owner, User $user) | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user  = $user; | ||||
|         $this->owner = $owner; | ||||
|         $this->user = $user; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,39 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * UnknownUserAttemptedLogin.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\Events\Security; | ||||
| 
 | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class UnknownUserAttemptedLogin | ||||
| { | ||||
|     use SerializesModels; | ||||
| 
 | ||||
|     public string $address; | ||||
| 
 | ||||
|     public function __construct(string $address) | ||||
|     { | ||||
|         $this->address = $address; | ||||
|     } | ||||
| } | ||||
| @@ -1,44 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * UserAttemptedLogin.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\Events\Security; | ||||
| 
 | ||||
| use FireflyIII\Events\Event; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Contracts\Auth\Authenticatable; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class UserAttemptedLogin extends Event | ||||
| { | ||||
|     use SerializesModels; | ||||
| 
 | ||||
|     public User $user; | ||||
| 
 | ||||
|     public function __construct(null|Authenticatable|User $user) | ||||
|     { | ||||
|         if ($user instanceof User) { | ||||
|             $this->user = $user; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,46 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestEmailChannel.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\Events\Test; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class OwnerTestNotificationChannel | ||||
| { | ||||
|     use SerializesModels; | ||||
| 
 | ||||
|     public OwnerNotifiable $owner; | ||||
|     public string          $channel; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new event instance. | ||||
|      */ | ||||
|     public function __construct(string $channel, OwnerNotifiable $owner) | ||||
|     { | ||||
|         app('log')->debug(sprintf('Triggered OwnerTestNotificationChannel("%s")', $channel)); | ||||
|         $this->owner   = $owner; | ||||
|         $this->channel = $channel; | ||||
|     } | ||||
| } | ||||
| @@ -23,116 +23,27 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Factory; | ||||
| 
 | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; | ||||
| use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Database\QueryException; | ||||
| 
 | ||||
| /** | ||||
|  * Class PiggyBankFactory | ||||
|  */ | ||||
| class PiggyBankFactory | ||||
| { | ||||
|     use CreatesObjectGroups; | ||||
| 
 | ||||
|     public User                          $user { | ||||
|         set(User $value) { | ||||
|             $this->user = $value; | ||||
|             $this->currencyRepository->setUser($value); | ||||
|             $this->accountRepository->setUser($value); | ||||
|             $this->piggyBankRepository->setUser($value); | ||||
|         } | ||||
|     } | ||||
|     private CurrencyRepositoryInterface  $currencyRepository; | ||||
|     private AccountRepositoryInterface   $accountRepository; | ||||
|     private PiggyBankRepositoryInterface $piggyBankRepository; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->currencyRepository  = app(CurrencyRepositoryInterface::class); | ||||
|         $this->accountRepository   = app(AccountRepositoryInterface::class); | ||||
|         $this->piggyBankRepository = app(PiggyBankRepositoryInterface::class); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Store a piggy bank or come back with an exception. | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return PiggyBank | ||||
|      */ | ||||
|     public function store(array $data): PiggyBank | ||||
|     { | ||||
| 
 | ||||
|         $piggyBankData = $data; | ||||
| 
 | ||||
|         // unset some fields
 | ||||
|         unset($piggyBankData['object_group_title'], $piggyBankData['transaction_currency_code'], $piggyBankData['transaction_currency_id'], $piggyBankData['accounts'], $piggyBankData['object_group_id'], $piggyBankData['notes']); | ||||
| 
 | ||||
|         // validate amount:
 | ||||
|         if (array_key_exists('target_amount', $piggyBankData) && '' === (string) $piggyBankData['target_amount']) { | ||||
|             $piggyBankData['target_amount'] = '0'; | ||||
|         } | ||||
| 
 | ||||
|         $piggyBankData['start_date_tz']           = $piggyBankData['start_date']?->format('e'); | ||||
|         $piggyBankData['target_date_tz']          = $piggyBankData['target_date']?->format('e'); | ||||
|         $piggyBankData['account_id']              = null; | ||||
|         $piggyBankData['transaction_currency_id'] = $this->getCurrency($data)->id; | ||||
|         $piggyBankData['order']                   = 131337; | ||||
| 
 | ||||
|         try { | ||||
|             /** @var PiggyBank $piggyBank */ | ||||
|             $piggyBank = PiggyBank::create($piggyBankData); | ||||
|         } catch (QueryException $e) { | ||||
|             app('log')->error(sprintf('Could not store piggy bank: %s', $e->getMessage()), $piggyBankData); | ||||
| 
 | ||||
|             throw new FireflyException('400005: Could not store new piggy bank.', 0, $e); | ||||
|         } | ||||
|         $piggyBank = $this->setOrder($piggyBank, $data); | ||||
|         $this->linkToAccountIds($piggyBank, $data['accounts']); | ||||
|         $this->piggyBankRepository->updateNote($piggyBank, $data['notes']); | ||||
| 
 | ||||
|         $objectGroupTitle = $data['object_group_title'] ?? ''; | ||||
|         if ('' !== $objectGroupTitle) { | ||||
|             $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); | ||||
|             if (null !== $objectGroup) { | ||||
|                 $piggyBank->objectGroups()->sync([$objectGroup->id]); | ||||
|                 $piggyBank->save(); | ||||
|             } | ||||
|         } | ||||
|         // try also with ID
 | ||||
|         $objectGroupId = (int) ($data['object_group_id'] ?? 0); | ||||
|         if (0 !== $objectGroupId) { | ||||
|             $objectGroup = $this->findObjectGroupById($objectGroupId); | ||||
|             if (null !== $objectGroup) { | ||||
|                 $piggyBank->objectGroups()->sync([$objectGroup->id]); | ||||
|                 $piggyBank->save(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $piggyBank; | ||||
|     } | ||||
|     private User $user; | ||||
| 
 | ||||
|     public function find(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank | ||||
|     { | ||||
|         $piggyBankId   = (int) $piggyBankId; | ||||
|         $piggyBankName = (string) $piggyBankName; | ||||
|         $piggyBankId   = (int)$piggyBankId; | ||||
|         $piggyBankName = (string)$piggyBankName; | ||||
|         if ('' === $piggyBankName && 0 === $piggyBankId) { | ||||
|             return null; | ||||
|         } | ||||
|         // first find by ID:
 | ||||
|         if ($piggyBankId > 0) { | ||||
|             $piggyBank = PiggyBank | ||||
|                 ::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') | ||||
|                 ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|                 ->where('accounts.user_id', $this->user->id) | ||||
|                 ->where('piggy_banks.id', $piggyBankId) | ||||
|                 ->first(['piggy_banks.*']); | ||||
|             /** @var null|PiggyBank $piggyBank */ | ||||
|             $piggyBank = $this->user->piggyBanks()->find($piggyBankId); | ||||
|             if (null !== $piggyBank) { | ||||
|                 return $piggyBank; | ||||
|             } | ||||
| @@ -152,91 +63,11 @@ class PiggyBankFactory | ||||
| 
 | ||||
|     public function findByName(string $name): ?PiggyBank | ||||
|     { | ||||
|         return PiggyBank | ||||
|             ::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') | ||||
|             ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|             ->where('accounts.user_id', $this->user->id) | ||||
|             ->where('piggy_banks.name', $name) | ||||
|             ->first(['piggy_banks.*']); | ||||
|         return $this->user->piggyBanks()->where('piggy_banks.name', $name)->first(); | ||||
|     } | ||||
| 
 | ||||
|     private function getCurrency(array $data): TransactionCurrency | ||||
|     public function setUser(User $user): void | ||||
|     { | ||||
|         // currency:
 | ||||
|         $defaultCurrency = app('amount')->getDefaultCurrency(); | ||||
|         $currency        = null; | ||||
|         if (array_key_exists('transaction_currency_code', $data)) { | ||||
|             $currency = $this->currencyRepository->findByCode((string) ($data['transaction_currency_code'] ?? '')); | ||||
|         } | ||||
|         if (array_key_exists('transaction_currency_id', $data)) { | ||||
|             $currency = $this->currencyRepository->find((int) ($data['transaction_currency_id'] ?? 0)); | ||||
|         } | ||||
|         $currency ??= $defaultCurrency; | ||||
|         return $currency; | ||||
|     } | ||||
| 
 | ||||
|     private function setOrder(PiggyBank $piggyBank, array $data): PiggyBank | ||||
|     { | ||||
|         $this->resetOrder(); | ||||
|         $order = $this->getMaxOrder() + 1; | ||||
|         if (array_key_exists('order', $data)) { | ||||
|             $order = $data['order']; | ||||
|         } | ||||
|         $piggyBank->order = $order; | ||||
|         $piggyBank->save(); | ||||
|         return $piggyBank; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function resetOrder(): void | ||||
|     { | ||||
|         // TODO duplicate code
 | ||||
|         $set     = PiggyBank | ||||
|             ::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') | ||||
|             ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|             ->where('accounts.user_id', $this->user->id) | ||||
|             ->with( | ||||
|                 [ | ||||
|                     'objectGroups', | ||||
|                 ] | ||||
|             ) | ||||
|             ->orderBy('piggy_banks.order', 'ASC')->get(['piggy_banks.*']); | ||||
|         $current = 1; | ||||
|         foreach ($set as $piggyBank) { | ||||
|             if ($piggyBank->order !== $current) { | ||||
|                 app('log')->debug(sprintf('Piggy bank #%d ("%s") was at place %d but should be on %d', $piggyBank->id, $piggyBank->name, $piggyBank->order, $current)); | ||||
|                 $piggyBank->order = $current; | ||||
|                 $piggyBank->save(); | ||||
|             } | ||||
|             ++$current; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private function getMaxOrder(): int | ||||
|     { | ||||
|         return (int) $this->piggyBankRepository->getPiggyBanks()->max('order'); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function linkToAccountIds(PiggyBank $piggyBank, array $accounts): void | ||||
|     { | ||||
|         $toBeLinked = []; | ||||
|         /** @var array $info */ | ||||
|         foreach ($accounts as $info) { | ||||
|             $account = $this->accountRepository->find((int) ($info['account_id'] ?? 0)); | ||||
|             if (null === $account) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (array_key_exists('current_amount', $info)) { | ||||
|                 $toBeLinked[$account->id] = ['current_amount' => $info['current_amount']]; | ||||
|                 //$piggyBank->accounts()->syncWithoutDetaching([$account->id => ['current_amount' => $info['current_amount'] ?? '0']]);
 | ||||
|             } | ||||
|             if (!array_key_exists('current_amount', $info)) { | ||||
|                 $toBeLinked[$account->id] = []; | ||||
|                 //$piggyBank->accounts()->syncWithoutDetaching([$account->id]);
 | ||||
|             } | ||||
|         } | ||||
|         $piggyBank->accounts()->sync($toBeLinked); | ||||
|         $this->user = $user; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,18 +24,12 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Handlers\Events; | ||||
| 
 | ||||
| use FireflyIII\Events\Admin\InvitationCreated; | ||||
| use FireflyIII\Events\AdminRequestedTestMessage; | ||||
| use FireflyIII\Events\NewVersionAvailable; | ||||
| use FireflyIII\Events\Security\UnknownUserAttemptedLogin; | ||||
| use FireflyIII\Events\Test\OwnerTestNotificationChannel; | ||||
| use FireflyIII\Notifications\Admin\UnknownUserLoginAttempt; | ||||
| use FireflyIII\Notifications\Admin\TestNotification; | ||||
| use FireflyIII\Notifications\Admin\UserInvitation; | ||||
| use FireflyIII\Notifications\Admin\VersionCheckResult; | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use FireflyIII\Notifications\Test\OwnerTestNotificationEmail; | ||||
| use FireflyIII\Notifications\Test\OwnerTestNotificationNtfy; | ||||
| use FireflyIII\Notifications\Test\OwnerTestNotificationPushover; | ||||
| use FireflyIII\Notifications\Test\OwnerTestNotificationSlack; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use Illuminate\Support\Facades\Notification; | ||||
| 
 | ||||
| /** | ||||
| @@ -43,52 +37,36 @@ use Illuminate\Support\Facades\Notification; | ||||
|  */ | ||||
| class AdminEventHandler | ||||
| { | ||||
|     public function sendLoginAttemptNotification(UnknownUserAttemptedLogin $event): void | ||||
|     { | ||||
|         try { | ||||
|             $owner = new OwnerNotifiable(); | ||||
|             Notification::send($owner, new UnknownUserLoginAttempt($event->address)); | ||||
|         } catch (\Exception $e) { // @phpstan-ignore-line
 | ||||
|             $message = $e->getMessage(); | ||||
|             if (str_contains($message, 'Bcc')) { | ||||
|                 app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             if (str_contains($message, 'RFC 2822')) { | ||||
|                 app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             app('log')->error($e->getMessage()); | ||||
|             app('log')->error($e->getTraceAsString()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function sendInvitationNotification(InvitationCreated $event): void | ||||
|     { | ||||
|         $sendMail = app('fireflyconfig')->get('notification_invite_created', true)->data; | ||||
|         $sendMail   = app('fireflyconfig')->get('notification_invite_created', true)->data; | ||||
|         if (false === $sendMail) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             $owner = new OwnerNotifiable(); | ||||
|             Notification::send($owner, new UserInvitation($owner, $event->invitee)); | ||||
|         } catch (\Exception $e) { // @phpstan-ignore-line
 | ||||
|             $message = $e->getMessage(); | ||||
|             if (str_contains($message, 'Bcc')) { | ||||
|                 app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
|         /** @var UserRepositoryInterface $repository */ | ||||
|         $repository = app(UserRepositoryInterface::class); | ||||
|         $all        = $repository->all(); | ||||
|         foreach ($all as $user) { | ||||
|             if ($repository->hasRole($user, 'owner')) { | ||||
|                 try { | ||||
|                     Notification::send($user, new UserInvitation($event->invitee)); | ||||
|                 } catch (\Exception $e) { // @phpstan-ignore-line
 | ||||
|                     $message = $e->getMessage(); | ||||
|                     if (str_contains($message, 'Bcc')) { | ||||
|                         app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             if (str_contains($message, 'RFC 2822')) { | ||||
|                 app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
|                         return; | ||||
|                     } | ||||
|                     if (str_contains($message, 'RFC 2822')) { | ||||
|                         app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|                         return; | ||||
|                     } | ||||
|                     app('log')->error($e->getMessage()); | ||||
|                     app('log')->error($e->getTraceAsString()); | ||||
|                 } | ||||
|             } | ||||
|             app('log')->error($e->getMessage()); | ||||
|             app('log')->error($e->getTraceAsString()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -97,68 +75,51 @@ class AdminEventHandler | ||||
|      */ | ||||
|     public function sendNewVersion(NewVersionAvailable $event): void | ||||
|     { | ||||
|         $sendMail = app('fireflyconfig')->get('notification_new_version', true)->data; | ||||
|         $sendMail   = app('fireflyconfig')->get('notification_new_version', true)->data; | ||||
|         if (false === $sendMail) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             $owner = new OwnerNotifiable(); | ||||
|             Notification::send($owner, new VersionCheckResult($event->message)); | ||||
|         } catch (\Exception $e) {// @phpstan-ignore-line
 | ||||
|             $message = $e->getMessage(); | ||||
|             if (str_contains($message, 'Bcc')) { | ||||
|                 app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
|         /** @var UserRepositoryInterface $repository */ | ||||
|         $repository = app(UserRepositoryInterface::class); | ||||
|         $all        = $repository->all(); | ||||
|         foreach ($all as $user) { | ||||
|             if ($repository->hasRole($user, 'owner')) { | ||||
|                 try { | ||||
|                     Notification::send($user, new VersionCheckResult($event->message)); | ||||
|                 } catch (\Exception $e) {// @phpstan-ignore-line
 | ||||
|                     $message = $e->getMessage(); | ||||
|                     if (str_contains($message, 'Bcc')) { | ||||
|                         app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             if (str_contains($message, 'RFC 2822')) { | ||||
|                 app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
|                         return; | ||||
|                     } | ||||
|                     if (str_contains($message, 'RFC 2822')) { | ||||
|                         app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|                         return; | ||||
|                     } | ||||
|                     app('log')->error($e->getMessage()); | ||||
|                     app('log')->error($e->getTraceAsString()); | ||||
|                 } | ||||
|             } | ||||
|             app('log')->error($e->getMessage()); | ||||
|             app('log')->error($e->getTraceAsString()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sends a test message to an administrator. | ||||
|      */ | ||||
|     public function sendTestNotification(OwnerTestNotificationChannel $event): void | ||||
|     public function sendTestMessage(AdminRequestedTestMessage $event): void | ||||
|     { | ||||
|         Log::debug(sprintf('Now in sendTestNotification("%s")', $event->channel)); | ||||
|         /** @var UserRepositoryInterface $repository */ | ||||
|         $repository = app(UserRepositoryInterface::class); | ||||
| 
 | ||||
|         switch ($event->channel) { | ||||
|             case 'email': | ||||
|                 $class = OwnerTestNotificationEmail::class; | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'slack': | ||||
|                 $class = OwnerTestNotificationSlack::class; | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'ntfy': | ||||
|                 $class = OwnerTestNotificationNtfy::class; | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'pushover': | ||||
|                 $class = OwnerTestNotificationPushover::class; | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 app('log')->error(sprintf('Unknown channel "%s" in sendTestNotification method.', $event->channel)); | ||||
| 
 | ||||
|                 return; | ||||
|         if (!$repository->hasRole($event->user, 'owner')) { | ||||
|             return; | ||||
|         } | ||||
|         Log::debug(sprintf('Will send %s as a notification.', $class)); | ||||
| 
 | ||||
|         try { | ||||
|             Notification::send($event->owner, new $class($event->owner)); | ||||
|             Notification::send($event->user, new TestNotification($event->user->email)); | ||||
|         } catch (\Exception $e) { // @phpstan-ignore-line
 | ||||
|             $message = $e->getMessage(); | ||||
|             if (str_contains($message, 'Bcc')) { | ||||
| @@ -174,6 +135,5 @@ class AdminEventHandler | ||||
|             app('log')->error($e->getMessage()); | ||||
|             app('log')->error($e->getTraceAsString()); | ||||
|         } | ||||
|         Log::debug(sprintf('If you see no errors above this line, test notification was sent over channel "%s"', $event->channel)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -31,8 +31,6 @@ use FireflyIII\Events\Admin\InvitationCreated; | ||||
| use FireflyIII\Events\DetectedNewIPAddress; | ||||
| use FireflyIII\Events\RegisteredUser; | ||||
| use FireflyIII\Events\RequestedNewPassword; | ||||
| use FireflyIII\Events\Security\UserAttemptedLogin; | ||||
| use FireflyIII\Events\Test\UserTestNotificationChannel; | ||||
| use FireflyIII\Events\UserChangedEmail; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Mail\ConfirmEmailChangeMail; | ||||
| @@ -42,18 +40,12 @@ use FireflyIII\Models\GroupMembership; | ||||
| use FireflyIII\Models\UserGroup; | ||||
| use FireflyIII\Models\UserRole; | ||||
| use FireflyIII\Notifications\Admin\UserRegistration as AdminRegistrationNotification; | ||||
| use FireflyIII\Notifications\Security\UserFailedLoginAttempt; | ||||
| use FireflyIII\Notifications\Test\UserTestNotificationEmail; | ||||
| use FireflyIII\Notifications\Test\UserTestNotificationNtfy; | ||||
| use FireflyIII\Notifications\Test\UserTestNotificationPushover; | ||||
| use FireflyIII\Notifications\Test\UserTestNotificationSlack; | ||||
| use FireflyIII\Notifications\User\UserLogin; | ||||
| use FireflyIII\Notifications\User\UserNewPassword; | ||||
| use FireflyIII\Notifications\User\UserRegistration as UserRegistrationNotification; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Auth\Events\Login; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Support\Facades\Notification; | ||||
| use Mail; | ||||
| 
 | ||||
| @@ -228,26 +220,31 @@ class UserEventHandler | ||||
| 
 | ||||
|     public function sendAdminRegistrationNotification(RegisteredUser $event): void | ||||
|     { | ||||
|         $sendMail = (bool) app('fireflyconfig')->get('notification_admin_new_reg', true)->data; | ||||
|         $sendMail = (bool)app('fireflyconfig')->get('notification_admin_new_reg', true)->data; | ||||
|         if ($sendMail) { | ||||
|             $owner = $event->owner; | ||||
|             /** @var UserRepositoryInterface $repository */ | ||||
|             $repository = app(UserRepositoryInterface::class); | ||||
|             $all        = $repository->all(); | ||||
|             foreach ($all as $user) { | ||||
|                 if ($repository->hasRole($user, 'owner')) { | ||||
|                     try { | ||||
|                         Notification::send($user, new AdminRegistrationNotification($event->user)); | ||||
|                     } catch (\Exception $e) { // @phpstan-ignore-line
 | ||||
|                         $message = $e->getMessage(); | ||||
|                         if (str_contains($message, 'Bcc')) { | ||||
|                             app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|             try { | ||||
|                 Notification::send($owner, new AdminRegistrationNotification($event->owner, $event->user)); | ||||
|             } catch (\Exception $e) { // @phpstan-ignore-line
 | ||||
|                 $message = $e->getMessage(); | ||||
|                 if (str_contains($message, 'Bcc')) { | ||||
|                     app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
|                             return; | ||||
|                         } | ||||
|                         if (str_contains($message, 'RFC 2822')) { | ||||
|                             app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                     return; | ||||
|                             return; | ||||
|                         } | ||||
|                         app('log')->error($e->getMessage()); | ||||
|                         app('log')->error($e->getTraceAsString()); | ||||
|                     } | ||||
|                 } | ||||
|                 if (str_contains($message, 'RFC 2822')) { | ||||
|                     app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
|                 app('log')->error($e->getMessage()); | ||||
|                 app('log')->error($e->getTraceAsString()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -288,7 +285,7 @@ class UserEventHandler | ||||
|         $oldEmail = $event->oldEmail; | ||||
|         $user     = $event->user; | ||||
|         $token    = app('preferences')->getForUser($user, 'email_change_undo_token', 'invalid'); | ||||
|         $hashed   = hash('sha256', sprintf('%s%s', (string) config('app.key'), $oldEmail)); | ||||
|         $hashed   = hash('sha256', sprintf('%s%s', (string)config('app.key'), $oldEmail)); | ||||
|         $url      = route('profile.undo-email-change', [$token->data, $hashed]); | ||||
| 
 | ||||
|         try { | ||||
| @@ -350,7 +347,7 @@ class UserEventHandler | ||||
|      */ | ||||
|     public function sendRegistrationMail(RegisteredUser $event): void | ||||
|     { | ||||
|         $sendMail = (bool) app('fireflyconfig')->get('notification_user_new_reg', true)->data; | ||||
|         $sendMail = (bool)app('fireflyconfig')->get('notification_user_new_reg', true)->data; | ||||
|         if ($sendMail) { | ||||
|             try { | ||||
|                 Notification::send($event->user, new UserRegistrationNotification()); | ||||
| @@ -431,80 +428,4 @@ class UserEventHandler | ||||
|             event(new DetectedNewIPAddress($user, $ip)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function sendLoginAttemptNotification(UserAttemptedLogin $event): void | ||||
|     { | ||||
|         try { | ||||
|             Notification::send($event->user, new UserFailedLoginAttempt($event->user)); | ||||
|         } catch (\Exception $e) { // @phpstan-ignore-line
 | ||||
|             $message = $e->getMessage(); | ||||
|             if (str_contains($message, 'Bcc')) { | ||||
|                 app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             if (str_contains($message, 'RFC 2822')) { | ||||
|                 app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             app('log')->error($e->getMessage()); | ||||
|             app('log')->error($e->getTraceAsString()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sends a test message to an administrator. | ||||
|      */ | ||||
|     public function sendTestNotification(UserTestNotificationChannel $event): void | ||||
|     { | ||||
|         Log::debug(sprintf('Now in (user) sendTestNotification("%s")', $event->channel)); | ||||
| 
 | ||||
|         switch ($event->channel) { | ||||
|             case 'email': | ||||
|                 $class = UserTestNotificationEmail::class; | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'slack': | ||||
|                 $class = UserTestNotificationSlack::class; | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'ntfy': | ||||
|                 $class = UserTestNotificationNtfy::class; | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'pushover': | ||||
|                 $class = UserTestNotificationPushover::class; | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 app('log')->error(sprintf('Unknown channel "%s" in (user) sendTestNotification method.', $event->channel)); | ||||
| 
 | ||||
|                 return; | ||||
|         } | ||||
|         Log::debug(sprintf('Will send %s as a notification.', $class)); | ||||
| 
 | ||||
|         try { | ||||
|             Notification::send($event->user, new $class($event->user)); | ||||
|         } catch (\Exception $e) { // @phpstan-ignore-line
 | ||||
|             $message = $e->getMessage(); | ||||
|             if (str_contains($message, 'Bcc')) { | ||||
|                 app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             if (str_contains($message, 'RFC 2822')) { | ||||
|                 app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             app('log')->error($e->getMessage()); | ||||
|             app('log')->error($e->getTraceAsString()); | ||||
|         } | ||||
|         Log::debug(sprintf('If you see no errors above this line, test notification was sent over channel "%s"', $event->channel)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,7 +25,6 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Handlers\Observer; | ||||
| 
 | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| 
 | ||||
| /** | ||||
|  * Class AccountObserver | ||||
| @@ -39,10 +38,8 @@ class AccountObserver | ||||
|     { | ||||
|         app('log')->debug('Observe "deleting" of an account.'); | ||||
|         $account->accountMeta()->delete(); | ||||
| 
 | ||||
|         /** @var PiggyBank $piggy */ | ||||
|         foreach ($account->piggyBanks()->get() as $piggy) { | ||||
|             $piggy->accounts()->detach($account); | ||||
|             $piggy->delete(); | ||||
|         } | ||||
|         foreach ($account->attachments()->get() as $attachment) { | ||||
|             $attachment->delete(); | ||||
|   | ||||
| @@ -34,16 +34,15 @@ class PiggyBankObserver | ||||
| { | ||||
|     public function created(PiggyBank $piggyBank): void | ||||
|     { | ||||
|         app('log')->debug('Observe "created" of a piggy bank. DO NOTHING.'); | ||||
| 
 | ||||
|         //        $repetition                = new PiggyBankRepetition();
 | ||||
|         //        $repetition->piggyBank()->associate($piggyBank);
 | ||||
|         //        $repetition->start_date     = $piggyBank->start_date;
 | ||||
|         //        $repetition->start_date_tz  = $piggyBank->start_date->format('e');
 | ||||
|         //        $repetition->target_date    = $piggyBank->target_date;
 | ||||
|         //        $repetition->target_date_tz = $piggyBank->target_date?->format('e');
 | ||||
|         //        $repetition->current_amount = '0';
 | ||||
|         //        $repetition->save();
 | ||||
|         app('log')->debug('Observe "created" of a piggy bank.'); | ||||
|         $repetition                = new PiggyBankRepetition(); | ||||
|         $repetition->piggyBank()->associate($piggyBank); | ||||
|         $repetition->startdate     = $piggyBank->startdate; | ||||
|         $repetition->startdate_tz  = $piggyBank->startdate->format('e'); | ||||
|         $repetition->targetdate    = $piggyBank->targetdate; | ||||
|         $repetition->targetdate_tz = $piggyBank->targetdate?->format('e'); | ||||
|         $repetition->currentamount = '0'; | ||||
|         $repetition->save(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -41,18 +41,22 @@ class TransactionObserver | ||||
|     public function updated(Transaction $transaction): void | ||||
|     { | ||||
|         Log::debug('Observe "updated" of a transaction.'); | ||||
|         if (1 === bccomp($transaction->amount, '0')) { | ||||
|             Log::debug('Trigger recalculateForJournal'); | ||||
|             AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal); | ||||
|         if (config('firefly.feature_flags.running_balance_column')) { | ||||
|             if (1 === bccomp($transaction->amount, '0')) { | ||||
|                 Log::debug('Trigger recalculateForJournal'); | ||||
|                 AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function created(Transaction $transaction): void | ||||
|     { | ||||
|         Log::debug('Observe "created" of a transaction.'); | ||||
|         if (1 === bccomp($transaction->amount, '0')) { | ||||
|             Log::debug('Trigger recalculateForJournal'); | ||||
|             AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal); | ||||
|         if (config('firefly.feature_flags.running_balance_column')) { | ||||
|             if (1 === bccomp($transaction->amount, '0')) { | ||||
|                 Log::debug('Trigger recalculateForJournal'); | ||||
|                 AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,9 +23,15 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Http\Controllers\Admin; | ||||
| 
 | ||||
| use FireflyIII\Events\AdminRequestedTestMessage; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Http\Middleware\IsDemoUser; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Routing\Redirector; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\View\View; | ||||
| 
 | ||||
| @@ -59,6 +65,53 @@ class HomeController extends Controller | ||||
|             $email = $pref->data; | ||||
|         } | ||||
| 
 | ||||
|         return view('admin.index', compact('title', 'mainTitleIcon', 'email')); | ||||
|         // admin notification settings:
 | ||||
|         $notifications = []; | ||||
|         foreach (config('firefly.admin_notifications') as $item) { | ||||
|             $notifications[$item] = app('fireflyconfig')->get(sprintf('notification_%s', $item), true)->data; | ||||
|         } | ||||
|         $slackUrl      = app('fireflyconfig')->get('slack_webhook_url', '')->data; | ||||
| 
 | ||||
|         return view('admin.index', compact('title', 'mainTitleIcon', 'email', 'notifications', 'slackUrl')); | ||||
|     } | ||||
| 
 | ||||
|     public function notifications(Request $request): RedirectResponse | ||||
|     { | ||||
|         foreach (config('firefly.admin_notifications') as $item) { | ||||
|             $value = false; | ||||
|             if ($request->has(sprintf('notification_%s', $item))) { | ||||
|                 $value = true; | ||||
|             } | ||||
|             app('fireflyconfig')->set(sprintf('notification_%s', $item), $value); | ||||
|         } | ||||
|         $url = (string)$request->get('slackUrl'); | ||||
|         if ('' === $url) { | ||||
|             app('fireflyconfig')->delete('slack_webhook_url'); | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL($url)) { | ||||
|             app('fireflyconfig')->set('slack_webhook_url', $url); | ||||
|         } | ||||
| 
 | ||||
|         session()->flash('success', (string)trans('firefly.notification_settings_saved')); | ||||
| 
 | ||||
|         return redirect(route('admin.index')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send a test message to the admin. | ||||
|      * | ||||
|      * @return Redirector|RedirectResponse | ||||
|      */ | ||||
|     public function testMessage() | ||||
|     { | ||||
|         Log::channel('audit')->info('User sends test message.'); | ||||
| 
 | ||||
|         /** @var User $user */ | ||||
|         $user = auth()->user(); | ||||
|         app('log')->debug('Now in testMessage() controller.'); | ||||
|         event(new AdminRequestedTestMessage($user)); | ||||
|         session()->flash('info', (string)trans('firefly.send_test_triggered')); | ||||
| 
 | ||||
|         return redirect(route('admin.index')); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,144 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * NotificationController.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\Admin; | ||||
| 
 | ||||
| use FireflyIII\Events\Test\OwnerTestNotificationChannel; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Http\Requests\NotificationRequest; | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| 
 | ||||
| class NotificationController extends Controller | ||||
| { | ||||
|     public function index() | ||||
|     { | ||||
|         Log::channel('audit')->info('User visits notifications index.'); | ||||
|         $title                          = (string) trans('firefly.administration'); | ||||
|         $mainTitleIcon                  = 'fa-hand-spock-o'; | ||||
|         $subTitle                       = (string) trans('firefly.title_owner_notifications'); | ||||
|         $subTitleIcon                   = 'envelope-o'; | ||||
| 
 | ||||
|         // notification settings:
 | ||||
|         $slackUrl                       = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data; | ||||
|         $pushoverAppToken               = app('fireflyconfig')->getEncrypted('pushover_app_token', '')->data; | ||||
|         $pushoverUserToken              = app('fireflyconfig')->getEncrypted('pushover_user_token', '')->data; | ||||
|         $ntfyServer                     = app('fireflyconfig')->getEncrypted('ntfy_server', 'https://ntfy.sh')->data; | ||||
|         $ntfyTopic                      = app('fireflyconfig')->getEncrypted('ntfy_topic', '')->data; | ||||
|         $ntfyAuth                       = app('fireflyconfig')->get('ntfy_auth', false)->data; | ||||
|         $ntfyUser                       = app('fireflyconfig')->getEncrypted('ntfy_user', '')->data; | ||||
|         $ntfyPass                       = app('fireflyconfig')->getEncrypted('ntfy_pass', '')->data; | ||||
|         $channels                       = config('notifications.channels'); | ||||
|         $forcedAvailability             = []; | ||||
| 
 | ||||
|         // admin notification settings:
 | ||||
|         $notifications                  = []; | ||||
|         foreach (config('notifications.notifications.owner') as $key => $info) { | ||||
|             if ($info['enabled']) { | ||||
|                 $notifications[$key] = app('fireflyconfig')->get(sprintf('notification_%s', $key), true)->data; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // loop all channels to see if they are available.
 | ||||
|         foreach ($channels as $channel => $info) { | ||||
|             $forcedAvailability[$channel] = true; | ||||
|         } | ||||
|         $forcedAvailability['ntfy']     = '' !== $ntfyTopic; | ||||
|         $forcedAvailability['pushover'] = '' !== $pushoverAppToken && '' !== $pushoverUserToken; | ||||
| 
 | ||||
|         return view( | ||||
|             'admin.notifications.index', | ||||
|             compact( | ||||
|                 'title', | ||||
|                 'subTitle', | ||||
|                 'forcedAvailability', | ||||
|                 'mainTitleIcon', | ||||
|                 'subTitleIcon', | ||||
|                 'channels', | ||||
|                 'slackUrl', | ||||
|                 'notifications', | ||||
|                 'pushoverAppToken', | ||||
|                 'pushoverUserToken', | ||||
|                 'ntfyServer', | ||||
|                 'ntfyTopic', | ||||
|                 'ntfyAuth', | ||||
|                 'ntfyUser', | ||||
|                 'ntfyPass' | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public function postIndex(NotificationRequest $request): RedirectResponse | ||||
|     { | ||||
|         $all       = $request->getAll(); | ||||
| 
 | ||||
|         foreach (config('notifications.notifications.owner') as $key => $info) { | ||||
|             if (array_key_exists($key, $all)) { | ||||
|                 app('fireflyconfig')->set(sprintf('notification_%s', $key), $all[$key]); | ||||
|             } | ||||
|         } | ||||
|         $variables = ['slack_webhook_url', 'pushover_app_token', 'pushover_user_token', 'ntfy_server', 'ntfy_topic', 'ntfy_user', 'ntfy_pass']; | ||||
|         foreach ($variables as $variable) { | ||||
|             if ('' === $all[$variable]) { | ||||
|                 app('fireflyconfig')->delete($variable); | ||||
|             } | ||||
|             if ('' !== $all[$variable]) { | ||||
|                 app('fireflyconfig')->setEncrypted($variable, $all[$variable]); | ||||
|             } | ||||
|         } | ||||
|         app('fireflyconfig')->set('ntfy_auth', $all['ntfy_auth'] ?? false); | ||||
| 
 | ||||
| 
 | ||||
|         session()->flash('success', (string) trans('firefly.notification_settings_saved')); | ||||
| 
 | ||||
|         return redirect(route('admin.notification.index')); | ||||
|     } | ||||
| 
 | ||||
|     public function testNotification(Request $request): RedirectResponse | ||||
|     { | ||||
| 
 | ||||
|         $all     = $request->all(); | ||||
|         $channel = $all['test_submit'] ?? ''; | ||||
| 
 | ||||
|         switch ($channel) { | ||||
|             default: | ||||
|                 session()->flash('error', (string) trans('firefly.notification_test_failed', ['channel' => $channel])); | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'email': | ||||
|             case 'slack': | ||||
|             case 'pushover': | ||||
|             case 'ntfy': | ||||
|                 $owner = new OwnerNotifiable(); | ||||
|                 app('log')->debug(sprintf('Now in testNotification("%s") controller.', $channel)); | ||||
|                 event(new OwnerTestNotificationChannel($channel, $owner)); | ||||
|                 session()->flash('success', (string) trans('firefly.notification_test_executed', ['channel' => $channel])); | ||||
|         } | ||||
| 
 | ||||
|         return redirect(route('admin.notification.index')); | ||||
|     } | ||||
| } | ||||
| @@ -25,12 +25,9 @@ namespace FireflyIII\Http\Controllers\Auth; | ||||
| 
 | ||||
| use Cookie; | ||||
| use FireflyIII\Events\ActuallyLoggedIn; | ||||
| use FireflyIII\Events\Security\UnknownUserAttemptedLogin; | ||||
| use FireflyIII\Events\Security\UserAttemptedLogin; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Providers\RouteServiceProvider; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use Illuminate\Contracts\Foundation\Application; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Contracts\View\View; | ||||
| @@ -60,7 +57,6 @@ class LoginController extends Controller | ||||
|      * Where to redirect users after login. | ||||
|      */ | ||||
|     protected string $redirectTo = RouteServiceProvider::HOME; | ||||
|     private UserRepositoryInterface $repository; | ||||
| 
 | ||||
|     private string $username; | ||||
| 
 | ||||
| @@ -70,9 +66,8 @@ class LoginController extends Controller | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->username   = 'email'; | ||||
|         $this->username = 'email'; | ||||
|         $this->middleware('guest')->except('logout'); | ||||
|         $this->repository = app(UserRepositoryInterface::class); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -127,15 +122,6 @@ class LoginController extends Controller | ||||
|             return $this->sendLoginResponse($request); | ||||
|         } | ||||
|         app('log')->warning('Login attempt failed.'); | ||||
|         $username = (string) $request->get($this->username()); | ||||
|         $user     = $this->repository->findByEmail($username); | ||||
|         if (null === $user) { | ||||
|             // send event to owner.
 | ||||
|             event(new UnknownUserAttemptedLogin($username)); | ||||
|         } | ||||
|         if (null !== $user) { | ||||
|             event(new UserAttemptedLogin($user)); | ||||
|         } | ||||
| 
 | ||||
|         // Copied directly from AuthenticatesUsers, but with logging added:
 | ||||
|         // If the login attempt was unsuccessful we will increment the number of attempts
 | ||||
|   | ||||
| @@ -26,7 +26,6 @@ namespace FireflyIII\Http\Controllers\Auth; | ||||
| use FireflyIII\Events\RegisteredUser; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use FireflyIII\Support\Http\Controllers\CreateStuff; | ||||
| use FireflyIII\User; | ||||
| @@ -95,8 +94,7 @@ class RegisterController extends Controller | ||||
|         $this->validator($request->all())->validate(); | ||||
|         $user              = $this->createUser($request->all()); | ||||
|         app('log')->info(sprintf('Registered new user %s', $user->email)); | ||||
|         $owner             = new OwnerNotifiable(); | ||||
|         event(new RegisteredUser($owner, $user)); | ||||
|         event(new RegisteredUser($user)); | ||||
| 
 | ||||
|         $this->guard()->login($user); | ||||
| 
 | ||||
|   | ||||
| @@ -63,7 +63,7 @@ class BudgetLimitController extends Controller | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 app('view')->share('title', (string) trans('firefly.budgets')); | ||||
|                 app('view')->share('title', (string)trans('firefly.budgets')); | ||||
|                 app('view')->share('mainTitleIcon', 'fa-pie-chart'); | ||||
|                 $this->repository    = app(BudgetRepositoryInterface::class); | ||||
|                 $this->opsRepository = app(OperationsRepositoryInterface::class); | ||||
| @@ -101,26 +101,6 @@ class BudgetLimitController extends Controller | ||||
|         return view('budgets.budget-limits.create', compact('start', 'end', 'currencies', 'budget')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Factory|View | ||||
|      */ | ||||
|     public function show(BudgetLimit $budgetLimit) | ||||
|     { | ||||
|         $notes = $this->blRepository->getNoteText($budgetLimit); | ||||
| 
 | ||||
|         return view('budgets.budget-limits.show', compact('budgetLimit', 'notes')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Factory|View | ||||
|      */ | ||||
|     public function edit(BudgetLimit $budgetLimit) | ||||
|     { | ||||
|         $notes = $this->blRepository->getNoteText($budgetLimit); | ||||
| 
 | ||||
|         return view('budgets.budget-limits.edit', compact('budgetLimit', 'notes')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Redirector|RedirectResponse | ||||
|      */ | ||||
| @@ -141,8 +121,8 @@ class BudgetLimitController extends Controller | ||||
|     { | ||||
|         app('log')->debug('Going to store new budget-limit.', $request->all()); | ||||
|         // first search for existing one and update it if necessary.
 | ||||
|         $currency = $this->currencyRepos->find((int) $request->get('transaction_currency_id')); | ||||
|         $budget   = $this->repository->find((int) $request->get('budget_id')); | ||||
|         $currency = $this->currencyRepos->find((int)$request->get('transaction_currency_id')); | ||||
|         $budget   = $this->repository->find((int)$request->get('budget_id')); | ||||
|         if (null === $currency || null === $budget) { | ||||
|             throw new FireflyException('No valid currency or budget.'); | ||||
|         } | ||||
| @@ -153,7 +133,7 @@ class BudgetLimitController extends Controller | ||||
|             return response()->json([]); | ||||
|         } | ||||
| 
 | ||||
|         $amount   = (string) $request->get('amount'); | ||||
|         $amount   = (string)$request->get('amount'); | ||||
|         $start->startOfDay(); | ||||
|         $end->startOfDay(); | ||||
| 
 | ||||
| @@ -174,7 +154,7 @@ class BudgetLimitController extends Controller | ||||
|             // return empty=ish array:
 | ||||
|             return response()->json([]); | ||||
|         } | ||||
|         if ((int) $amount > 268435456) { // intentional cast to integer
 | ||||
|         if ((int)$amount > 268435456) { // intentional cast to integer
 | ||||
|             $amount = '268435456'; | ||||
|         } | ||||
|         if (-1 === bccomp($amount, '0')) { | ||||
| @@ -189,16 +169,13 @@ class BudgetLimitController extends Controller | ||||
|             $limit = $this->blRepository->store( | ||||
|                 [ | ||||
|                     'budget_id'   => $request->get('budget_id'), | ||||
|                     'currency_id' => (int) $request->get('transaction_currency_id'), | ||||
|                     'currency_id' => (int)$request->get('transaction_currency_id'), | ||||
|                     'start_date'  => $start, | ||||
|                     'end_date'    => $end, | ||||
|                     'amount'      => $amount, | ||||
|                 ] | ||||
|             ); | ||||
|         } | ||||
|         // parse notes, if any.
 | ||||
|         $notes    = (string) $request->get('notes'); | ||||
|         $this->blRepository->setNoteText($limit, $notes); | ||||
| 
 | ||||
|         if ($request->expectsJson()) { | ||||
|             $array                           = $limit->toArray(); | ||||
| @@ -207,29 +184,26 @@ class BudgetLimitController extends Controller | ||||
|             $array['spent']                  = $spentArr[$currency->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($start, $end); | ||||
|             $array['days_left']              = (string)$this->activeDaysLeft($start, $end); | ||||
|             // left per day:
 | ||||
|             $array['left_per_day']           = 0 === bccomp('0', $array['days_left']) ? bcadd($array['spent'], $array['amount']) : bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']); | ||||
| 
 | ||||
|             // left per day formatted.
 | ||||
|             $array['left_per_day_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $array['left_per_day']); | ||||
| 
 | ||||
|             // notes:
 | ||||
|             $array['notes']                  = $this->blRepository->getNoteText($limit); | ||||
| 
 | ||||
|             return response()->json($array); | ||||
|         } | ||||
| 
 | ||||
|         return redirect(route('budgets.index')); | ||||
|     } | ||||
| 
 | ||||
|     public function update(Request $request, BudgetLimit $budgetLimit): JsonResponse|RedirectResponse | ||||
|     public function update(Request $request, BudgetLimit $budgetLimit): JsonResponse | ||||
|     { | ||||
|         $amount                          = (string) $request->get('amount'); | ||||
|         $amount                          = (string)$request->get('amount'); | ||||
|         if ('' === $amount) { | ||||
|             $amount = '0'; | ||||
|         } | ||||
|         if ((int) $amount > 268435456) { // 268 million, intentional integer
 | ||||
|         if ((int)$amount > 268435456) { // 268 million, intentional integer
 | ||||
|             $amount = '268435456'; | ||||
|         } | ||||
|         // sanity check on amount:
 | ||||
| @@ -250,13 +224,8 @@ class BudgetLimitController extends Controller | ||||
|         if (-1 === bccomp($amount, '0')) { | ||||
|             $amount = bcmul($amount, '-1'); | ||||
|         } | ||||
|         $notes                           = (string)$request->get('notes'); | ||||
|         if (strlen($notes) > 32768) { | ||||
|             $notes = substr($notes, 0, 32768); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         $limit                           = $this->blRepository->update($budgetLimit, ['amount' => $amount, 'notes' => $notes]); | ||||
|         $limit                           = $this->blRepository->update($budgetLimit, ['amount' => $amount]); | ||||
|         app('preferences')->mark(); | ||||
|         $array                           = $limit->toArray(); | ||||
| 
 | ||||
| @@ -271,15 +240,12 @@ class BudgetLimitController extends Controller | ||||
|         $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) $daysLeft; | ||||
|         $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); | ||||
|         $array['left_per_day_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $array['left_per_day']); | ||||
|         if ('true' === $request->get('redirect')) { | ||||
|             return redirect(route('budgets.index')); | ||||
|         } | ||||
| 
 | ||||
|         return response()->json($array); | ||||
|     } | ||||
|   | ||||
| @@ -213,7 +213,6 @@ class IndexController extends Controller | ||||
|                 $array['budgeted'][] = [ | ||||
|                     'id'                      => $limit->id, | ||||
|                     'amount'                  => $amount, | ||||
|                     'notes'                   => $this->blRepository->getNoteText($limit), | ||||
|                     'start_date'              => $limit->start_date->isoFormat($this->monthAndDayFormat), | ||||
|                     'end_date'                => $limit->end_date->isoFormat($this->monthAndDayFormat), | ||||
|                     'in_range'                => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end), | ||||
|   | ||||
| @@ -75,7 +75,7 @@ class PiggyBankController extends Controller | ||||
|         $locale                 = app('steam')->getLocale(); | ||||
| 
 | ||||
|         // get first event or start date of piggy bank or today
 | ||||
|         $startDate              = $piggyBank->start_date ?? today(config('app.timezone')); | ||||
|         $startDate              = $piggyBank->startdate ?? today(config('app.timezone')); | ||||
| 
 | ||||
|         /** @var null|PiggyBankEvent $firstEvent */ | ||||
|         $firstEvent             = $set->first(); | ||||
|   | ||||
| @@ -44,9 +44,6 @@ abstract class Controller extends BaseController | ||||
|     use UserNavigation; | ||||
|     use ValidatesRequests; | ||||
| 
 | ||||
|     // fails on PHP < 8.4
 | ||||
|     public protected(set) string $name; | ||||
| 
 | ||||
|     protected string $dateTimeFormat; | ||||
|     protected string $monthAndDayFormat; | ||||
|     protected string $monthFormat; | ||||
|   | ||||
| @@ -30,7 +30,6 @@ use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Http\Middleware\IsDemoUser; | ||||
| use FireflyIII\Models\AccountType; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| use FireflyIII\Support\Http\Controllers\GetConfigurationData; | ||||
| use FireflyIII\Support\Models\AccountBalanceCalculator; | ||||
| use FireflyIII\User; | ||||
| @@ -281,10 +280,10 @@ class DebugController extends Controller | ||||
| 
 | ||||
|     private function getUserFlags(): string | ||||
|     { | ||||
|         $flags      = []; | ||||
|         $flags = []; | ||||
| 
 | ||||
|         /** @var User $user */ | ||||
|         $user       = auth()->user(); | ||||
|         $user  = auth()->user(); | ||||
| 
 | ||||
|         // has liabilities
 | ||||
|         if ($user->accounts()->accountTypeIn([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE])->count() > 0) { | ||||
| @@ -292,15 +291,12 @@ class DebugController extends Controller | ||||
|         } | ||||
| 
 | ||||
|         // has piggies
 | ||||
|         $repository = app(PiggyBankRepositoryInterface::class); | ||||
|         $repository->setUser($user); | ||||
| 
 | ||||
|         if ($repository->getPiggyBanks()->count() > 0) { | ||||
|         if ($user->piggyBanks()->count() > 0) { | ||||
|             $flags[] = '<span title="Has piggy banks">:pig:</span>'; | ||||
|         } | ||||
| 
 | ||||
|         // has stored reconciliations
 | ||||
|         $type       = TransactionType::whereType(TransactionType::RECONCILIATION)->first(); | ||||
|         $type  = TransactionType::whereType(TransactionType::RECONCILIATION)->first(); | ||||
|         if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count() > 0) { | ||||
|             $flags[] = '<span title="Has reconciled">:ledger:</span>'; | ||||
|         } | ||||
|   | ||||
| @@ -120,7 +120,6 @@ class HomeController extends Controller | ||||
|      */ | ||||
|     public function index(AccountRepositoryInterface $repository): mixed | ||||
|     { | ||||
| 
 | ||||
|         $types = config('firefly.accountTypesByIdentifier.asset'); | ||||
|         $count = $repository->count($types); | ||||
|         Log::channel('audit')->info('User visits homepage.'); | ||||
|   | ||||
| @@ -50,15 +50,15 @@ class FrontpageController extends Controller | ||||
|             if (1 === bccomp($amount, '0')) { | ||||
|                 // percentage!
 | ||||
|                 $pct    = 0; | ||||
|                 if (0 !== bccomp($piggyBank->target_amount, '0')) { | ||||
|                     $pct = (int)bcmul(bcdiv($amount, $piggyBank->target_amount), '100'); | ||||
|                 if (0 !== bccomp($piggyBank->targetamount, '0')) { | ||||
|                     $pct = (int)bcmul(bcdiv($amount, $piggyBank->targetamount), '100'); | ||||
|                 } | ||||
| 
 | ||||
|                 $entry  = [ | ||||
|                     'id'         => $piggyBank->id, | ||||
|                     'name'       => $piggyBank->name, | ||||
|                     'amount'     => $amount, | ||||
|                     'target'     => $piggyBank->target_amount, | ||||
|                     'target'     => $piggyBank->targetamount, | ||||
|                     'percentage' => $pct, | ||||
|                 ]; | ||||
| 
 | ||||
|   | ||||
| @@ -26,14 +26,12 @@ namespace FireflyIII\Http\Controllers\PiggyBank; | ||||
| 
 | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\View\View; | ||||
| 
 | ||||
| /** | ||||
| @@ -53,7 +51,7 @@ class AmountController extends Controller | ||||
| 
 | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 app('view')->share('title', (string) trans('firefly.piggyBanks')); | ||||
|                 app('view')->share('title', (string)trans('firefly.piggyBanks')); | ||||
|                 app('view')->share('mainTitleIcon', 'fa-bullseye'); | ||||
| 
 | ||||
|                 $this->piggyRepos   = app(PiggyBankRepositoryInterface::class); | ||||
| @@ -71,26 +69,16 @@ class AmountController extends Controller | ||||
|      */ | ||||
|     public function add(PiggyBank $piggyBank) | ||||
|     { | ||||
|         $accounts   = []; | ||||
|         $total      = '0'; | ||||
|         $totalSaved = $this->piggyRepos->getCurrentAmount($piggyBank); | ||||
|         $leftToSave = bcsub($piggyBank->target_amount, $totalSaved); | ||||
|         foreach ($piggyBank->accounts as $account) { | ||||
|             $leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $account, today(config('app.timezone'))->endOfDay()); | ||||
|             $savedSoFar    = $this->piggyRepos->getCurrentAmount($piggyBank, $account); | ||||
|             $maxAmount     = 0 === bccomp($piggyBank->target_amount, '0') ? $leftToSave : min($leftOnAccount, $leftToSave); | ||||
|             $accounts[]    = [ | ||||
|                 'account'         => $account, | ||||
|                 'left_on_account' => $leftOnAccount, | ||||
|                 'saved_so_far'    => $savedSoFar, | ||||
|                 'left_to_save'    => $leftToSave, | ||||
|                 'max_amount'      => $maxAmount, | ||||
|             ]; | ||||
|             $total         = bcadd($total, $leftOnAccount); | ||||
|         $leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, today(config('app.timezone'))); | ||||
|         $savedSoFar    = $this->piggyRepos->getCurrentAmount($piggyBank); | ||||
|         $maxAmount     = $leftOnAccount; | ||||
|         if (0 !== bccomp($piggyBank->targetamount, '0')) { | ||||
|             $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar); | ||||
|             $maxAmount  = min($leftOnAccount, $leftToSave); | ||||
|         } | ||||
|         $total      = (float) $total; // intentional float.
 | ||||
|         $currency      = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|         return view('piggy-banks.add', compact('piggyBank', 'accounts', 'total')); | ||||
|         return view('piggy-banks.add', compact('piggyBank', 'maxAmount', 'currency')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -101,24 +89,18 @@ class AmountController extends Controller | ||||
|     public function addMobile(PiggyBank $piggyBank) | ||||
|     { | ||||
|         /** @var Carbon $date */ | ||||
|         $date     = session('end', today(config('app.timezone'))); | ||||
|         $accounts = []; | ||||
|         $total    = '0'; | ||||
|         foreach ($piggyBank->accounts as $account) { | ||||
|             $leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $account, $date); | ||||
|             $savedSoFar    = $this->piggyRepos->getCurrentAmount($piggyBank, $account); | ||||
|             $leftToSave    = bcsub($piggyBank->target_amount, $savedSoFar); | ||||
|             $accounts[]    = [ | ||||
|                 'account'         => $account, | ||||
|                 'left_on_account' => $leftOnAccount, | ||||
|                 'saved_so_far'    => $savedSoFar, | ||||
|                 'left_to_save'    => $leftToSave, | ||||
|                 'max_amount'      => 0 === bccomp($piggyBank->target_amount, '0') ? $leftOnAccount : min($leftOnAccount, $leftToSave), | ||||
|             ]; | ||||
|             $total         = bcadd($total, $leftOnAccount); | ||||
|         } | ||||
|         $date          = session('end', today(config('app.timezone'))); | ||||
|         $leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $date); | ||||
|         $savedSoFar    = $this->piggyRepos->getCurrentAmount($piggyBank); | ||||
|         $maxAmount     = $leftOnAccount; | ||||
| 
 | ||||
|         return view('piggy-banks.add-mobile', compact('piggyBank', 'total', 'accounts')); | ||||
|         if (0 !== bccomp($piggyBank->targetamount, '0')) { | ||||
|             $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar); | ||||
|             $maxAmount  = min($leftOnAccount, $leftToSave); | ||||
|         } | ||||
|         $currency      = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|         return view('piggy-banks.add-mobile', compact('piggyBank', 'maxAmount', 'currency')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -126,48 +108,32 @@ class AmountController extends Controller | ||||
|      */ | ||||
|     public function postAdd(Request $request, PiggyBank $piggyBank): RedirectResponse | ||||
|     { | ||||
|         $data    = $request->all(); | ||||
|         $amounts = $data['amount'] ?? []; | ||||
|         $total   = '0'; | ||||
|         Log::debug('Start with loop.'); | ||||
| 
 | ||||
|         /** @var Account $account */ | ||||
|         foreach ($piggyBank->accounts as $account) { | ||||
|             $amount        = (string) ($amounts[$account->id] ?? '0'); | ||||
|             if ('' === $amount || 0 === bccomp($amount, '0')) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (-1 === bccomp($amount, '0')) { | ||||
|                 $amount = bcmul($amount, '-1'); | ||||
|             } | ||||
| 
 | ||||
|             // small check to see if the $amount is not more than the total "left to save" value
 | ||||
|             $currentAmount = $this->piggyRepos->getCurrentAmount($piggyBank); | ||||
|             $leftToSave    = 0 === bccomp($piggyBank->target_amount, '0') ? '0' : bcsub($piggyBank->target_amount, $currentAmount); | ||||
|             if (bccomp($amount, $leftToSave) > 0 && 0 !== bccomp($leftToSave, '0')) { | ||||
|                 Log::debug(sprintf('Amount "%s" is more than left to save "%s". Using left to save.', $amount, $leftToSave)); | ||||
|                 $amount = $leftToSave; | ||||
|             } | ||||
| 
 | ||||
|             $canAddAmount  = $this->piggyRepos->canAddAmount($piggyBank, $account, $amount); | ||||
|             if ($canAddAmount) { | ||||
|                 $this->piggyRepos->addAmount($piggyBank, $account, $amount); | ||||
|                 $total = bcadd($total, $amount); | ||||
|             } | ||||
|             $piggyBank->refresh(); | ||||
|         $amount   = $request->get('amount') ?? '0'; | ||||
|         $currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); | ||||
|         // if amount is negative, make positive and continue:
 | ||||
|         if (-1 === bccomp($amount, '0')) { | ||||
|             $amount = bcmul($amount, '-1'); | ||||
|         } | ||||
|         if (0 !== bccomp($total, '0')) { | ||||
|             session()->flash('success', (string) trans('firefly.added_amount_to_piggy', ['amount' => app('amount')->formatAnything($piggyBank->transactionCurrency, $total, false), 'name' => $piggyBank->name])); | ||||
|         if ($this->piggyRepos->canAddAmount($piggyBank, $amount)) { | ||||
|             $this->piggyRepos->addAmount($piggyBank, $amount); | ||||
|             session()->flash( | ||||
|                 'success', | ||||
|                 (string)trans( | ||||
|                     'firefly.added_amount_to_piggy', | ||||
|                     ['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => $piggyBank->name] | ||||
|                 ) | ||||
|             ); | ||||
|             app('preferences')->mark(); | ||||
| 
 | ||||
|             return redirect(route('piggy-banks.index')); | ||||
|         } | ||||
|         app('log')->error(sprintf('Cannot add %s because canAddAmount returned false.', $total)); | ||||
| 
 | ||||
|         app('log')->error('Cannot add '.$amount.' because canAddAmount returned false.'); | ||||
|         session()->flash( | ||||
|             'error', | ||||
|             (string) trans( | ||||
|             (string)trans( | ||||
|                 'firefly.cannot_add_amount_piggy', | ||||
|                 ['amount' => app('amount')->formatAnything($piggyBank->transactionCurrency, $total, false), 'name' => e($piggyBank->name)] | ||||
|                 ['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)] | ||||
|             ) | ||||
|         ); | ||||
| 
 | ||||
| @@ -179,44 +145,32 @@ class AmountController extends Controller | ||||
|      */ | ||||
|     public function postRemove(Request $request, PiggyBank $piggyBank): RedirectResponse | ||||
|     { | ||||
|         $amounts = $request->get('amount') ?? []; | ||||
|         if (!is_array($amounts)) { | ||||
|             $amounts = []; | ||||
|         $amount   = $request->get('amount') ?? '0'; | ||||
|         $currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); | ||||
|         // if amount is negative, make positive and continue:
 | ||||
|         if (-1 === bccomp($amount, '0')) { | ||||
|             $amount = bcmul($amount, '-1'); | ||||
|         } | ||||
|         $total   = '0'; | ||||
| 
 | ||||
|         /** @var Account $account */ | ||||
|         foreach ($piggyBank->accounts as $account) { | ||||
|             $amount = (string) ($amounts[$account->id] ?? '0'); | ||||
|             if ('' === $amount || 0 === bccomp($amount, '0')) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (-1 === bccomp($amount, '0')) { | ||||
|                 $amount = bcmul($amount, '-1'); | ||||
|             } | ||||
|             if ($this->piggyRepos->canRemoveAmount($piggyBank, $account, $amount)) { | ||||
|                 $this->piggyRepos->removeAmount($piggyBank, $account, $amount); | ||||
|                 $total = bcadd($total, $amount); | ||||
|             } | ||||
|         } | ||||
|         if (0 !== bccomp($total, '0')) { | ||||
|         if ($this->piggyRepos->canRemoveAmount($piggyBank, $amount)) { | ||||
|             $this->piggyRepos->removeAmount($piggyBank, $amount); | ||||
|             session()->flash( | ||||
|                 'success', | ||||
|                 (string) trans( | ||||
|                 (string)trans( | ||||
|                     'firefly.removed_amount_from_piggy', | ||||
|                     ['amount' => app('amount')->formatAnything($piggyBank->transactionCurrency, $total, false), 'name' => $piggyBank->name] | ||||
|                     ['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => $piggyBank->name] | ||||
|                 ) | ||||
|             ); | ||||
|             app('preferences')->mark(); | ||||
| 
 | ||||
|             return redirect(route('piggy-banks.index')); | ||||
|         } | ||||
|         $amount   = (string)$request->get('amount'); | ||||
| 
 | ||||
|         session()->flash( | ||||
|             'error', | ||||
|             (string) trans( | ||||
|             (string)trans( | ||||
|                 'firefly.cannot_remove_from_piggy', | ||||
|                 ['amount' => app('amount')->formatAnything($piggyBank->transactionCurrency, $total, false), 'name' => e($piggyBank->name)] | ||||
|                 ['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)] | ||||
|             ) | ||||
|         ); | ||||
| 
 | ||||
| @@ -230,15 +184,10 @@ class AmountController extends Controller | ||||
|      */ | ||||
|     public function remove(PiggyBank $piggyBank) | ||||
|     { | ||||
|         $accounts = []; | ||||
|         foreach ($piggyBank->accounts as $account) { | ||||
|             $accounts[] = [ | ||||
|                 'account'      => $account, | ||||
|                 'saved_so_far' => $this->piggyRepos->getCurrentAmount($piggyBank, $account), | ||||
|             ]; | ||||
|         } | ||||
|         $repetition = $this->piggyRepos->getRepetition($piggyBank); | ||||
|         $currency   = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|         return view('piggy-banks.remove', compact('piggyBank', 'accounts')); | ||||
|         return view('piggy-banks.remove', compact('piggyBank', 'repetition', 'currency')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -248,14 +197,9 @@ class AmountController extends Controller | ||||
|      */ | ||||
|     public function removeMobile(PiggyBank $piggyBank) | ||||
|     { | ||||
|         $accounts = []; | ||||
|         foreach ($piggyBank->accounts as $account) { | ||||
|             $accounts[] = [ | ||||
|                 'account'      => $account, | ||||
|                 'saved_so_far' => $this->piggyRepos->getCurrentAmount($piggyBank, $account), | ||||
|             ]; | ||||
|         } | ||||
|         $repetition = $this->piggyRepos->getRepetition($piggyBank); | ||||
|         $currency   = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|         return view('piggy-banks.remove-mobile', compact('piggyBank', 'accounts')); | ||||
|         return view('piggy-banks.remove-mobile', compact('piggyBank', 'repetition', 'currency')); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -31,7 +31,6 @@ use FireflyIII\Http\Requests\PiggyBankStoreRequest; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Routing\Redirector; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\View\View; | ||||
| @@ -53,7 +52,7 @@ class CreateController extends Controller | ||||
| 
 | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 app('view')->share('title', (string) trans('firefly.piggyBanks')); | ||||
|                 app('view')->share('title', (string)trans('firefly.piggyBanks')); | ||||
|                 app('view')->share('mainTitleIcon', 'fa-bullseye'); | ||||
| 
 | ||||
|                 $this->attachments = app(AttachmentHelperInterface::class); | ||||
| @@ -69,12 +68,10 @@ class CreateController extends Controller | ||||
|      * | ||||
|      * @return Factory|View | ||||
|      */ | ||||
|     public function create(Request $request) | ||||
|     public function create() | ||||
|     { | ||||
|         $subTitle     = (string) trans('firefly.new_piggy_bank'); | ||||
|         $subTitle     = (string)trans('firefly.new_piggy_bank'); | ||||
|         $subTitleIcon = 'fa-plus'; | ||||
|         $hasOldInput  = null !== $request->old('_token'); | ||||
|         $preFilled    = $request->old(); | ||||
| 
 | ||||
|         // put previous url in session if not redirect from store (not "create another").
 | ||||
|         if (true !== session('piggy-banks.create.fromStore')) { | ||||
| @@ -82,7 +79,7 @@ class CreateController extends Controller | ||||
|         } | ||||
|         session()->forget('piggy-banks.create.fromStore'); | ||||
| 
 | ||||
|         return view('piggy-banks.create', compact('subTitle', 'subTitleIcon', 'preFilled')); | ||||
|         return view('piggy-banks.create', compact('subTitle', 'subTitleIcon')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -95,14 +92,12 @@ class CreateController extends Controller | ||||
|     public function store(PiggyBankStoreRequest $request) | ||||
|     { | ||||
|         $data      = $request->getPiggyBankData(); | ||||
| 
 | ||||
|         if (null === $data['start_date']) { | ||||
|             $data['start_date'] = today(config('app.timezone')); | ||||
|         if (null === $data['startdate']) { | ||||
|             $data['startdate'] = today(config('app.timezone')); | ||||
|         } | ||||
|         $piggyBank = $this->piggyRepos->store($data); | ||||
| 
 | ||||
|         session()->flash('success', (string) trans('firefly.stored_piggy_bank', ['name' => $piggyBank->name])); | ||||
|         session()->flash('success_url', route('piggy-banks.show', [$piggyBank->id])); | ||||
|         session()->flash('success', (string)trans('firefly.stored_piggy_bank', ['name' => $piggyBank->name])); | ||||
|         app('preferences')->mark(); | ||||
| 
 | ||||
|         // store attachment(s):
 | ||||
| @@ -113,7 +108,7 @@ class CreateController extends Controller | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string) trans('firefly.no_att_demo_user')); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|         if (count($this->attachments->getMessages()->get('attachments')) > 0) { | ||||
| @@ -121,7 +116,7 @@ class CreateController extends Controller | ||||
|         } | ||||
|         $redirect  = redirect($this->getPreviousUrl('piggy-banks.create.url')); | ||||
| 
 | ||||
|         if (1 === (int) $request->get('create_another')) { | ||||
|         if (1 === (int)$request->get('create_another')) { | ||||
|             session()->put('piggy-banks.create.fromStore', true); | ||||
| 
 | ||||
|             $redirect = redirect(route('piggy-banks.create'))->withInput(); | ||||
|   | ||||
| @@ -77,23 +77,24 @@ class EditController extends Controller | ||||
|         $subTitleIcon = 'fa-pencil'; | ||||
|         $note         = $piggyBank->notes()->first(); | ||||
|         // Flash some data to fill the form.
 | ||||
|         $targetDate   = $piggyBank->target_date?->format('Y-m-d'); | ||||
|         $startDate    = $piggyBank->start_date?->format('Y-m-d'); | ||||
|         $targetDate   = $piggyBank->targetdate?->format('Y-m-d'); | ||||
|         $startDate    = $piggyBank->startdate?->format('Y-m-d'); | ||||
|         $currency     = $this->accountRepository->getAccountCurrency($piggyBank->account); | ||||
|         if (null === $currency) { | ||||
|             $currency = app('amount')->getDefaultCurrency(); | ||||
|         } | ||||
| 
 | ||||
|         $preFilled    = [ | ||||
|             'name'          => $piggyBank->name, | ||||
|             'target_amount' => app('steam')->bcround($piggyBank->target_amount, $piggyBank->transactionCurrency->decimal_places), | ||||
|             'target_date'   => $targetDate, | ||||
|             'start_date'    => $startDate, | ||||
|             'accounts'      => [], | ||||
|             'object_group'  => null !== $piggyBank->objectGroups->first() ? $piggyBank->objectGroups->first()->title : '', | ||||
|             'notes'         => null === $note ? '' : $note->text, | ||||
|             'name'         => $piggyBank->name, | ||||
|             'account_id'   => $piggyBank->account_id, | ||||
|             'targetamount' => app('steam')->bcround($piggyBank->targetamount, $currency->decimal_places), | ||||
|             'targetdate'   => $targetDate, | ||||
|             'startdate'    => $startDate, | ||||
|             'object_group' => null !== $piggyBank->objectGroups->first() ? $piggyBank->objectGroups->first()->title : '', | ||||
|             'notes'        => null === $note ? '' : $note->text, | ||||
|         ]; | ||||
|         foreach ($piggyBank->accounts as $account) { | ||||
|             $preFilled['accounts'][] = $account->id; | ||||
|         } | ||||
|         if (0 === bccomp($piggyBank->target_amount, '0')) { | ||||
|             $preFilled['target_amount'] = ''; | ||||
|         if (0 === bccomp($piggyBank->targetamount, '0')) { | ||||
|             $preFilled['targetamount'] = ''; | ||||
|         } | ||||
|         session()->flash('preFilled', $preFilled); | ||||
| 
 | ||||
|   | ||||
| @@ -27,7 +27,6 @@ namespace FireflyIII\Http\Controllers\PiggyBank; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Repositories\ObjectGroup\OrganisesObjectGroups; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| @@ -36,7 +35,6 @@ use FireflyIII\Transformers\PiggyBankTransformer; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Collection; | ||||
| use Illuminate\View\View; | ||||
| use Symfony\Component\HttpFoundation\ParameterBag; | ||||
| 
 | ||||
| @@ -58,7 +56,7 @@ class IndexController extends Controller | ||||
| 
 | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 app('view')->share('title', (string) trans('firefly.piggyBanks')); | ||||
|                 app('view')->share('title', (string)trans('firefly.piggyBanks')); | ||||
|                 app('view')->share('mainTitleIcon', 'fa-bullseye'); | ||||
| 
 | ||||
|                 $this->piggyRepos = app(PiggyBankRepositoryInterface::class); | ||||
| @@ -82,6 +80,7 @@ class IndexController extends Controller | ||||
|         $this->cleanupObjectGroups(); | ||||
|         $this->piggyRepos->resetOrder(); | ||||
|         $collection         = $this->piggyRepos->getPiggyBanks(); | ||||
|         $accounts           = []; | ||||
| 
 | ||||
|         /** @var Carbon $end */ | ||||
|         $end                = session('end', today(config('app.timezone'))->endOfMonth()); | ||||
| @@ -90,15 +89,51 @@ class IndexController extends Controller | ||||
|         $parameters         = new ParameterBag(); | ||||
|         $parameters->set('end', $end); | ||||
| 
 | ||||
|         // make piggy bank groups:
 | ||||
|         $piggyBanks         = []; | ||||
| 
 | ||||
|         /** @var PiggyBankTransformer $transformer */ | ||||
|         $transformer        = app(PiggyBankTransformer::class); | ||||
|         $transformer->setParameters(new ParameterBag()); | ||||
| 
 | ||||
|         /** @var AccountTransformer $accountTransformer */ | ||||
|         $accountTransformer = app(AccountTransformer::class); | ||||
|         $accountTransformer->setParameters($parameters); | ||||
| 
 | ||||
|         // data
 | ||||
|         $piggyBanks         = $this->groupPiggyBanks($collection); | ||||
|         $accounts           = $this->collectAccounts($collection); | ||||
|         $accounts           = $this->mergeAccountsAndPiggies($piggyBanks, $accounts); | ||||
|         /** @var PiggyBank $piggy */ | ||||
|         foreach ($collection as $piggy) { | ||||
|             $array                                    = $transformer->transform($piggy); | ||||
|             $groupOrder                               = (int)$array['object_group_order']; | ||||
|             // make group array if necessary:
 | ||||
|             $piggyBanks[$groupOrder] ??= [ | ||||
|                 'object_group_id'    => $array['object_group_id'] ?? 0, | ||||
|                 'object_group_title' => $array['object_group_title'] ?? trans('firefly.default_group_title_name'), | ||||
|                 'piggy_banks'        => [], | ||||
|             ]; | ||||
| 
 | ||||
|             $account                                  = $accountTransformer->transform($piggy->account); | ||||
|             $accountId                                = (int)$account['id']; | ||||
|             $array['attachments']                     = $this->piggyRepos->getAttachments($piggy); | ||||
|             if (!array_key_exists($accountId, $accounts)) { | ||||
|                 // create new:
 | ||||
|                 $accounts[$accountId]            = $account; | ||||
| 
 | ||||
|                 // add some interesting details:
 | ||||
|                 $accounts[$accountId]['left']    = $accounts[$accountId]['current_balance']; | ||||
|                 $accounts[$accountId]['saved']   = 0; | ||||
|                 $accounts[$accountId]['target']  = 0; | ||||
|                 $accounts[$accountId]['to_save'] = 0; | ||||
|             } | ||||
| 
 | ||||
|             // calculate new interesting fields:
 | ||||
|             $accounts[$accountId]['left']             -= $array['current_amount']; | ||||
|             $accounts[$accountId]['saved']            += $array['current_amount']; | ||||
|             $accounts[$accountId]['target']           += $array['target_amount']; | ||||
|             $accounts[$accountId]['to_save']          += ($array['target_amount'] - $array['current_amount']); | ||||
|             $array['account_name']                    = $account['name']; | ||||
|             $piggyBanks[$groupOrder]['piggy_banks'][] = $array; | ||||
|         } | ||||
|         // do a bunch of summaries.
 | ||||
|         $piggyBanks         = $this->makeSums($piggyBanks); | ||||
| 
 | ||||
|         ksort($piggyBanks); | ||||
| @@ -127,10 +162,10 @@ class IndexController extends Controller | ||||
|                 // current_amount
 | ||||
|                 // left_to_save
 | ||||
|                 // save_per_month
 | ||||
|                 $sums[$groupId][$currencyId]['target']         = bcadd($sums[$groupId][$currencyId]['target'], (string) $piggy['target_amount']); | ||||
|                 $sums[$groupId][$currencyId]['saved']          = bcadd($sums[$groupId][$currencyId]['saved'], (string) $piggy['current_amount']); | ||||
|                 $sums[$groupId][$currencyId]['left_to_save']   = bcadd($sums[$groupId][$currencyId]['left_to_save'], (string) $piggy['left_to_save']); | ||||
|                 $sums[$groupId][$currencyId]['save_per_month'] = bcadd($sums[$groupId][$currencyId]['save_per_month'], (string) $piggy['save_per_month']); | ||||
|                 $sums[$groupId][$currencyId]['target']         = bcadd($sums[$groupId][$currencyId]['target'], (string)$piggy['target_amount']); | ||||
|                 $sums[$groupId][$currencyId]['saved']          = bcadd($sums[$groupId][$currencyId]['saved'], (string)$piggy['current_amount']); | ||||
|                 $sums[$groupId][$currencyId]['left_to_save']   = bcadd($sums[$groupId][$currencyId]['left_to_save'], (string)$piggy['left_to_save']); | ||||
|                 $sums[$groupId][$currencyId]['save_per_month'] = bcadd($sums[$groupId][$currencyId]['save_per_month'], (string)$piggy['save_per_month']); | ||||
|             } | ||||
|         } | ||||
|         foreach ($piggyBanks as $groupOrder => $group) { | ||||
| @@ -146,8 +181,8 @@ class IndexController extends Controller | ||||
|      */ | ||||
|     public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse | ||||
|     { | ||||
|         $objectGroupTitle = (string) $request->get('objectGroupTitle'); | ||||
|         $newOrder         = (int) $request->get('order'); | ||||
|         $objectGroupTitle = (string)$request->get('objectGroupTitle'); | ||||
|         $newOrder         = (int)$request->get('order'); | ||||
|         $this->piggyRepos->setOrder($piggyBank, $newOrder); | ||||
|         if ('' !== $objectGroupTitle) { | ||||
|             $this->piggyRepos->setObjectGroup($piggyBank, $objectGroupTitle); | ||||
| @@ -158,97 +193,4 @@ class IndexController extends Controller | ||||
| 
 | ||||
|         return response()->json(['data' => 'OK']); | ||||
|     } | ||||
| 
 | ||||
|     private function groupPiggyBanks(Collection $collection): array | ||||
|     { | ||||
|         /** @var PiggyBankTransformer $transformer */ | ||||
|         $transformer = app(PiggyBankTransformer::class); | ||||
|         $transformer->setParameters(new ParameterBag()); | ||||
|         $piggyBanks  = []; | ||||
| 
 | ||||
|         /** @var PiggyBank $piggy */ | ||||
|         foreach ($collection as $piggy) { | ||||
|             $array                                    = $transformer->transform($piggy); | ||||
|             $groupOrder                               = (int) $array['object_group_order']; | ||||
|             $piggyBanks[$groupOrder] ??= [ | ||||
|                 'object_group_id'    => $array['object_group_id'] ?? 0, | ||||
|                 'object_group_title' => $array['object_group_title'] ?? trans('firefly.default_group_title_name'), | ||||
|                 'piggy_banks'        => [], | ||||
|             ]; | ||||
|             $array['attachments']                     = $this->piggyRepos->getAttachments($piggy); | ||||
| 
 | ||||
|             // sum the total amount for the index.
 | ||||
|             $piggyBanks[$groupOrder]['piggy_banks'][] = $array; | ||||
|         } | ||||
| 
 | ||||
|         return $piggyBanks; | ||||
|     } | ||||
| 
 | ||||
|     private function collectAccounts(Collection $collection): array | ||||
|     { | ||||
|         /** @var Carbon $end */ | ||||
|         $end                = session('end', today(config('app.timezone'))->endOfMonth()); | ||||
| 
 | ||||
|         // transform piggies using the transformer:
 | ||||
|         $parameters         = new ParameterBag(); | ||||
|         $parameters->set('end', $end); | ||||
| 
 | ||||
|         /** @var AccountTransformer $accountTransformer */ | ||||
|         $accountTransformer = app(AccountTransformer::class); | ||||
|         $accountTransformer->setParameters($parameters); | ||||
| 
 | ||||
|         $return             = []; | ||||
| 
 | ||||
|         /** @var PiggyBank $piggy */ | ||||
|         foreach ($collection as $piggy) { | ||||
|             $accounts = $piggy->accounts; | ||||
| 
 | ||||
|             /** @var Account $account */ | ||||
|             foreach ($accounts as $account) { | ||||
|                 $array     = $accountTransformer->transform($account); | ||||
|                 $accountId = (int) $array['id']; | ||||
|                 if (!array_key_exists($accountId, $return)) { | ||||
|                     $return[$accountId]            = $array; | ||||
| 
 | ||||
|                     // add some interesting details:
 | ||||
|                     $return[$accountId]['left']    = $return[$accountId]['current_balance']; | ||||
|                     $return[$accountId]['saved']   = '0'; | ||||
|                     $return[$accountId]['target']  = '0'; | ||||
|                     $return[$accountId]['to_save'] = '0'; | ||||
|                 } | ||||
| 
 | ||||
|                 // calculate new interesting fields:
 | ||||
|                 //                $return[$accountId]['left']             -= $array['current_amount'];
 | ||||
|                 //                $return[$accountId]['saved']            += $array['current_amount'];
 | ||||
|                 //                $return[$accountId]['target']           += $array['target_amount'];
 | ||||
|                 //                $return[$accountId]['to_save']          += ($array['target_amount'] - $array['current_amount']);
 | ||||
|                 //                $return['account_name']                    = $account['name'];
 | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $return; | ||||
|     } | ||||
| 
 | ||||
|     private function mergeAccountsAndPiggies(array $piggyBanks, array $accounts): array | ||||
|     { | ||||
|         // @var array $piggyBank
 | ||||
|         foreach ($piggyBanks as $group) { | ||||
|             foreach ($group['piggy_banks'] as $piggyBank) { | ||||
|                 // loop all accounts in this piggy bank subtract the current amount from "left to save" in the $accounts array.
 | ||||
|                 /** @var array $piggyAccount */ | ||||
|                 foreach ($piggyBank['accounts'] as $piggyAccount) { | ||||
|                     $accountId = $piggyAccount['id']; | ||||
|                     if (array_key_exists($accountId, $accounts)) { | ||||
|                         $accounts[$accountId]['left']    = bcsub($accounts[$accountId]['left'], $piggyAccount['current_amount']); | ||||
|                         $accounts[$accountId]['saved']   = bcadd($accounts[$accountId]['saved'], $piggyAccount['current_amount']); | ||||
|                         $accounts[$accountId]['target']  = bcadd($accounts[$accountId]['target'], $piggyBank['target_amount']); | ||||
|                         $accounts[$accountId]['to_save'] = bcadd($accounts[$accountId]['to_save'], bcsub($piggyBank['target_amount'], $piggyAccount['current_amount'])); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $accounts; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -83,7 +83,6 @@ class ShowController extends Controller | ||||
|         $subTitle    = $piggyBank->name; | ||||
|         $attachments = $this->piggyRepos->getAttachments($piggyBank); | ||||
| 
 | ||||
| 
 | ||||
|         return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle', 'piggy', 'attachments')); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,14 +23,12 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Http\Controllers; | ||||
| 
 | ||||
| use FireflyIII\Events\Test\UserTestNotificationChannel; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Http\Requests\PreferencesRequest; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\AccountType; | ||||
| use FireflyIII\Models\Preference; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\User; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Illuminate\Http\Request; | ||||
| @@ -51,7 +49,7 @@ class PreferencesController extends Controller | ||||
| 
 | ||||
|         $this->middleware( | ||||
|             static function ($request, $next) { | ||||
|                 app('view')->share('title', (string) trans('firefly.preferences')); | ||||
|                 app('view')->share('title', (string)trans('firefly.preferences')); | ||||
|                 app('view')->share('mainTitleIcon', 'fa-gear'); | ||||
| 
 | ||||
|                 return $next($request); | ||||
| @@ -68,14 +66,14 @@ class PreferencesController extends Controller | ||||
|      */ | ||||
|     public function index(AccountRepositoryInterface $repository) | ||||
|     { | ||||
|         $accounts                       = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); | ||||
|         $isDocker                       = env('IS_DOCKER', false); | ||||
|         $groupedAccounts                = []; | ||||
|         $accounts              = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); | ||||
|         $isDocker              = env('IS_DOCKER', false); | ||||
|         $groupedAccounts       = []; | ||||
| 
 | ||||
|         /** @var Account $account */ | ||||
|         foreach ($accounts as $account) { | ||||
|             $type                                                                        = $account->accountType->type; | ||||
|             $role                                                                        = sprintf('opt_group_%s', $repository->getMetaValue($account, 'account_role')); | ||||
|             $type                                                                       = $account->accountType->type; | ||||
|             $role                                                                       = sprintf('opt_group_%s', $repository->getMetaValue($account, 'account_role')); | ||||
| 
 | ||||
|             if (in_array($type, [AccountType::MORTGAGE, AccountType::DEBT, AccountType::LOAN], true)) { | ||||
|                 $role = sprintf('opt_group_l_%s', $type); | ||||
| @@ -84,119 +82,64 @@ class PreferencesController extends Controller | ||||
|             if ('opt_group_' === $role) { | ||||
|                 $role = 'opt_group_defaultAsset'; | ||||
|             } | ||||
|             $groupedAccounts[(string) trans(sprintf('firefly.%s', $role))][$account->id] = $account->name; | ||||
|             $groupedAccounts[(string)trans(sprintf('firefly.%s', $role))][$account->id] = $account->name; | ||||
|         } | ||||
|         ksort($groupedAccounts); | ||||
| 
 | ||||
|         /** @var array<int, int> $accountIds */ | ||||
|         $accountIds                     = $accounts->pluck('id')->toArray(); | ||||
|         $viewRange                      = app('navigation')->getViewRange(false); | ||||
|         $frontpageAccountsPref          = app('preferences')->get('frontpageAccounts', $accountIds); | ||||
|         $frontpageAccounts              = $frontpageAccountsPref->data; | ||||
|         $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; | ||||
|         } | ||||
|         $language                       = app('steam')->getLanguage(); | ||||
|         $languages                      = config('firefly.languages'); | ||||
|         $locale                         = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; | ||||
|         $listPageSize                   = app('preferences')->get('listPageSize', 50)->data; | ||||
|         $darkMode                       = app('preferences')->get('darkMode', 'browser')->data; | ||||
|         $customFiscalYear               = app('preferences')->get('customFiscalYear', 0)->data; | ||||
|         $fiscalYearStartStr             = app('preferences')->get('fiscalYearStart', '01-01')->data; | ||||
|         $language              = app('steam')->getLanguage(); | ||||
|         $languages             = config('firefly.languages'); | ||||
|         $locale                = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; | ||||
|         $listPageSize          = app('preferences')->get('listPageSize', 50)->data; | ||||
|         $darkMode              = app('preferences')->get('darkMode', 'browser')->data; | ||||
|         $slackUrl              = app('preferences')->get('slack_webhook_url', '')->data; | ||||
|         $customFiscalYear      = app('preferences')->get('customFiscalYear', 0)->data; | ||||
|         $fiscalYearStartStr    = app('preferences')->get('fiscalYearStart', '01-01')->data; | ||||
|         if (is_array($fiscalYearStartStr)) { | ||||
|             $fiscalYearStartStr = '01-01'; | ||||
|         } | ||||
|         $fiscalYearStart                = sprintf('%s-%s', date('Y'), (string) $fiscalYearStartStr); | ||||
|         $tjOptionalFields               = app('preferences')->get('transaction_journal_optional_fields', [])->data; | ||||
|         $availableDarkModes             = config('firefly.available_dark_modes'); | ||||
|         $fiscalYearStart       = sprintf('%s-%s', date('Y'), (string)$fiscalYearStartStr); | ||||
|         $tjOptionalFields      = app('preferences')->get('transaction_journal_optional_fields', [])->data; | ||||
|         $availableDarkModes    = config('firefly.available_dark_modes'); | ||||
| 
 | ||||
|         // notifications settings
 | ||||
|         $slackUrl                       = app('preferences')->getEncrypted('slack_webhook_url', '')->data; | ||||
|         $pushoverAppToken               = (string) app('preferences')->getEncrypted('pushover_app_token', '')->data; | ||||
|         $pushoverUserToken              = (string) app('preferences')->getEncrypted('pushover_user_token', '')->data; | ||||
|         $ntfyServer                     = app('preferences')->getEncrypted('ntfy_server', 'https://ntfy.sh')->data; | ||||
|         $ntfyTopic                      = (string) app('preferences')->getEncrypted('ntfy_topic', '')->data; | ||||
|         $ntfyAuth                       = app('preferences')->get('ntfy_auth', false)->data; | ||||
|         $ntfyUser                       = app('preferences')->getEncrypted('ntfy_user', '')->data; | ||||
|         $ntfyPass                       = (string) app('preferences')->getEncrypted('ntfy_pass', '')->data; | ||||
|         $channels                       = config('notifications.channels'); | ||||
|         $forcedAvailability             = []; | ||||
| 
 | ||||
|         // notification preferences
 | ||||
|         $notifications                  = []; | ||||
|         foreach (config('notifications.notifications.user') as $key => $info) { | ||||
|             if ($info['enabled']) { | ||||
|                 $notifications[$key] | ||||
|                     = [ | ||||
|                         'enabled'      => app('preferences')->get(sprintf('notification_%s', $key), true)->data, | ||||
|                         'configurable' => $info['configurable'], | ||||
|                     ]; | ||||
|             } | ||||
|         // notification preferences (single value for each):
 | ||||
|         $notifications         = []; | ||||
|         foreach (config('firefly.available_notifications') as $notification) { | ||||
|             $notifications[$notification] = app('preferences')->get(sprintf('notification_%s', $notification), true)->data; | ||||
|         } | ||||
|         // loop all channels to see if they are available.
 | ||||
|         foreach ($channels as $channel => $info) { | ||||
|             $forcedAvailability[$channel] = true; | ||||
|         } | ||||
|         $forcedAvailability['ntfy']     = '' !== $ntfyTopic; | ||||
|         $forcedAvailability['pushover'] = '' !== $pushoverAppToken && '' !== $pushoverUserToken; | ||||
| 
 | ||||
|         ksort($languages); | ||||
| 
 | ||||
|         // list of locales also has "equal" which makes it equal to whatever the language is.
 | ||||
| 
 | ||||
|         try { | ||||
|             $locales = json_decode((string) file_get_contents(resource_path(sprintf('locales/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR); | ||||
|             $locales = json_decode((string)file_get_contents(resource_path(sprintf('locales/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR); | ||||
|         } catch (\JsonException $e) { | ||||
|             app('log')->error($e->getMessage()); | ||||
|             $locales = []; | ||||
|         } | ||||
|         $locales                        = ['equal' => (string) trans('firefly.equal_to_language')] + $locales; | ||||
|         $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; | ||||
|         } | ||||
| 
 | ||||
|         // for the demo user, the notification settings are automatically emptied.
 | ||||
|         // this isn't really secure, but it means that the demo site has semi-secret notification settings.
 | ||||
|         // for the demo user, the slackUrl is automatically emptied.
 | ||||
|         // this isn't really secure, but it means that the demo site has a semi-secret
 | ||||
|         // slackUrl.
 | ||||
|         if (auth()->user()->hasRole('demo')) { | ||||
|             $slackUrl          = ''; | ||||
|             $pushoverAppToken  = ''; | ||||
|             $pushoverUserToken = ''; | ||||
|             $ntfyServer        = ''; | ||||
|             $ntfyTopic         = ''; | ||||
|             $ntfyAuth          = false; | ||||
|             $ntfyUser          = ''; | ||||
|             $ntfyPass          = ''; | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
| 
 | ||||
|         return view('preferences.index', compact( | ||||
|             'language', | ||||
|             'pushoverAppToken', | ||||
|             'pushoverUserToken', | ||||
|             'ntfyServer', | ||||
|             'ntfyTopic', | ||||
|             'ntfyAuth', | ||||
|             'channels', | ||||
|             'ntfyUser', | ||||
|             'forcedAvailability', | ||||
|             'ntfyPass', | ||||
|             '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')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -209,21 +152,21 @@ class PreferencesController extends Controller | ||||
|      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) | ||||
|      * @SuppressWarnings(PHPMD.NPathComplexity) | ||||
|      */ | ||||
|     public function postIndex(PreferencesRequest $request) | ||||
|     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[] = (int)$id; | ||||
|             } | ||||
|             app('preferences')->set('frontpageAccounts', $frontpageAccounts); | ||||
|         } | ||||
| 
 | ||||
|         // extract notifications:
 | ||||
|         $all               = $request->all(); | ||||
|         foreach (config('notifications.notifications.user') as $key => $info) { | ||||
|             $key = sprintf('notification_%s', $key); | ||||
|         foreach (config('firefly.available_notifications') as $option) { | ||||
|             $key = sprintf('notification_%s', $option); | ||||
|             if (array_key_exists($key, $all)) { | ||||
|                 app('preferences')->set($key, true); | ||||
|             } | ||||
| @@ -239,24 +182,20 @@ class PreferencesController extends Controller | ||||
|         session()->forget('end'); | ||||
|         session()->forget('range'); | ||||
| 
 | ||||
|         // notification settings, cannot be set by the demo user.
 | ||||
|         // slack URL:
 | ||||
|         if (!auth()->user()->hasRole('demo')) { | ||||
| 
 | ||||
|             $variables = ['slack_webhook_url', 'pushover_app_token', 'pushover_user_token', 'ntfy_server', 'ntfy_topic', 'ntfy_user', 'ntfy_pass']; | ||||
|             foreach ($variables as $variable) { | ||||
|                 if ('' === $all[$variable]) { | ||||
|                     app('preferences')->delete($variable); | ||||
|                 } | ||||
|                 if ('' !== $all[$variable]) { | ||||
|                     app('preferences')->setEncrypted($variable, $all[$variable]); | ||||
|                 } | ||||
|             $url = (string)$request->get('slackUrl'); | ||||
|             if (UrlValidator::isValidWebhookURL($url)) { | ||||
|                 app('preferences')->set('slack_webhook_url', $url); | ||||
|             } | ||||
|             if ('' === $url) { | ||||
|                 app('preferences')->delete('slack_webhook_url'); | ||||
|             } | ||||
|             app('preferences')->set('ntfy_auth', $all['ntfy_auth'] ?? false); | ||||
|         } | ||||
| 
 | ||||
|         // custom fiscal year
 | ||||
|         $customFiscalYear  = 1 === (int) $request->get('customFiscalYear'); | ||||
|         $string            = strtotime((string) $request->get('fiscalYearStart')); | ||||
|         $customFiscalYear  = 1 === (int)$request->get('customFiscalYear'); | ||||
|         $string            = strtotime((string)$request->get('fiscalYearStart')); | ||||
|         if (false !== $string) { | ||||
|             $fiscalYearStart = date('m-d', $string); | ||||
|             app('preferences')->set('customFiscalYear', $customFiscalYear); | ||||
| @@ -265,7 +204,7 @@ class PreferencesController extends Controller | ||||
| 
 | ||||
|         // save page size:
 | ||||
|         app('preferences')->set('listPageSize', 50); | ||||
|         $listPageSize      = (int) $request->get('listPageSize'); | ||||
|         $listPageSize      = (int)$request->get('listPageSize'); | ||||
|         if ($listPageSize > 0 && $listPageSize < 1337) { | ||||
|             app('preferences')->set('listPageSize', $listPageSize); | ||||
|         } | ||||
| @@ -313,35 +252,9 @@ class PreferencesController extends Controller | ||||
|             app('preferences')->set('darkMode', $darkMode); | ||||
|         } | ||||
| 
 | ||||
|         session()->flash('success', (string) trans('firefly.saved_preferences')); | ||||
|         session()->flash('success', (string)trans('firefly.saved_preferences')); | ||||
|         app('preferences')->mark(); | ||||
| 
 | ||||
|         return redirect(route('preferences.index')); | ||||
|     } | ||||
| 
 | ||||
|     public function testNotification(Request $request): mixed | ||||
|     { | ||||
| 
 | ||||
|         $all     = $request->all(); | ||||
|         $channel = $all['channel'] ?? ''; | ||||
| 
 | ||||
|         switch ($channel) { | ||||
|             default: | ||||
|                 session()->flash('error', (string) trans('firefly.notification_test_failed', ['channel' => $channel])); | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'email': | ||||
|             case 'slack': | ||||
|             case 'pushover': | ||||
|             case 'ntfy': | ||||
|                 /** @var User $user */ | ||||
|                 $user = auth()->user(); | ||||
|                 app('log')->debug(sprintf('Now in testNotification("%s") controller.', $channel)); | ||||
|                 event(new UserTestNotificationChannel($channel, $user)); | ||||
|                 session()->flash('success', (string) trans('firefly.notification_test_executed', ['channel' => $channel])); | ||||
|         } | ||||
| 
 | ||||
|         return ''; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,78 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * NotificationRequest.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\Requests; | ||||
| 
 | ||||
| use FireflyIII\Rules\Admin\IsValidSlackOrDiscordUrl; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| 
 | ||||
| class NotificationRequest extends FormRequest | ||||
| { | ||||
|     use ChecksLogin; | ||||
|     use ConvertsDataTypes; | ||||
| 
 | ||||
|     public function getAll(): array | ||||
|     { | ||||
|         $return                        = []; | ||||
|         foreach (config('notifications.notifications.owner') as $key => $info) { | ||||
|             $value        = false; | ||||
|             if ($this->has(sprintf('notification_%s', $key))) { | ||||
|                 $value = true; | ||||
|             } | ||||
|             $return[$key] = $value; | ||||
|         } | ||||
|         $return['slack_webhook_url']   = $this->convertString('slack_webhook_url'); | ||||
| 
 | ||||
|         $return['pushover_app_token']  = $this->convertString('pushover_app_token'); | ||||
|         $return['pushover_user_token'] = $this->convertString('pushover_user_token'); | ||||
| 
 | ||||
|         $return['ntfy_server']         = $this->convertString('ntfy_server'); | ||||
|         $return['ntfy_topic']          = $this->convertString('ntfy_topic'); | ||||
|         $return['ntfy_auth']           = $this->convertBoolean($this->get('ntfy_auth')); | ||||
|         $return['ntfy_user']           = $this->convertString('ntfy_user'); | ||||
|         $return['ntfy_pass']           = $this->convertString('ntfy_pass'); | ||||
| 
 | ||||
|         return $return; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Rules for this request. | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         $rules = [ | ||||
|             'slack_webhook_url'   => ['nullable', 'url', 'min:1', new IsValidSlackOrDiscordUrl()], | ||||
|             'ntfy_server'         => ['nullable', 'url', 'min:1'], | ||||
|             'ntfy_user'           => ['required_with:ntfy_pass,ntfy_auth', 'nullable', 'string', 'min:1'], | ||||
|             'ntfy_pass'           => ['required_with:ntfy_user,ntfy_auth', 'nullable', 'string', 'min:1'], | ||||
|         ]; | ||||
|         foreach (config('notifications.notifications.owner') as $key => $info) { | ||||
|             $rules[sprintf('notification_%s', $key)] = 'in:0,1'; | ||||
|         } | ||||
| 
 | ||||
|         return $rules; | ||||
|     } | ||||
| } | ||||
| @@ -23,8 +23,6 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Http\Requests; | ||||
| 
 | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| @@ -45,24 +43,15 @@ class PiggyBankStoreRequest extends FormRequest | ||||
|      */ | ||||
|     public function getPiggyBankData(): array | ||||
|     { | ||||
|         $accounts = $this->get('accounts'); | ||||
|         $data     = [ | ||||
|             'name'                    => $this->convertString('name'), | ||||
|             'start_date'              => $this->getCarbonDate('start_date'), | ||||
|             'target_amount'           => $this->convertString('target_amount'), | ||||
|             'transaction_currency_id' => $this->convertInteger('transaction_currency_id'), | ||||
|             'target_date'             => $this->getCarbonDate('target_date'), | ||||
|             'notes'                   => $this->stringWithNewlines('notes'), | ||||
|             'object_group_title'      => $this->convertString('object_group'), | ||||
|         return [ | ||||
|             'name'               => $this->convertString('name'), | ||||
|             'startdate'          => $this->getCarbonDate('startdate'), | ||||
|             'account_id'         => $this->convertInteger('account_id'), | ||||
|             'targetamount'       => $this->convertString('targetamount'), | ||||
|             'targetdate'         => $this->getCarbonDate('targetdate'), | ||||
|             'notes'              => $this->stringWithNewlines('notes'), | ||||
|             'object_group_title' => $this->convertString('object_group'), | ||||
|         ]; | ||||
|         if (!is_array($accounts)) { | ||||
|             $accounts = []; | ||||
|         } | ||||
|         foreach ($accounts as $item) { | ||||
|             $data['accounts'][] = ['account_id' => (int) $item]; | ||||
|         } | ||||
| 
 | ||||
|         return $data; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -71,63 +60,21 @@ class PiggyBankStoreRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name'          => 'required|min:1|max:255|uniquePiggyBankForUser', | ||||
|             'accounts'      => 'required|array', | ||||
|             'accounts.*'    => 'required|belongsToUser:accounts', | ||||
|             'target_amount' => ['nullable', new IsValidPositiveAmount()], | ||||
|             'start_date'    => 'date', | ||||
|             'target_date'   => 'date|nullable', | ||||
|             'order'         => 'integer|min:1', | ||||
|             'object_group'  => 'min:0|max:255', | ||||
|             'notes'         => 'min:1|max:32768|nullable', | ||||
|             'name'         => 'required|min:1|max:255|uniquePiggyBankForUser', | ||||
|             'account_id'   => 'required|belongsToUser:accounts', | ||||
|             'targetamount' => ['nullable', new IsValidPositiveAmount()], | ||||
|             'startdate'    => 'date', | ||||
|             'targetdate'   => 'date|nullable', | ||||
|             'order'        => 'integer|min:1', | ||||
|             'object_group' => 'min:0|max:255', | ||||
|             'notes'        => 'min:1|max:32768|nullable', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function withValidator(Validator $validator): void | ||||
|     { | ||||
|         // need to have more than one account.
 | ||||
|         // accounts need to have the same currency or be multi-currency(?).
 | ||||
|         $validator->after( | ||||
|             function (Validator $validator): void { | ||||
|                 // validate start before end only if both are there.
 | ||||
|                 $data     = $validator->getData(); | ||||
|                 $currency = $this->getCurrencyFromData($data); | ||||
|                 if (array_key_exists('accounts', $data) && is_array($data['accounts'])) { | ||||
|                     $repository = app(AccountRepositoryInterface::class); | ||||
|                     $types      = config('firefly.piggy_bank_account_types'); | ||||
|                     foreach ($data['accounts'] as $value) { | ||||
|                         $accountId = (int) $value; | ||||
|                         $account   = $repository->find($accountId); | ||||
|                         if (null !== $account) { | ||||
|                             // check currency here.
 | ||||
|                             $accountCurrency = $repository->getAccountCurrency($account); | ||||
|                             $isMultiCurrency = $repository->getMetaValue($account, 'is_multi_currency'); | ||||
|                             if ($accountCurrency->id !== $currency->id && 'true' !== $isMultiCurrency) { | ||||
|                                 $validator->errors()->add('accounts', trans('validation.invalid_account_currency')); | ||||
|                             } | ||||
|                             $type            = $account->accountType->type; | ||||
|                             if (!in_array($type, $types, true)) { | ||||
|                                 $validator->errors()->add('accounts', trans('validation.invalid_account_type')); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
| 
 | ||||
|         if ($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function getCurrencyFromData(array $data): TransactionCurrency | ||||
|     { | ||||
|         $currencyId = (int) ($data['transaction_currency_id'] ?? 0); | ||||
|         $currency   = TransactionCurrency::find($currencyId); | ||||
|         if (null === $currency) { | ||||
|             return app('amount')->getDefaultCurrency(); | ||||
|         } | ||||
| 
 | ||||
|         return $currency; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,8 +24,6 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Http\Requests; | ||||
| 
 | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| @@ -46,23 +44,15 @@ class PiggyBankUpdateRequest extends FormRequest | ||||
|      */ | ||||
|     public function getPiggyBankData(): array | ||||
|     { | ||||
|         $accounts = $this->get('accounts'); | ||||
|         $data     = [ | ||||
|         return [ | ||||
|             'name'               => $this->convertString('name'), | ||||
|             'start_date'         => $this->getCarbonDate('start_date'), | ||||
|             'target_amount'      => trim($this->convertString('target_amount')), | ||||
|             'target_date'        => $this->getCarbonDate('target_date'), | ||||
|             'startdate'          => $this->getCarbonDate('startdate'), | ||||
|             'account_id'         => $this->convertInteger('account_id'), | ||||
|             'targetamount'       => trim($this->convertString('targetamount')), | ||||
|             'targetdate'         => $this->getCarbonDate('targetdate'), | ||||
|             'notes'              => $this->stringWithNewlines('notes'), | ||||
|             'object_group_title' => $this->convertString('object_group'), | ||||
|         ]; | ||||
|         if (!is_array($accounts)) { | ||||
|             $accounts = []; | ||||
|         } | ||||
|         foreach ($accounts as $item) { | ||||
|             $data['accounts'][] = ['account_id' => (int) $item]; | ||||
|         } | ||||
| 
 | ||||
|         return $data; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -74,63 +64,21 @@ class PiggyBankUpdateRequest extends FormRequest | ||||
|         $piggy = $this->route()->parameter('piggyBank'); | ||||
| 
 | ||||
|         return [ | ||||
|             'name'          => sprintf('required|min:1|max:255|uniquePiggyBankForUser:%d', $piggy->id), | ||||
|             'accounts'      => 'required|array', | ||||
|             'accounts.*'    => 'required|belongsToUser:accounts', | ||||
|             'target_amount' => ['nullable', new IsValidPositiveAmount()], | ||||
|             'start_date'    => 'date', | ||||
|             'target_date'   => 'date|nullable', | ||||
|             'order'         => 'integer|max:32768|min:1', | ||||
|             'object_group'  => 'min:0|max:255', | ||||
|             'notes'         => 'min:1|max:32768|nullable', | ||||
|             'name'         => sprintf('required|min:1|max:255|uniquePiggyBankForUser:%d', $piggy->id), | ||||
|             'account_id'   => 'required|belongsToUser:accounts', | ||||
|             'targetamount' => ['nullable', new IsValidPositiveAmount()], | ||||
|             'startdate'    => 'date', | ||||
|             'targetdate'   => 'date|nullable', | ||||
|             'order'        => 'integer|max:32768|min:1', | ||||
|             'object_group' => 'min:0|max:255', | ||||
|             'notes'        => 'min:1|max:32768|nullable', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function withValidator(Validator $validator): void | ||||
|     {        // need to have more than one account.
 | ||||
|         // accounts need to have the same currency or be multi-currency(?).
 | ||||
|         $validator->after( | ||||
|             function (Validator $validator): void { | ||||
|                 // validate start before end only if both are there.
 | ||||
|                 $data     = $validator->getData(); | ||||
|                 $currency = $this->getCurrencyFromData($data); | ||||
|                 if (array_key_exists('accounts', $data) && is_array($data['accounts'])) { | ||||
|                     $repository = app(AccountRepositoryInterface::class); | ||||
|                     $types      = config('firefly.piggy_bank_account_types'); | ||||
|                     foreach ($data['accounts'] as $value) { | ||||
|                         $accountId = (int) $value; | ||||
|                         $account   = $repository->find($accountId); | ||||
|                         if (null !== $account) { | ||||
|                             // check currency here.
 | ||||
|                             $accountCurrency = $repository->getAccountCurrency($account); | ||||
|                             $isMultiCurrency = $repository->getMetaValue($account, 'is_multi_currency'); | ||||
|                             if ($accountCurrency->id !== $currency->id && 'true' !== $isMultiCurrency) { | ||||
|                                 $validator->errors()->add('accounts', trans('validation.invalid_account_currency')); | ||||
|                             } | ||||
|                             $type            = $account->accountType->type; | ||||
|                             if (!in_array($type, $types, true)) { | ||||
|                                 $validator->errors()->add('accounts', trans('validation.invalid_account_type')); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
| 
 | ||||
| 
 | ||||
|     { | ||||
|         if ($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function getCurrencyFromData(array $data): TransactionCurrency | ||||
|     { | ||||
|         $currencyId = (int) ($data['transaction_currency_id'] ?? 0); | ||||
|         $currency   = TransactionCurrency::find($currencyId); | ||||
|         if (null === $currency) { | ||||
|             return app('amount')->getDefaultCurrency(); | ||||
|         } | ||||
| 
 | ||||
|         return $currency; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,52 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * PreferencesRequest.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\Requests; | ||||
| 
 | ||||
| use FireflyIII\Rules\Admin\IsValidSlackOrDiscordUrl; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| 
 | ||||
| class PreferencesRequest extends FormRequest | ||||
| { | ||||
|     use ChecksLogin; | ||||
| 
 | ||||
|     /** | ||||
|      * Rules for this request. | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         $rules = [ | ||||
|             'slack_webhook_url' => ['nullable', 'url', 'min:1', new IsValidSlackOrDiscordUrl()], | ||||
|             'ntfy_server'       => ['nullable', 'url', 'min:1'], | ||||
|             'ntfy_user'         => ['required_with:ntfy_pass,ntfy_auth', 'nullable', 'string', 'min:1'], | ||||
|             'ntfy_pass'         => ['required_with:ntfy_user,ntfy_auth', 'nullable', 'string', 'min:1'], | ||||
|         ]; | ||||
|         foreach (config('notifications.notifications.user') as $key => $info) { | ||||
|             $rules[sprintf('notification_%s', $key)] = 'in:0,1'; | ||||
|         } | ||||
| 
 | ||||
|         return $rules; | ||||
|     } | ||||
| } | ||||
| @@ -32,7 +32,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||||
| use Illuminate\Database\Eloquent\Relations\BelongsToMany; | ||||
| use Illuminate\Database\Eloquent\Relations\HasMany; | ||||
| use Illuminate\Database\Eloquent\Relations\MorphMany; | ||||
| use Illuminate\Database\Eloquent\Relations\MorphToMany; | ||||
| @@ -145,7 +144,7 @@ class Account extends Model | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get all the notes. | ||||
|      * Get all of the notes. | ||||
|      */ | ||||
|     public function notes(): MorphMany | ||||
|     { | ||||
| @@ -160,9 +159,9 @@ class Account extends Model | ||||
|         return $this->morphToMany(ObjectGroup::class, 'object_groupable'); | ||||
|     } | ||||
| 
 | ||||
|     public function piggyBanks(): BelongsToMany | ||||
|     public function piggyBanks(): HasMany | ||||
|     { | ||||
|         return $this->belongsToMany(PiggyBank::class); | ||||
|         return $this->hasMany(PiggyBank::class); | ||||
|     } | ||||
| 
 | ||||
|     public function scopeAccountTypeIn(EloquentBuilder $query, array $types): void | ||||
|   | ||||
| @@ -35,46 +35,46 @@ class AccountType extends Model | ||||
| { | ||||
|     use ReturnsIntegerIdTrait; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string ASSET            = 'Asset account'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string BENEFICIARY      = 'Beneficiary account'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string CASH             = 'Cash account'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string CREDITCARD       = 'Credit card'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string DEBT             = 'Debt'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string DEFAULT          = 'Default account'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string EXPENSE          = 'Expense account'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string IMPORT           = 'Import account'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string INITIAL_BALANCE  = 'Initial balance account'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string LIABILITY_CREDIT = 'Liability credit account'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string LOAN             = 'Loan'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string MORTGAGE         = 'Mortgage'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string RECONCILIATION   = 'Reconciliation account'; | ||||
| 
 | ||||
|     #[\Deprecated] /** @deprecated  */
 | ||||
|     /** @deprecated */ | ||||
|     public const string REVENUE          = 'Revenue account'; | ||||
| 
 | ||||
|     protected $casts | ||||
|   | ||||
| @@ -39,13 +39,13 @@ class AutoBudget extends Model | ||||
|     use ReturnsIntegerIdTrait; | ||||
|     use SoftDeletes; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int AUTO_BUDGET_ADJUSTED = 3; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int AUTO_BUDGET_RESET    = 1; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int AUTO_BUDGET_ROLLOVER = 2; | ||||
|     protected $fillable                   = ['budget_id', 'amount', 'period']; | ||||
| 
 | ||||
|   | ||||
| @@ -31,7 +31,6 @@ use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||||
| use Illuminate\Database\Eloquent\Relations\MorphMany; | ||||
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||||
| 
 | ||||
| /** | ||||
| @@ -91,14 +90,6 @@ class BudgetLimit extends Model | ||||
|         return $this->belongsTo(TransactionCurrency::class); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get all the notes. | ||||
|      */ | ||||
|     public function notes(): MorphMany | ||||
|     { | ||||
|         return $this->morphMany(Note::class, 'noteable'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the amount | ||||
|      */ | ||||
|   | ||||
| @@ -27,7 +27,6 @@ use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||||
| use Illuminate\Database\Eloquent\Relations\BelongsToMany; | ||||
| use Illuminate\Database\Eloquent\Relations\HasMany; | ||||
| use Illuminate\Database\Eloquent\Relations\MorphMany; | ||||
| use Illuminate\Database\Eloquent\Relations\MorphToMany; | ||||
| @@ -44,18 +43,20 @@ class PiggyBank extends Model | ||||
| 
 | ||||
|     protected $casts | ||||
|                         = [ | ||||
|             'created_at'    => 'datetime', | ||||
|             'updated_at'    => 'datetime', | ||||
|             'deleted_at'    => 'datetime', | ||||
|             'start_date'    => 'date', | ||||
|             'target_date'   => 'date', | ||||
|             'order'         => 'int', | ||||
|             'active'        => 'boolean', | ||||
|             'encrypted'     => 'boolean', | ||||
|             'target_amount' => 'string', | ||||
|             'created_at'   => 'datetime', | ||||
|             'updated_at'   => 'datetime', | ||||
|             'deleted_at'   => 'datetime', | ||||
|             'startdate'    => 'date', | ||||
|             'targetdate'   => 'date', | ||||
|             'order'        => 'int', | ||||
|             'active'       => 'boolean', | ||||
|             'encrypted'    => 'boolean', | ||||
|             'targetamount' => 'string', | ||||
|         ]; | ||||
| 
 | ||||
|     protected $fillable = ['name', 'order', 'target_amount', 'start_date', 'start_date_tz', 'target_date', 'target_date_tz', 'active', 'transaction_currency_id']; | ||||
|     protected $fillable = ['name', 'account_id', 'order', 'targetamount', 'startdate', 'startdate_tz', 'targetdate', 'targetdate_tz', 'active']; | ||||
| 
 | ||||
|     protected $hidden   = ['targetamount_encrypted', 'encrypted']; | ||||
| 
 | ||||
|     /** | ||||
|      * Route binder. Converts the key in the URL to the specified object (or throw 404). | ||||
| @@ -67,8 +68,7 @@ class PiggyBank extends Model | ||||
|         if (auth()->check()) { | ||||
|             $piggyBankId = (int)$value; | ||||
|             $piggyBank   = self::where('piggy_banks.id', $piggyBankId) | ||||
|                 ->leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') | ||||
|                 ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|                 ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') | ||||
|                 ->where('accounts.user_id', auth()->user()->id)->first(['piggy_banks.*']) | ||||
|             ; | ||||
|             if (null !== $piggyBank) { | ||||
| @@ -79,11 +79,6 @@ class PiggyBank extends Model | ||||
|         throw new NotFoundHttpException(); | ||||
|     } | ||||
| 
 | ||||
|     public function transactionCurrency(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(TransactionCurrency::class); | ||||
|     } | ||||
| 
 | ||||
|     public function account(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(Account::class); | ||||
| @@ -115,11 +110,6 @@ class PiggyBank extends Model | ||||
|         return $this->hasMany(PiggyBankEvent::class); | ||||
|     } | ||||
| 
 | ||||
|     public function accounts(): BelongsToMany | ||||
|     { | ||||
|         return $this->belongsToMany(Account::class)->withPivot('current_amount'); | ||||
|     } | ||||
| 
 | ||||
|     public function piggyBankRepetitions(): HasMany | ||||
|     { | ||||
|         return $this->hasMany(PiggyBankRepetition::class); | ||||
| @@ -128,9 +118,9 @@ class PiggyBank extends Model | ||||
|     /** | ||||
|      * @param mixed $value | ||||
|      */ | ||||
|     public function setTargetAmountAttribute($value): void | ||||
|     public function setTargetamountAttribute($value): void | ||||
|     { | ||||
|         $this->attributes['target_amount'] = (string)$value; | ||||
|         $this->attributes['targetamount'] = (string)$value; | ||||
|     } | ||||
| 
 | ||||
|     protected function accountId(): Attribute | ||||
| @@ -150,7 +140,7 @@ class PiggyBank extends Model | ||||
|     /** | ||||
|      * Get the max amount | ||||
|      */ | ||||
|     protected function targetAmount(): Attribute | ||||
|     protected function targetamount(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (string)$value, | ||||
|   | ||||
| @@ -40,14 +40,14 @@ class PiggyBankRepetition extends Model | ||||
| 
 | ||||
|     protected $casts | ||||
|                         = [ | ||||
|             'created_at'       => 'datetime', | ||||
|             'updated_at'       => 'datetime', | ||||
|             'start_date'       => SeparateTimezoneCaster::class, | ||||
|             'target_date'      => SeparateTimezoneCaster::class, | ||||
|             'virtual_balance'  => 'string', | ||||
|             'created_at'      => 'datetime', | ||||
|             'updated_at'      => 'datetime', | ||||
|             'startdate'       => SeparateTimezoneCaster::class, | ||||
|             'targetdate'      => SeparateTimezoneCaster::class, | ||||
|             'virtual_balance' => 'string', | ||||
|         ]; | ||||
| 
 | ||||
|     protected $fillable = ['piggy_bank_id', 'start_date', 'start_date_tz', 'target_date', 'target_date_tz', 'current_amount']; | ||||
|     protected $fillable = ['piggy_bank_id', 'startdate', 'startdate_tz', 'targetdate', 'targetdate_tz', 'currentamount']; | ||||
| 
 | ||||
|     public function piggyBank(): BelongsTo | ||||
|     { | ||||
| @@ -56,7 +56,7 @@ class PiggyBankRepetition extends Model | ||||
| 
 | ||||
|     public function scopeOnDates(EloquentBuilder $query, Carbon $start, Carbon $target): EloquentBuilder | ||||
|     { | ||||
|         return $query->where('start_date', $start->format('Y-m-d'))->where('target_date', $target->format('Y-m-d')); | ||||
|         return $query->where('startdate', $start->format('Y-m-d'))->where('targetdate', $target->format('Y-m-d')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -66,14 +66,14 @@ class PiggyBankRepetition extends Model | ||||
|     { | ||||
|         return $query->where( | ||||
|             static function (EloquentBuilder $q) use ($date): void { | ||||
|                 $q->where('start_date', '<=', $date->format('Y-m-d 00:00:00')); | ||||
|                 $q->orWhereNull('start_date'); | ||||
|                 $q->where('startdate', '<=', $date->format('Y-m-d 00:00:00')); | ||||
|                 $q->orWhereNull('startdate'); | ||||
|             } | ||||
|         ) | ||||
|             ->where( | ||||
|                 static function (EloquentBuilder $q) use ($date): void { | ||||
|                     $q->where('target_date', '>=', $date->format('Y-m-d 00:00:00')); | ||||
|                     $q->orWhereNull('target_date'); | ||||
|                     $q->where('targetdate', '>=', $date->format('Y-m-d 00:00:00')); | ||||
|                     $q->orWhereNull('targetdate'); | ||||
|                 } | ||||
|             ) | ||||
|         ; | ||||
| @@ -82,15 +82,15 @@ class PiggyBankRepetition extends Model | ||||
|     /** | ||||
|      * @param mixed $value | ||||
|      */ | ||||
|     public function setCurrentAmountAttribute($value): void | ||||
|     public function setCurrentamountAttribute($value): void | ||||
|     { | ||||
|         $this->attributes['current_amount'] = (string)$value; | ||||
|         $this->attributes['currentamount'] = (string)$value; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the amount | ||||
|      */ | ||||
|     protected function currentAmount(): Attribute | ||||
|     protected function currentamount(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (string)$value, | ||||
|   | ||||
| @@ -39,16 +39,16 @@ class RecurrenceRepetition extends Model | ||||
|     use ReturnsIntegerIdTrait; | ||||
|     use SoftDeletes; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int WEEKEND_DO_NOTHING    = 1; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int WEEKEND_SKIP_CREATION = 2; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int WEEKEND_TO_FRIDAY     = 3; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int WEEKEND_TO_MONDAY     = 4; | ||||
| 
 | ||||
|     protected $casts | ||||
|   | ||||
| @@ -38,6 +38,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; | ||||
| use Illuminate\Database\Eloquent\Relations\HasMany; | ||||
| use Illuminate\Database\Eloquent\Relations\MorphMany; | ||||
| use Illuminate\Database\Eloquent\SoftDeletes; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||||
| 
 | ||||
| /** | ||||
| @@ -169,11 +170,15 @@ class TransactionJournal extends Model | ||||
| 
 | ||||
|     public function scopeAfter(EloquentBuilder $query, Carbon $date): EloquentBuilder | ||||
|     { | ||||
|         Log::debug(sprintf('scopeAfter("%s")', $date->format('Y-m-d H:i:s'))); | ||||
| 
 | ||||
|         return $query->where('transaction_journals.date', '>=', $date->format('Y-m-d H:i:s')); | ||||
|     } | ||||
| 
 | ||||
|     public function scopeBefore(EloquentBuilder $query, Carbon $date): EloquentBuilder | ||||
|     { | ||||
|         Log::debug(sprintf('scopeBefore("%s")', $date->format('Y-m-d H:i:s'))); | ||||
| 
 | ||||
|         return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')); | ||||
|     } | ||||
| 
 | ||||
|   | ||||
| @@ -38,25 +38,25 @@ class TransactionType extends Model | ||||
|     use ReturnsIntegerIdTrait; | ||||
|     use SoftDeletes; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string DEPOSIT          = 'Deposit'; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string INVALID          = 'Invalid'; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string LIABILITY_CREDIT = 'Liability credit'; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string OPENING_BALANCE  = 'Opening balance'; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string RECONCILIATION   = 'Reconciliation'; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string TRANSFER         = 'Transfer'; | ||||
| 
 | ||||
|     #[\Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string WITHDRAWAL       = 'Withdrawal'; | ||||
| 
 | ||||
|     protected $casts | ||||
|   | ||||
							
								
								
									
										114
									
								
								app/Notifications/Admin/TestNotification.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								app/Notifications/Admin/TestNotification.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestNotification.php | ||||
|  * Copyright (c) 2022 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\Notifications\Admin; | ||||
| 
 | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| 
 | ||||
| /** | ||||
|  * Class TestNotification | ||||
|  */ | ||||
| class TestNotification extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private string $address; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(string $address) | ||||
|     { | ||||
|         $this->address = $address; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      */ | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.admin-test', ['email' => $this->address]) | ||||
|             ->subject((string)trans('email.admin_test_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      */ | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         return (new SlackMessage())->content((string)trans('email.admin_test_subject')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         $slackUrl = app('fireflyconfig')->get('slack_webhook_url', '')->data; | ||||
|         if (UrlValidator::isValidWebhookURL($slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
| @@ -1,109 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * UnknownUserLoginAttempt.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\Notifications\Admin; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| class UnknownUserLoginAttempt extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private string $address; | ||||
| 
 | ||||
|     public function __construct(string $address) | ||||
|     { | ||||
|         $this->address = $address; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(OwnerNotifiable $notifiable): MailMessage | ||||
|     { | ||||
|         return new MailMessage() | ||||
|             ->markdown('emails.owner.unknown-user', ['address' => $this->address]) | ||||
|             ->subject((string) trans('email.unknown_user_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toNtfy(OwnerNotifiable $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.unknown_user_subject')); | ||||
|         $message->body((string) trans('email.unknown_user_message', ['address' => $this->address])); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(OwnerNotifiable $notifiable): PushoverMessage | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.unknown_user_message', ['address' => $this->address])) | ||||
|             ->title((string) trans('email.unknown_user_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(OwnerNotifiable $notifiable): SlackMessage | ||||
|     { | ||||
|         return new SlackMessage()->content( | ||||
|             (string) trans('email.unknown_user_body', ['address' => $this->address]) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('owner'); | ||||
|     } | ||||
| } | ||||
| @@ -25,16 +25,11 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Notifications\Admin; | ||||
| 
 | ||||
| use FireflyIII\Models\InvitedUser; | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| /** | ||||
|  * Class UserInvitation | ||||
| @@ -43,77 +38,80 @@ class UserInvitation extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private InvitedUser     $invitee; | ||||
|     private OwnerNotifiable $owner; | ||||
|     private InvitedUser $invitee; | ||||
| 
 | ||||
|     public function __construct(OwnerNotifiable $owner, InvitedUser $invitee) | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(InvitedUser $invitee) | ||||
|     { | ||||
|         $this->invitee = $invitee; | ||||
|         $this->owner   = $owner; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(OwnerNotifiable $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(OwnerNotifiable $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.invitation-created', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email]) | ||||
|             ->subject((string) trans('email.invitation_created_subject')) | ||||
|             ->subject((string)trans('email.invitation_created_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toNtfy(OwnerNotifiable $notifiable): Message | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         Log::debug('Now in toNtfy() for UserInvitation'); | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.invitation_created_subject')); | ||||
|         $message->body((string) trans('email.invitation_created_body', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email])); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(OwnerNotifiable $notifiable): PushoverMessage | ||||
|     { | ||||
|         Log::debug('Now in toPushover() for UserInvitation'); | ||||
| 
 | ||||
|         return PushoverMessage::create((string) trans('email.invitation_created_body', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email])) | ||||
|             ->title((string) trans('email.invitation_created_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return new SlackMessage()->content( | ||||
|             (string) trans('email.invitation_created_body', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email]) | ||||
|         return (new SlackMessage())->content( | ||||
|             (string)trans('email.invitation_created_body', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email]) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(OwnerNotifiable $notifiable) | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('owner'); | ||||
|         $slackUrl = app('fireflyconfig')->get('slack_webhook_url', '')->data; | ||||
|         if (UrlValidator::isValidWebhookURL($slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,17 +24,12 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\Admin; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| /** | ||||
|  * Class UserRegistration | ||||
| @@ -43,75 +38,78 @@ class UserRegistration extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private OwnerNotifiable $owner; | ||||
|     private User            $user; | ||||
|     private User $user; | ||||
| 
 | ||||
|     public function __construct(OwnerNotifiable $owner, User $user) | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user  = $user; | ||||
|         $this->owner = $owner; | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(OwnerNotifiable $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(OwnerNotifiable $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.registered-admin', ['email' => $this->user->email, 'id' => $this->user->id]) | ||||
|             ->subject((string) trans('email.registered_subject_admin')) | ||||
|             ->subject((string)trans('email.registered_subject_admin')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toNtfy(OwnerNotifiable $notifiable): Message | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         Log::debug('Now in toNtfy() for (Admin) UserRegistration'); | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.registered_subject_admin')); | ||||
|         $message->body((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'invitee' => $this->user->email])); | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new SlackMessage())->content((string)trans('email.admin_new_user_registered', ['email' => $this->user->email, 'id' => $this->user->id])); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(OwnerNotifiable $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         Log::debug('Now in toPushover() for UserRegistration'); | ||||
|         $slackUrl = app('fireflyconfig')->get('slack_webhook_url', '')->data; | ||||
|         if (UrlValidator::isValidWebhookURL($slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|         return PushoverMessage::create((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'invitee' => $this->user->email])) | ||||
|             ->title((string) trans('email.registered_subject_admin')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return new SlackMessage()->content((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'id' => $this->user->id])); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('owner'); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,16 +24,11 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\Admin; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| /** | ||||
|  * Class VersionCheckResult | ||||
| @@ -44,64 +39,58 @@ class VersionCheckResult extends Notification | ||||
| 
 | ||||
|     private string $message; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(string $message) | ||||
|     { | ||||
|         $this->message = $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(OwnerNotifiable $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(OwnerNotifiable $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.new-version', ['message' => $this->message]) | ||||
|             ->subject((string) trans('email.new_version_email_subject')) | ||||
|             ->subject((string)trans('email.new_version_email_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toNtfy(OwnerNotifiable $notifiable): Message | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         Log::debug('Now in toNtfy() for VersionCheckResult'); | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.new_version_email_subject')); | ||||
|         $message->body($this->message); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(OwnerNotifiable $notifiable): PushoverMessage | ||||
|     { | ||||
|         Log::debug('Now in toPushover() for VersionCheckResult'); | ||||
| 
 | ||||
|         return PushoverMessage::create($this->message) | ||||
|             ->title((string) trans('email.new_version_email_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return new SlackMessage()->content($this->message) | ||||
|         return (new SlackMessage())->content($this->message) | ||||
|             ->attachment(static function ($attachment): void { | ||||
|                 $attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases'); | ||||
|             }) | ||||
| @@ -109,10 +98,21 @@ class VersionCheckResult extends Notification | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(OwnerNotifiable $notifiable) | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('owner'); | ||||
|         $slackUrl = app('fireflyconfig')->get('slack_webhook_url', '')->data; | ||||
|         if (UrlValidator::isValidWebhookURL($slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,78 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * AdminNotifiable.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\Notifications\Notifiables; | ||||
| 
 | ||||
| use Illuminate\Notifications\Notification; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Support\Str; | ||||
| use NotificationChannels\Pushover\PushoverReceiver; | ||||
| 
 | ||||
| class OwnerNotifiable | ||||
| { | ||||
|     /** | ||||
|      * Get the notification routing information for the given driver. | ||||
|      * | ||||
|      * @param string            $driver | ||||
|      * @param null|Notification $notification | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function routeNotificationFor($driver, $notification = null) | ||||
|     { | ||||
|         $method = 'routeNotificationFor'.Str::studly($driver); | ||||
|         if (method_exists($this, $method)) { | ||||
|             Log::debug(sprintf('Redirect for settings to "%s".', $method)); | ||||
| 
 | ||||
|             return $this->{$method}($notification); // @phpstan-ignore-line
 | ||||
|         } | ||||
|         Log::debug(sprintf('No method "%s" found, return generic settings.', $method)); | ||||
| 
 | ||||
|         return match ($driver) { | ||||
|             'mail'  => (string) config('firefly.site_owner'), | ||||
|             default => null, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     public function routeNotificationForPushover() | ||||
|     { | ||||
|         Log::debug('Return settings for routeNotificationForPushover'); | ||||
|         $pushoverAppToken  = (string) app('fireflyconfig')->getEncrypted('pushover_app_token', '')->data; | ||||
|         $pushoverUserToken = (string) app('fireflyconfig')->getEncrypted('pushover_user_token', '')->data; | ||||
| 
 | ||||
|         return PushoverReceiver::withUserKey($pushoverUserToken) | ||||
|             ->withApplicationToken($pushoverAppToken) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     public function routeNotificationForSlack(): string | ||||
|     { | ||||
|         $res = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data; | ||||
|         if (is_array($res)) { | ||||
|             $res = ''; | ||||
|         } | ||||
| 
 | ||||
|         return (string) $res; | ||||
|     } | ||||
| } | ||||
| @@ -1,117 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * ReturnsAvailableChannels.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\Notifications; | ||||
| 
 | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use NotificationChannels\Pushover\PushoverChannel; | ||||
| use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel; | ||||
| 
 | ||||
| class ReturnsAvailableChannels | ||||
| { | ||||
|     public static function returnChannels(string $type, ?User $user = null): array | ||||
|     { | ||||
|         $channels = ['mail']; | ||||
| 
 | ||||
|         if ('owner' === $type) { | ||||
|             return self::returnOwnerChannels(); | ||||
|         } | ||||
|         if ('user' === $type && null !== $user) { | ||||
|             return self::returnUserChannels($user); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         return $channels; | ||||
|     } | ||||
| 
 | ||||
|     private static function returnOwnerChannels(): array | ||||
|     { | ||||
| 
 | ||||
|         $channels          = ['mail']; | ||||
|         $slackUrl          = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data; | ||||
|         if (UrlValidator::isValidWebhookURL($slackUrl)) { | ||||
|             $channels[] = 'slack'; | ||||
|         } | ||||
| 
 | ||||
|         // validate presence of of Ntfy settings.
 | ||||
|         if ('' !== (string) app('fireflyconfig')->getEncrypted('ntfy_topic', '')->data) { | ||||
|             Log::debug('Enabled ntfy.'); | ||||
|             $channels[] = NtfyChannel::class; | ||||
|         } | ||||
|         if ('' === (string) app('fireflyconfig')->getEncrypted('ntfy_topic', '')->data) { | ||||
|             Log::warning('No topic name for Ntfy, channel is disabled.'); | ||||
|         } | ||||
| 
 | ||||
|         // pushover
 | ||||
|         $pushoverAppToken  = (string) app('fireflyconfig')->getEncrypted('pushover_app_token', '')->data; | ||||
|         $pushoverUserToken = (string) app('fireflyconfig')->getEncrypted('pushover_user_token', '')->data; | ||||
|         if ('' === $pushoverAppToken || '' === $pushoverUserToken) { | ||||
|             Log::warning('[b] No Pushover token, channel is disabled.'); | ||||
|         } | ||||
|         if ('' !== $pushoverAppToken && '' !== $pushoverUserToken) { | ||||
|             Log::debug('Enabled pushover.'); | ||||
|             $channels[] = PushoverChannel::class; | ||||
|         } | ||||
| 
 | ||||
|         Log::debug(sprintf('Final channel set in ReturnsAvailableChannels: %s ', implode(', ', $channels))); | ||||
| 
 | ||||
|         return $channels; | ||||
|     } | ||||
| 
 | ||||
|     private static function returnUserChannels(User $user): array | ||||
|     { | ||||
|         $channels          = ['mail']; | ||||
|         $slackUrl          = app('preferences')->getEncryptedForUser($user, 'slack_webhook_url', '')->data; | ||||
|         if (UrlValidator::isValidWebhookURL($slackUrl)) { | ||||
|             $channels[] = 'slack'; | ||||
|         } | ||||
| 
 | ||||
|         // validate presence of of Ntfy settings.
 | ||||
|         if ('' !== (string) app('preferences')->getEncryptedForUser($user, 'ntfy_topic', '')->data) { | ||||
|             Log::debug('Enabled ntfy.'); | ||||
|             $channels[] = NtfyChannel::class; | ||||
|         } | ||||
|         if ('' === (string) app('preferences')->getEncryptedForUser($user, 'ntfy_topic', '')->data) { | ||||
|             Log::warning('No topic name for Ntfy, channel is disabled.'); | ||||
|         } | ||||
| 
 | ||||
|         // pushover
 | ||||
|         $pushoverAppToken  = (string) app('preferences')->getEncryptedForUser($user, 'pushover_app_token', '')->data; | ||||
|         $pushoverUserToken = (string) app('preferences')->getEncryptedForUser($user, 'pushover_user_token', '')->data; | ||||
|         if ('' === $pushoverAppToken || '' === $pushoverUserToken) { | ||||
|             Log::warning('[b] No Pushover token, channel is disabled.'); | ||||
|         } | ||||
|         if ('' !== $pushoverAppToken && '' !== $pushoverUserToken) { | ||||
|             Log::debug('Enabled pushover.'); | ||||
|             $channels[] = PushoverChannel::class; | ||||
|         } | ||||
| 
 | ||||
|         Log::debug(sprintf('Final channel set in ReturnsAvailableChannels (user): %s ', implode(', ', $channels))); | ||||
| 
 | ||||
|         // only the owner can get notifications over
 | ||||
|         return $channels; | ||||
|     } | ||||
| } | ||||
| @@ -1,81 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * ReturnsSettings.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\Notifications; | ||||
| 
 | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Support\Facades\FireflyConfig; | ||||
| use FireflyIII\Support\Facades\Preferences; | ||||
| use FireflyIII\User; | ||||
| 
 | ||||
| class ReturnsSettings | ||||
| { | ||||
|     public static function getSettings(string $channel, string $type, ?User $user): array | ||||
|     { | ||||
|         if ('ntfy' === $channel) { | ||||
|             return self::getNtfySettings($type, $user); | ||||
|         } | ||||
| 
 | ||||
|         throw new FireflyException(sprintf('Cannot handle channel "%s"', $channel)); | ||||
|     } | ||||
| 
 | ||||
|     private static function getNtfySettings(string $type, ?User $user) | ||||
|     { | ||||
|         $settings = [ | ||||
|             'ntfy_server' => 'https://ntfy.sh', | ||||
|             'ntfy_topic'  => '', | ||||
|             'ntfy_auth'   => false, | ||||
|             'ntfy_user'   => '', | ||||
|             'ntfy_pass'   => '', | ||||
| 
 | ||||
|         ]; | ||||
|         if ('user' === $type && null !== $user) { | ||||
|             $settings['ntfy_server'] = Preferences::getEncryptedForUser($user, 'ntfy_server', 'https://ntfy.sh')->data; | ||||
|             $settings['ntfy_topic']  = Preferences::getEncryptedForUser($user, 'ntfy_topic', '')->data; | ||||
|             $settings['ntfy_auth']   = Preferences::getForUser($user, 'ntfy_auth', false)->data; | ||||
|             $settings['ntfy_user']   = Preferences::getEncryptedForUser($user, 'ntfy_user', '')->data; | ||||
|             $settings['ntfy_pass']   = Preferences::getEncryptedForUser($user, 'ntfy_pass', '')->data; | ||||
|         } | ||||
|         if ('owner' === $type) { | ||||
|             $settings['ntfy_server'] = FireflyConfig::getEncrypted('ntfy_server', 'https://ntfy.sh')->data; | ||||
|             $settings['ntfy_topic']  = FireflyConfig::getEncrypted('ntfy_topic', '')->data; | ||||
|             $settings['ntfy_auth']   = FireflyConfig::get('ntfy_auth', false)->data; | ||||
|             $settings['ntfy_user']   = FireflyConfig::getEncrypted('ntfy_user', '')->data; | ||||
|             $settings['ntfy_pass']   = FireflyConfig::getEncrypted('ntfy_pass', '')->data; | ||||
|         } | ||||
| 
 | ||||
|         // overrule config.
 | ||||
|         config(['ntfy-notification-channel.server' => $settings['ntfy_server']]); | ||||
|         config(['ntfy-notification-channel.topic' => $settings['ntfy_topic']]); | ||||
| 
 | ||||
|         if ($settings['ntfy_auth']) { | ||||
|             // overrule auth as well.
 | ||||
|             config(['ntfy-notification-channel.authentication.enabled' => true]); | ||||
|             config(['ntfy-notification-channel.authentication.username' => $settings['ntfy_user']]); | ||||
|             config(['ntfy-notification-channel.authentication.password' => $settings['ntfy_pass']]); | ||||
|         } | ||||
| 
 | ||||
|         return $settings; | ||||
|     } | ||||
| } | ||||
| @@ -24,82 +24,95 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\Security; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| class DisabledMFANotification extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private User $user; | ||||
|     private User   $user; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         $subject = (string) trans('email.disabled_mfa_subject'); | ||||
|         $subject = (string)trans('email.disabled_mfa_subject'); | ||||
| 
 | ||||
|         return (new MailMessage())->markdown('emails.security.disabled-mfa', ['user' => $this->user])->subject($subject); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.disabled_mfa_subject')); | ||||
|         $message->body((string) trans('email.disabled_mfa_slack', ['email' => $this->user->email])); | ||||
|         $message = (string)trans('email.disabled_mfa_slack', ['email' => $this->user->email]); | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new SlackMessage())->content($message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.disabled_mfa_slack', ['email' => $this->user->email])) | ||||
|             ->title((string) trans('email.disabled_mfa_subject')) | ||||
|         ; | ||||
|     } | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     { | ||||
|         $message = (string) trans('email.disabled_mfa_slack', ['email' => $this->user->email]); | ||||
| 
 | ||||
|         return new SlackMessage()->content($message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,82 +24,95 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\Security; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| class EnabledMFANotification extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private User $user; | ||||
|     private User   $user; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         $subject = (string) trans('email.enabled_mfa_subject'); | ||||
|         $subject = (string)trans('email.enabled_mfa_subject'); | ||||
| 
 | ||||
|         return (new MailMessage())->markdown('emails.security.enabled-mfa', ['user' => $this->user])->subject($subject); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.enabled_mfa_subject')); | ||||
|         $message->body((string) trans('email.enabled_mfa_slack', ['email' => $this->user->email])); | ||||
|         $message = (string)trans('email.enabled_mfa_slack', ['email' => $this->user->email]); | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new SlackMessage())->content($message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.enabled_mfa_slack', ['email' => $this->user->email])) | ||||
|             ->title((string) trans('email.enabled_mfa_subject')) | ||||
|         ; | ||||
|     } | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     { | ||||
|         $message = (string) trans('email.enabled_mfa_slack', ['email' => $this->user->email]); | ||||
| 
 | ||||
|         return new SlackMessage()->content($message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,23 +24,23 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\Security; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| class MFABackupFewLeftNotification extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private int  $count; | ||||
|     private User $user; | ||||
|     private User   $user; | ||||
|     private int $count; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(User $user, int $count) | ||||
|     { | ||||
|         $this->user  = $user; | ||||
| @@ -48,60 +48,73 @@ class MFABackupFewLeftNotification extends Notification | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         $subject = (string) trans('email.mfa_few_backups_left_subject', ['count' => $this->count]); | ||||
|         $subject = (string)trans('email.mfa_few_backups_left_subject', ['count' => $this->count]); | ||||
| 
 | ||||
|         return (new MailMessage())->markdown('emails.security.few-backup-codes', ['user' => $this->user, 'count' => $this->count])->subject($subject); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $message = (string) trans('email.mfa_few_backups_left_slack', ['email' => $this->user->email, 'count' => $this->count]); | ||||
|         $message = (string)trans('email.mfa_few_backups_left_slack', ['email' => $this->user->email, 'count' => $this->count]); | ||||
| 
 | ||||
|         return new SlackMessage()->content($message); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.mfa_few_backups_left_subject')); | ||||
|         $message->body((string) trans('email.mfa_few_backups_left_slack', ['email' => $this->user->email, 'count' => $this->count])); | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new SlackMessage())->content($message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.mfa_few_backups_left_slack', ['email' => $this->user->email, 'count' => $this->count])) | ||||
|             ->title((string) trans('email.mfa_few_backups_left_subject')) | ||||
|         ; | ||||
|     } | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,82 +24,95 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\Security; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| class MFABackupNoLeftNotification extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private User $user; | ||||
|     private User   $user; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         $subject = (string) trans('email.mfa_no_backups_left_subject'); | ||||
|         $subject = (string)trans('email.mfa_no_backups_left_subject'); | ||||
| 
 | ||||
|         return (new MailMessage())->markdown('emails.security.no-backup-codes', ['user' => $this->user])->subject($subject); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $message = (string) trans('email.mfa_no_backups_left_slack', ['email' => $this->user->email]); | ||||
|         $message = (string)trans('email.mfa_no_backups_left_slack', ['email' => $this->user->email]); | ||||
| 
 | ||||
|         return new SlackMessage()->content($message); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.mfa_no_backups_left_subject')); | ||||
|         $message->body((string) trans('email.mfa_no_backups_left_slack', ['email' => $this->user->email])); | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new SlackMessage())->content($message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.mfa_few_backups_left_slack', ['email' => $this->user->email])) | ||||
|             ->title((string) trans('email.mfa_no_backups_left_slack')) | ||||
|         ; | ||||
|     } | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,81 +24,97 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\Security; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| class MFAManyFailedAttemptsNotification extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private int  $count; | ||||
|     private User $user; | ||||
|     private User   $user; | ||||
|     private int $count; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(User $user, int $count) | ||||
|     { | ||||
|         $this->user  = $user; | ||||
|         $this->count = $count; | ||||
|     } | ||||
| 
 | ||||
|     public function toArray(User $notifiable) | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         $subject = (string) trans('email.mfa_many_failed_subject', ['count' => $this->count]); | ||||
|         $subject = (string)trans('email.mfa_many_failed_subject', ['count' => $this->count]); | ||||
| 
 | ||||
|         return (new MailMessage())->markdown('emails.security.many-failed-attempts', ['user' => $this->user, 'count' => $this->count])->subject($subject); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $message = (string) trans('email.mfa_many_failed_slack', ['email' => $this->user->email, 'count' => $this->count]); | ||||
|         $message = (string)trans('email.mfa_many_failed_slack', ['email' => $this->user->email, 'count' => $this->count]); | ||||
| 
 | ||||
|         return new SlackMessage()->content($message); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.mfa_many_failed_subject')); | ||||
|         $message->body((string) trans('email.mfa_many_failed_slack', ['email' => $this->user->email, 'count' => $this->count])); | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new SlackMessage())->content($message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.mfa_many_failed_slack', ['email' => $this->user->email, 'count' => $this->count])) | ||||
|             ->title((string) trans('email.mfa_many_failed_subject')) | ||||
|         ; | ||||
|     } | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,82 +24,95 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\Security; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| class MFAUsedBackupCodeNotification extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private User $user; | ||||
|     private User   $user; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         $subject = (string) trans('email.used_backup_code_subject'); | ||||
|         $subject = (string)trans('email.used_backup_code_subject'); | ||||
| 
 | ||||
|         return (new MailMessage())->markdown('emails.security.used-backup-code', ['user' => $this->user])->subject($subject); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $message = (string) trans('email.used_backup_code_slack', ['email' => $this->user->email]); | ||||
|         $message = (string)trans('email.used_backup_code_slack', ['email' => $this->user->email]); | ||||
| 
 | ||||
|         return new SlackMessage()->content($message); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.used_backup_code_subject')); | ||||
|         $message->body((string) trans('email.used_backup_code_slack', ['email' => $this->user->email])); | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new SlackMessage())->content($message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.used_backup_code_slack', ['email' => $this->user->email])) | ||||
|             ->title((string) trans('email.used_backup_code_subject')) | ||||
|         ; | ||||
|     } | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,82 +24,95 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\Security; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| class NewBackupCodesNotification extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private User $user; | ||||
|     private User   $user; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         $subject = (string) trans('email.new_backup_codes_subject'); | ||||
|         $subject = (string)trans('email.new_backup_codes_subject'); | ||||
| 
 | ||||
|         return (new MailMessage())->markdown('emails.security.new-backup-codes', ['user' => $this->user])->subject($subject); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $message = (string) trans('email.new_backup_codes_slack', ['email' => $this->user->email]); | ||||
|         $message = (string)trans('email.new_backup_codes_slack', ['email' => $this->user->email]); | ||||
| 
 | ||||
|         return new SlackMessage()->content($message); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.new_backup_codes_subject')); | ||||
|         $message->body((string) trans('email.new_backup_codes_slack', ['email' => $this->user->email])); | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new SlackMessage())->content($message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.new_backup_codes_slack', ['email' => $this->user->email])) | ||||
|             ->title((string) trans('email.new_backup_codes_subject')) | ||||
|         ; | ||||
|     } | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,102 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * UserFailedLoginAttempt.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\Notifications\Security; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| class UserFailedLoginAttempt extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private User $user; | ||||
| 
 | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     public function toArray(User $notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     { | ||||
|         $subject = (string) trans('email.failed_login_subject'); | ||||
| 
 | ||||
|         return (new MailMessage())->markdown('emails.security.failed-login', ['user' => $this->user])->subject($subject); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     { | ||||
|         $message = (string) trans('email.failed_login_message', ['email' => $this->user->email]); | ||||
| 
 | ||||
|         return new SlackMessage()->content($message); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.failed_login_subject')); | ||||
|         $message->body((string) trans('email.failed_login_message', ['email' => $this->user->email])); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.failed_login_message', ['email' => $this->user->email])) | ||||
|             ->title((string) trans('email.failed_login_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|     } | ||||
| } | ||||
| @@ -1,75 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestNotification.php | ||||
|  * Copyright (c) 2022 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\Notifications\Test; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| 
 | ||||
| /** | ||||
|  * Class TestNotification | ||||
|  */ | ||||
| class OwnerTestNotificationEmail extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private OwnerNotifiable $owner; | ||||
| 
 | ||||
|     public function __construct(OwnerNotifiable $owner) | ||||
|     { | ||||
|         $this->owner = $owner; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         $address = (string) config('firefly.site_owner'); | ||||
| 
 | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.admin-test', ['email' => $address]) | ||||
|             ->subject((string) trans('email.admin_test_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
| @@ -1,83 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestNotification.php | ||||
|  * Copyright (c) 2022 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\Notifications\Test; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use Ntfy\Message; | ||||
| use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel; | ||||
| 
 | ||||
| // use Illuminate\Notifications\Slack\SlackMessage;
 | ||||
| 
 | ||||
| /** | ||||
|  * Class TestNotification | ||||
|  */ | ||||
| class OwnerTestNotificationNtfy extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     public OwnerNotifiable $owner; | ||||
| 
 | ||||
|     public function __construct(OwnerNotifiable $owner) | ||||
|     { | ||||
|         $this->owner = $owner; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      */ | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toNtfy(OwnerNotifiable $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.admin_test_subject')); | ||||
|         $message->body((string) trans('email.admin_test_message', ['channel' => 'ntfy'])); | ||||
|         $message->tags(['white_check_mark']); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return [NtfyChannel::class]; | ||||
|     } | ||||
| } | ||||
| @@ -1,78 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestNotification.php | ||||
|  * Copyright (c) 2022 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\Notifications\Test; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use NotificationChannels\Pushover\PushoverChannel; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| 
 | ||||
| // use Illuminate\Notifications\Slack\SlackMessage;
 | ||||
| 
 | ||||
| /** | ||||
|  * Class TestNotification | ||||
|  */ | ||||
| class OwnerTestNotificationPushover extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private OwnerNotifiable $owner; | ||||
| 
 | ||||
|     public function __construct(OwnerNotifiable $owner) | ||||
|     { | ||||
|         $this->owner = $owner; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(OwnerNotifiable $notifiable): PushoverMessage | ||||
|     { | ||||
|         Log::debug('Now in toPushover()'); | ||||
| 
 | ||||
|         return PushoverMessage::create((string) trans('email.admin_test_message', ['channel' => 'Pushover'])) | ||||
|             ->title((string) trans('email.admin_test_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return [PushoverChannel::class]; | ||||
|     } | ||||
| } | ||||
| @@ -1,73 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestNotification.php | ||||
|  * Copyright (c) 2022 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\Notifications\Test; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| 
 | ||||
| // use Illuminate\Notifications\Slack\SlackMessage;
 | ||||
| 
 | ||||
| /** | ||||
|  * Class TestNotification | ||||
|  */ | ||||
| class OwnerTestNotificationSlack extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private OwnerNotifiable $owner; | ||||
| 
 | ||||
|     public function __construct(OwnerNotifiable $owner) | ||||
|     { | ||||
|         $this->owner = $owner; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return new SlackMessage()->content((string) trans('email.admin_test_subject')); | ||||
|         // return new SlackMessage()->text((string) trans('email.admin_test_subject'))->to($url);
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return ['slack']; | ||||
|     } | ||||
| } | ||||
| @@ -1,72 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestNotification.php | ||||
|  * Copyright (c) 2022 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\Notifications\Test; | ||||
| 
 | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| 
 | ||||
| /** | ||||
|  * Class TestNotification | ||||
|  */ | ||||
| class UserTestNotificationEmail extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private User $user; | ||||
| 
 | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function toMail(User $notifiable) | ||||
|     { | ||||
|         $address = (string) $notifiable->email; | ||||
| 
 | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.admin-test', ['email' => $address]) | ||||
|             ->subject((string) trans('email.admin_test_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
| @@ -1,81 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestNotification.php | ||||
|  * Copyright (c) 2022 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\Notifications\Test; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use Ntfy\Message; | ||||
| use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel; | ||||
| 
 | ||||
| // use Illuminate\Notifications\Slack\SlackMessage;
 | ||||
| 
 | ||||
| /** | ||||
|  * Class TestNotification | ||||
|  */ | ||||
| class UserTestNotificationNtfy extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     public User $user; | ||||
| 
 | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toNtfy(User $user): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $user); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.admin_test_subject')); | ||||
|         $message->body((string) trans('email.admin_test_message', ['channel' => 'ntfy'])); | ||||
|         $message->tags(['white_check_mark']); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $user) | ||||
|     { | ||||
|         return [NtfyChannel::class]; | ||||
|     } | ||||
| } | ||||
| @@ -1,78 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestNotification.php | ||||
|  * Copyright (c) 2022 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\Notifications\Test; | ||||
| 
 | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use NotificationChannels\Pushover\PushoverChannel; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| 
 | ||||
| // use Illuminate\Notifications\Slack\SlackMessage;
 | ||||
| 
 | ||||
| /** | ||||
|  * Class TestNotification | ||||
|  */ | ||||
| class UserTestNotificationPushover extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private User $user; | ||||
| 
 | ||||
|     public function __construct(User $user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     { | ||||
|         Log::debug('Now in (user) toPushover()'); | ||||
| 
 | ||||
|         return PushoverMessage::create((string) trans('email.admin_test_message', ['channel' => 'Pushover'])) | ||||
|             ->title((string) trans('email.admin_test_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return [PushoverChannel::class]; | ||||
|     } | ||||
| } | ||||
| @@ -1,76 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| /* | ||||
|  * TestNotification.php | ||||
|  * Copyright (c) 2022 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\Notifications\Test; | ||||
| 
 | ||||
| use FireflyIII\Notifications\Notifiables\OwnerNotifiable; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| 
 | ||||
| // use Illuminate\Notifications\Slack\SlackMessage;
 | ||||
| 
 | ||||
| /** | ||||
|  * Class TestNotification | ||||
|  */ | ||||
| class UserTestNotificationSlack extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     private OwnerNotifiable $owner; | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function __construct(OwnerNotifiable $owner) | ||||
|     { | ||||
|         $this->owner = $owner; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return new SlackMessage()->content((string) trans('email.admin_test_subject')); | ||||
|         // return new SlackMessage()->text((string) trans('email.admin_test_subject'))->to($url);
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(OwnerNotifiable $notifiable) | ||||
|     { | ||||
|         return ['slack']; | ||||
|     } | ||||
| } | ||||
| @@ -25,15 +25,12 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Notifications\User; | ||||
| 
 | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| /** | ||||
|  * Class BillReminder | ||||
| @@ -46,6 +43,9 @@ class BillReminder extends Notification | ||||
|     private int    $diff; | ||||
|     private string $field; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(Bill $bill, string $field, int $diff) | ||||
|     { | ||||
|         $this->bill  = $bill; | ||||
| @@ -54,78 +54,90 @@ class BillReminder extends Notification | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.bill-warning', ['field' => $this->field, 'diff' => $this->diff, 'bill' => $this->bill]) | ||||
|             ->subject($this->getSubject()) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     { | ||||
|         $bill = $this->bill; | ||||
|         $url  = route('bills.show', [$bill->id]); | ||||
| 
 | ||||
|         return new SlackMessage() | ||||
|             ->warning() | ||||
|             ->attachment(static function ($attachment) use ($bill, $url): void { | ||||
|                 $attachment->title((string) trans('firefly.visit_bill', ['name' => $bill->name]), $url); | ||||
|             }) | ||||
|             ->content($this->getSubject()) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title($this->getSubject()); | ||||
|         $message->body((string) trans('email.bill_warning_please_action')); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.bill_warning_please_action')) | ||||
|             ->title($this->getSubject()) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     private function getSubject(): string | ||||
|     { | ||||
|         $message = (string) trans(sprintf('email.bill_warning_subject_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]); | ||||
|         $subject = (string)trans(sprintf('email.bill_warning_subject_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]); | ||||
|         if (0 === $this->diff) { | ||||
|             $message = (string) trans(sprintf('email.bill_warning_subject_now_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]); | ||||
|             $subject = (string)trans(sprintf('email.bill_warning_subject_now_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]); | ||||
|         } | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.bill-warning', ['field' => $this->field, 'diff' => $this->diff, 'bill' => $this->bill]) | ||||
|             ->subject($subject) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         $message = (string)trans(sprintf('email.bill_warning_subject_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]); | ||||
|         if (0 === $this->diff) { | ||||
|             $message = (string)trans(sprintf('email.bill_warning_subject_now_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]); | ||||
|         } | ||||
|         $bill    = $this->bill; | ||||
|         $url     = route('bills.show', [$bill->id]); | ||||
| 
 | ||||
|         return (new SlackMessage()) | ||||
|             ->warning() | ||||
|             ->attachment(static function ($attachment) use ($bill, $url): void { | ||||
|                 $attachment->title((string)trans('firefly.visit_bill', ['name' => $bill->name]), $url); | ||||
|             }) | ||||
|             ->content($message) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,15 +24,12 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\User; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| /** | ||||
|  * Class NewAccessToken | ||||
| @@ -41,59 +38,78 @@ class NewAccessToken extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct() {} | ||||
| 
 | ||||
|     public function toArray(User $notifiable) | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.token-created') | ||||
|             ->subject((string) trans('email.access_token_created_subject')) | ||||
|             ->subject((string)trans('email.access_token_created_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         return new SlackMessage()->content((string) trans('email.access_token_created_body')); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.access_token_created_subject')); | ||||
|         $message->body((string) trans('email.access_token_created_body')); | ||||
| 
 | ||||
|         return $message; | ||||
|         return (new SlackMessage())->content((string)trans('email.access_token_created_body')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.access_token_created_body')) | ||||
|             ->title((string) trans('email.access_token_created_subject')) | ||||
|         ; | ||||
|     } | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,14 +24,11 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\User; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| /** | ||||
|  * Class RuleActionFailed | ||||
| @@ -46,6 +43,9 @@ class RuleActionFailed extends Notification | ||||
|     private string $ruleLink; | ||||
|     private string $ruleTitle; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(array $params) | ||||
|     { | ||||
|         [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink] = $params; | ||||
| @@ -57,59 +57,67 @@ class RuleActionFailed extends Notification | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $groupTitle = $this->groupTitle; | ||||
|         $groupLink  = $this->groupLink; | ||||
|         $ruleTitle  = $this->ruleTitle; | ||||
|         $ruleLink   = $this->ruleLink; | ||||
| 
 | ||||
|         return new SlackMessage()->content($this->message)->attachment(static function ($attachment) use ($groupTitle, $groupLink): void { | ||||
|             $attachment->title((string) trans('rules.inspect_transaction', ['title' => $groupTitle]), $groupLink); | ||||
|         return (new SlackMessage())->content($this->message)->attachment(static function ($attachment) use ($groupTitle, $groupLink): void { | ||||
|             $attachment->title((string)trans('rules.inspect_transaction', ['title' => $groupTitle]), $groupLink); | ||||
|         })->attachment(static function ($attachment) use ($ruleTitle, $ruleLink): void { | ||||
|             $attachment->title((string) trans('rules.inspect_rule', ['title' => $ruleTitle]), $ruleLink); | ||||
|             $attachment->title((string)trans('rules.inspect_rule', ['title' => $ruleTitle]), $ruleLink); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->body($this->message); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return PushoverMessage::create($this->message); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         $channels = ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         if (($key = array_search('mail', $channels, true)) !== false) { | ||||
|             unset($channels[$key]); | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             app('log')->debug('Will send ruleActionFailed through Slack or Discord!'); | ||||
| 
 | ||||
|         return $channels; | ||||
|             return ['slack']; | ||||
|         } | ||||
|         app('log')->debug('Will NOT send ruleActionFailed through Slack or Discord'); | ||||
| 
 | ||||
|         return []; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,6 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\User; | ||||
| 
 | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| @@ -38,24 +37,39 @@ class TransactionCreation extends Notification | ||||
| 
 | ||||
|     private array $collection; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(array $collection) | ||||
|     { | ||||
|         $this->collection = $collection; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.report-new-journals', ['transformed' => $this->collection]) | ||||
| @@ -64,9 +78,15 @@ class TransactionCreation extends Notification | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return ['mail']; | ||||
|     } | ||||
|   | ||||
| @@ -25,15 +25,12 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Notifications\User; | ||||
| 
 | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\Support\Notifications\UrlValidator; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| /** | ||||
|  * Class UserLogin | ||||
| @@ -44,68 +41,69 @@ class UserLogin extends Notification | ||||
| 
 | ||||
|     private string $ip; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(string $ip) | ||||
|     { | ||||
|         $this->ip = $ip; | ||||
|     } | ||||
| 
 | ||||
|     public function toArray(User $notifiable) | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         $time = now(config('app.timezone'))->isoFormat((string) trans('config.date_time_js')); | ||||
|         $time = now(config('app.timezone'))->isoFormat((string)trans('config.date_time_js')); | ||||
|         $host = ''; | ||||
| 
 | ||||
|         try { | ||||
|             $hostName = app('steam')->getHostName($this->ip); | ||||
|         } catch (FireflyException $e) { | ||||
|             app('log')->error($e->getMessage()); | ||||
|             $hostName = $this->ip; | ||||
|         } | ||||
|         if ($hostName !== $this->ip) { | ||||
|             $host = $hostName; | ||||
|         } | ||||
| 
 | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.new-ip', ['time' => $time, 'ipAddress' => $this->ip, 'host' => $this->getHost()]) | ||||
|             ->subject((string) trans('email.login_from_new_ip')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->title((string) trans('email.login_from_new_ip')); | ||||
|         $message->body((string) trans('email.slack_login_from_new_ip', ['host' => $this->getHost(), 'ip' => $this->ip])); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.slack_login_from_new_ip', ['host' => $this->getHost(), 'ip' => $this->ip])) | ||||
|             ->title((string) trans('email.login_from_new_ip')) | ||||
|             ->markdown('emails.new-ip', ['time' => $time, 'ipAddress' => $this->ip, 'host' => $host]) | ||||
|             ->subject((string)trans('email.login_from_new_ip')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the Slack representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return SlackMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     { | ||||
|         return new SlackMessage()->content((string) trans('email.slack_login_from_new_ip', ['host' => $this->getHost(), 'ip' => $this->ip])); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|     } | ||||
| 
 | ||||
|     private function getHost(): string | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $host = ''; | ||||
| 
 | ||||
| @@ -119,6 +117,30 @@ class UserLogin extends Notification | ||||
|             $host = $hostName; | ||||
|         } | ||||
| 
 | ||||
|         return $host; | ||||
|         return (new SlackMessage())->content((string)trans('email.slack_login_from_new_ip', ['host' => $host, 'ip' => $this->ip])); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         /** @var null|User $user */ | ||||
|         $user     = auth()->user(); | ||||
|         $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; | ||||
|         if (is_array($slackUrl)) { | ||||
|             $slackUrl = ''; | ||||
|         } | ||||
|         if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { | ||||
|             return ['mail', 'slack']; | ||||
|         } | ||||
| 
 | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,15 +24,9 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\User; | ||||
| 
 | ||||
| use FireflyIII\Notifications\ReturnsAvailableChannels; | ||||
| use FireflyIII\Notifications\ReturnsSettings; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use NotificationChannels\Pushover\PushoverMessage; | ||||
| use Ntfy\Message; | ||||
| 
 | ||||
| /** | ||||
|  * Class UserNewPassword | ||||
| @@ -43,59 +37,57 @@ class UserNewPassword extends Notification | ||||
| 
 | ||||
|     private string $url; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct(string $url) | ||||
|     { | ||||
|         $this->url = $url; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.password', ['url' => $this->url]) | ||||
|             ->subject((string) trans('email.reset_pw_subject')) | ||||
|             ->subject((string)trans('email.reset_pw_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toSlack(User $notifiable) | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return new SlackMessage()->content((string) trans('email.reset_pw_message')); | ||||
|     } | ||||
| 
 | ||||
|     public function toNtfy(User $notifiable): Message | ||||
|     { | ||||
|         $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); | ||||
|         $message  = new Message(); | ||||
|         $message->topic($settings['ntfy_topic']); | ||||
|         $message->body((string) trans('email.reset_pw_message')); | ||||
| 
 | ||||
|         return $message; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toPushover(User $notifiable): PushoverMessage | ||||
|     { | ||||
|         return PushoverMessage::create((string) trans('email.reset_pw_message')); | ||||
|     } | ||||
| 
 | ||||
|     public function via(User $notifiable) | ||||
|     { | ||||
|         return ReturnsAvailableChannels::returnChannels('user', $notifiable); | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,6 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Notifications\User; | ||||
| 
 | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| @@ -36,34 +35,54 @@ class UserRegistration extends Notification | ||||
| { | ||||
|     use Queueable; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      */ | ||||
|     public function __construct() {} | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toArray(User $notifiable) | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return MailMessage | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function toMail(User $notifiable) | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|         return (new MailMessage()) | ||||
|             ->markdown('emails.registered', ['address' => route('index')]) | ||||
|             ->subject((string) trans('email.registered_subject')) | ||||
|             ->subject((string)trans('email.registered_subject')) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param mixed $notifiable | ||||
|      * | ||||
|      * @return array | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||
|      */ | ||||
|     public function via(User $notifiable) | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         // other settings will not be available at this point anyway.
 | ||||
|         return ['mail']; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,6 +25,7 @@ namespace FireflyIII\Providers; | ||||
| 
 | ||||
| use FireflyIII\Events\ActuallyLoggedIn; | ||||
| use FireflyIII\Events\Admin\InvitationCreated; | ||||
| use FireflyIII\Events\AdminRequestedTestMessage; | ||||
| use FireflyIII\Events\DestroyedTransactionGroup; | ||||
| use FireflyIII\Events\DetectedNewIPAddress; | ||||
| use FireflyIII\Events\Model\BudgetLimit\Created; | ||||
| @@ -46,12 +47,8 @@ use FireflyIII\Events\Security\MFABackupNoLeft; | ||||
| use FireflyIII\Events\Security\MFAManyFailedAttempts; | ||||
| use FireflyIII\Events\Security\MFANewBackupCodes; | ||||
| use FireflyIII\Events\Security\MFAUsedBackupCode; | ||||
| use FireflyIII\Events\Security\UnknownUserAttemptedLogin; | ||||
| use FireflyIII\Events\Security\UserAttemptedLogin; | ||||
| use FireflyIII\Events\StoredAccount; | ||||
| use FireflyIII\Events\StoredTransactionGroup; | ||||
| use FireflyIII\Events\Test\OwnerTestNotificationChannel; | ||||
| use FireflyIII\Events\Test\UserTestNotificationChannel; | ||||
| use FireflyIII\Events\TriggeredAuditLog; | ||||
| use FireflyIII\Events\UpdatedAccount; | ||||
| use FireflyIII\Events\UpdatedTransactionGroup; | ||||
| @@ -103,148 +100,139 @@ class EventServiceProvider extends ServiceProvider | ||||
|     protected $listen | ||||
|         = [ | ||||
|             // is a User related event.
 | ||||
|             RegisteredUser::class                           => [ | ||||
|             RegisteredUser::class                      => [ | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationMail', | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@sendAdminRegistrationNotification', | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@attachUserRole', | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@createGroupMembership', | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@createExchangeRates', | ||||
|             ], | ||||
|             UserAttemptedLogin::class                       => [ | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@sendLoginAttemptNotification', | ||||
|             ], | ||||
|             // is a User related event.
 | ||||
|             Login::class                                    => [ | ||||
|             Login::class                               => [ | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin', | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish', | ||||
|             ], | ||||
|             ActuallyLoggedIn::class                         => [ | ||||
|             ActuallyLoggedIn::class                    => [ | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress', | ||||
|             ], | ||||
|             DetectedNewIPAddress::class                     => [ | ||||
|             DetectedNewIPAddress::class                => [ | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@notifyNewIPAddress', | ||||
|             ], | ||||
|             RequestedVersionCheckStatus::class              => [ | ||||
|             RequestedVersionCheckStatus::class         => [ | ||||
|                 'FireflyIII\Handlers\Events\VersionCheckEventHandler@checkForUpdates', | ||||
|             ], | ||||
|             RequestedReportOnJournals::class                => [ | ||||
|             RequestedReportOnJournals::class           => [ | ||||
|                 'FireflyIII\Handlers\Events\AutomationHandler@reportJournals', | ||||
|             ], | ||||
| 
 | ||||
|             // is a User related event.
 | ||||
|             RequestedNewPassword::class                     => [ | ||||
|             RequestedNewPassword::class                => [ | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@sendNewPassword', | ||||
|             ], | ||||
|             UserTestNotificationChannel::class              => [ | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@sendTestNotification', | ||||
|             ], | ||||
|             // is a User related event.
 | ||||
|             UserChangedEmail::class                         => [ | ||||
|             UserChangedEmail::class                    => [ | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeConfirmMail', | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeUndoMail', | ||||
|             ], | ||||
|             // admin related
 | ||||
|             OwnerTestNotificationChannel::class             => [ | ||||
|                 'FireflyIII\Handlers\Events\AdminEventHandler@sendTestNotification', | ||||
|             AdminRequestedTestMessage::class           => [ | ||||
|                 'FireflyIII\Handlers\Events\AdminEventHandler@sendTestMessage', | ||||
|             ], | ||||
|             NewVersionAvailable::class                      => [ | ||||
|             NewVersionAvailable::class                 => [ | ||||
|                 'FireflyIII\Handlers\Events\AdminEventHandler@sendNewVersion', | ||||
|             ], | ||||
|             InvitationCreated::class                        => [ | ||||
|             InvitationCreated::class                   => [ | ||||
|                 'FireflyIII\Handlers\Events\AdminEventHandler@sendInvitationNotification', | ||||
|                 'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationInvite', | ||||
|             ], | ||||
|             UnknownUserAttemptedLogin::class                => [ | ||||
|                 'FireflyIII\Handlers\Events\AdminEventHandler@sendLoginAttemptNotification', | ||||
|             ], | ||||
| 
 | ||||
|             // is a Transaction Journal related event.
 | ||||
|             StoredTransactionGroup::class                   => [ | ||||
|             StoredTransactionGroup::class              => [ | ||||
|                 'FireflyIII\Handlers\Events\StoredGroupEventHandler@processRules', | ||||
|                 'FireflyIII\Handlers\Events\StoredGroupEventHandler@recalculateCredit', | ||||
|                 'FireflyIII\Handlers\Events\StoredGroupEventHandler@triggerWebhooks', | ||||
|             ], | ||||
|             // is a Transaction Journal related event.
 | ||||
|             UpdatedTransactionGroup::class                  => [ | ||||
|             UpdatedTransactionGroup::class             => [ | ||||
|                 'FireflyIII\Handlers\Events\UpdatedGroupEventHandler@unifyAccounts', | ||||
|                 'FireflyIII\Handlers\Events\UpdatedGroupEventHandler@processRules', | ||||
|                 'FireflyIII\Handlers\Events\UpdatedGroupEventHandler@recalculateCredit', | ||||
|                 'FireflyIII\Handlers\Events\UpdatedGroupEventHandler@triggerWebhooks', | ||||
|             ], | ||||
|             DestroyedTransactionGroup::class                => [ | ||||
|             DestroyedTransactionGroup::class           => [ | ||||
|                 'FireflyIII\Handlers\Events\DestroyedGroupEventHandler@triggerWebhooks', | ||||
|             ], | ||||
|             // API related events:
 | ||||
|             AccessTokenCreated::class                       => [ | ||||
|             AccessTokenCreated::class                  => [ | ||||
|                 'FireflyIII\Handlers\Events\APIEventHandler@accessTokenCreated', | ||||
|             ], | ||||
| 
 | ||||
|             // Webhook related event:
 | ||||
|             RequestedSendWebhookMessages::class             => [ | ||||
|             RequestedSendWebhookMessages::class        => [ | ||||
|                 'FireflyIII\Handlers\Events\WebhookEventHandler@sendWebhookMessages', | ||||
|             ], | ||||
| 
 | ||||
|             // account related events:
 | ||||
|             StoredAccount::class                            => [ | ||||
|             StoredAccount::class                       => [ | ||||
|                 'FireflyIII\Handlers\Events\StoredAccountEventHandler@recalculateCredit', | ||||
|             ], | ||||
|             UpdatedAccount::class                           => [ | ||||
|             UpdatedAccount::class                      => [ | ||||
|                 'FireflyIII\Handlers\Events\UpdatedAccountEventHandler@recalculateCredit', | ||||
|             ], | ||||
| 
 | ||||
|             // bill related events:
 | ||||
|             WarnUserAboutBill::class                        => [ | ||||
|             WarnUserAboutBill::class                   => [ | ||||
|                 'FireflyIII\Handlers\Events\BillEventHandler@warnAboutBill', | ||||
|             ], | ||||
| 
 | ||||
|             // audit log events:
 | ||||
|             TriggeredAuditLog::class                        => [ | ||||
|             TriggeredAuditLog::class                   => [ | ||||
|                 'FireflyIII\Handlers\Events\AuditEventHandler@storeAuditEvent', | ||||
|             ], | ||||
|             // piggy bank related events:
 | ||||
|             ChangedAmount::class                            => [ | ||||
|             ChangedAmount::class                       => [ | ||||
|                 'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changePiggyAmount', | ||||
|             ], | ||||
| 
 | ||||
|             // budget related events: CRUD budget limit
 | ||||
|             Created::class                                  => [ | ||||
|             Created::class                             => [ | ||||
|                 'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@created', | ||||
|             ], | ||||
|             Updated::class                                  => [ | ||||
|             Updated::class                             => [ | ||||
|                 'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@updated', | ||||
|             ], | ||||
|             Deleted::class                                  => [ | ||||
|             Deleted::class                             => [ | ||||
|                 'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted', | ||||
|             ], | ||||
| 
 | ||||
|             // rule actions
 | ||||
|             RuleActionFailedOnArray::class                  => [ | ||||
|             RuleActionFailedOnArray::class             => [ | ||||
|                 'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray', | ||||
|             ], | ||||
|             RuleActionFailedOnObject::class                 => [ | ||||
|             RuleActionFailedOnObject::class            => [ | ||||
|                 'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnObject', | ||||
|             ], | ||||
| 
 | ||||
|             // security related
 | ||||
|             EnabledMFA::class                               => [ | ||||
|             EnabledMFA::class                          => [ | ||||
|                 'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAEnabledMail', | ||||
|             ], | ||||
|             DisabledMFA::class                              => [ | ||||
|             DisabledMFA::class                         => [ | ||||
|                 'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFADisabledMail', | ||||
|             ], | ||||
|             MFANewBackupCodes::class                        => [ | ||||
|             MFANewBackupCodes::class                   => [ | ||||
|                 'FireflyIII\Handlers\Events\Security\MFAHandler@sendNewMFABackupCodesMail', | ||||
|             ], | ||||
|             MFAUsedBackupCode::class                        => [ | ||||
|             MFAUsedBackupCode::class                   => [ | ||||
|                 'FireflyIII\Handlers\Events\Security\MFAHandler@sendUsedBackupCodeMail', | ||||
|             ], | ||||
|             MFABackupFewLeft::class                         => [ | ||||
|             MFABackupFewLeft::class                    => [ | ||||
|                 'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupFewLeftMail', | ||||
|             ], | ||||
|             MFABackupNoLeft::class                          => [ | ||||
|             MFABackupNoLeft::class                     => [ | ||||
|                 'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupNoLeftMail', | ||||
|             ], | ||||
|             MFAManyFailedAttempts::class                    => [ | ||||
|             MFAManyFailedAttempts::class               => [ | ||||
|                 'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAFailedAttemptsMail', | ||||
|             ], | ||||
|         ]; | ||||
|   | ||||
| @@ -29,7 +29,6 @@ use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Factory\TransactionCurrencyFactory; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Models\BudgetLimit; | ||||
| use FireflyIII\Models\Note; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Contracts\Auth\Authenticatable; | ||||
| @@ -304,12 +303,6 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface | ||||
|         $limit->amount                  = $data['amount']; | ||||
|         $limit->transaction_currency_id = $currency->id; | ||||
|         $limit->save(); | ||||
| 
 | ||||
|         $noteText                       = (string) ($data['notes'] ?? ''); | ||||
|         if ('' !== $noteText) { | ||||
|             $this->setNoteText($limit, $noteText); | ||||
|         } | ||||
| 
 | ||||
|         app('log')->debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $data['amount'])); | ||||
| 
 | ||||
|         return $limit; | ||||
| @@ -360,11 +353,6 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface | ||||
|         $budgetLimit->transaction_currency_id = $currency->id; | ||||
|         $budgetLimit->save(); | ||||
| 
 | ||||
|         // update notes if they exist.
 | ||||
|         if (array_key_exists('notes', $data)) { | ||||
|             $this->setNoteText($budgetLimit, (string)$data['notes']); | ||||
|         } | ||||
| 
 | ||||
|         return $budgetLimit; | ||||
|     } | ||||
| 
 | ||||
| @@ -427,27 +415,4 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface | ||||
| 
 | ||||
|         return $limit; | ||||
|     } | ||||
| 
 | ||||
|     #[\Override]
 | ||||
|     public function getNoteText(BudgetLimit $budgetLimit): string | ||||
|     { | ||||
|         return (string) $budgetLimit->notes()->first()?->text; | ||||
|     } | ||||
| 
 | ||||
|     #[\Override]
 | ||||
|     public function setNoteText(BudgetLimit $budgetLimit, string $text): void | ||||
|     { | ||||
|         $dbNote = $budgetLimit->notes()->first(); | ||||
|         if ('' !== $text) { | ||||
|             if (null === $dbNote) { | ||||
|                 $dbNote = new Note(); | ||||
|                 $dbNote->noteable()->associate($budgetLimit); | ||||
|             } | ||||
|             $dbNote->text = trim($text); | ||||
|             $dbNote->save(); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         $dbNote?->delete(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -48,10 +48,6 @@ interface BudgetLimitRepositoryInterface | ||||
|      */ | ||||
|     public function destroyAll(): void; | ||||
| 
 | ||||
|     public function getNoteText(BudgetLimit $budgetLimit): string; | ||||
| 
 | ||||
|     public function setNoteText(BudgetLimit $budgetLimit, string $text): void; | ||||
| 
 | ||||
|     /** | ||||
|      * Destroy a budget limit. | ||||
|      */ | ||||
|   | ||||
| @@ -105,10 +105,4 @@ class CurrencyRepository implements CurrencyRepositoryInterface | ||||
|             $this->user = $user; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[\Override]
 | ||||
|     public function find(int $currencyId): ?TransactionCurrency | ||||
|     { | ||||
|         return TransactionCurrency::find($currencyId); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -42,8 +42,6 @@ interface CurrencyRepositoryInterface | ||||
|      */ | ||||
|     public function findByCode(string $currencyCode): ?TransactionCurrency; | ||||
| 
 | ||||
|     public function find(int $currencyId): ?TransactionCurrency; | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the complete set of transactions but needs | ||||
|      * no user object. | ||||
|   | ||||
| @@ -26,15 +26,12 @@ namespace FireflyIII\Repositories\PiggyBank; | ||||
| 
 | ||||
| use FireflyIII\Events\Model\PiggyBank\ChangedAmount; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Factory\PiggyBankFactory; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Note; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Models\PiggyBankRepetition; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; | ||||
| use FireflyIII\Support\Facades\Amount; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Database\QueryException; | ||||
| 
 | ||||
| /** | ||||
|  * Trait ModifiesPiggyBanks | ||||
| @@ -45,7 +42,6 @@ trait ModifiesPiggyBanks | ||||
| 
 | ||||
|     public function addAmountToRepetition(PiggyBankRepetition $repetition, string $amount, TransactionJournal $journal): void | ||||
|     { | ||||
|         throw new FireflyException('[a] Piggy bank repetitions are EOL.'); | ||||
|         app('log')->debug(sprintf('addAmountToRepetition: %s', $amount)); | ||||
|         if (-1 === bccomp($amount, '0')) { | ||||
|             app('log')->debug('Remove amount.'); | ||||
| @@ -57,43 +53,30 @@ trait ModifiesPiggyBanks | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function removeAmount(PiggyBank $piggyBank, Account $account, string $amount, ?TransactionJournal $journal = null): bool | ||||
|     public function removeAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool | ||||
|     { | ||||
|         $currentAmount         = $this->getCurrentAmount($piggyBank, $account); | ||||
|         $pivot                 = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot; | ||||
|         $pivot->current_amount = bcsub($currentAmount, $amount); | ||||
|         $pivot->save(); | ||||
|         $repetition                = $this->getRepetition($piggyBank); | ||||
|         if (null === $repetition) { | ||||
|             return false; | ||||
|         } | ||||
|         $repetition->currentamount = bcsub($repetition->currentamount, $amount); | ||||
|         $repetition->save(); | ||||
| 
 | ||||
|         app('log')->debug('removeAmount [a]: Trigger change for negative amount.'); | ||||
|         app('log')->debug('addAmount [a]: Trigger change for negative amount.'); | ||||
|         event(new ChangedAmount($piggyBank, bcmul($amount, '-1'), $journal, null)); | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public function removeAmountFromAll(PiggyBank $piggyBank, string $amount): void | ||||
|     public function addAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool | ||||
|     { | ||||
|         foreach ($piggyBank->accounts as $account) { | ||||
|             $current = $account->pivot->current_amount; | ||||
|             // if this account contains more than the amount, remove the amount and return.
 | ||||
|             if (1 === bccomp($current, $amount)) { | ||||
|                 $this->removeAmount($piggyBank, $account, $amount); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             // if this account contains less than the amount, remove the current amount, update the amount and continue.
 | ||||
|             if (bccomp($current, $amount) < 1) { | ||||
|                 $this->removeAmount($piggyBank, $account, $current); | ||||
|                 $amount = bcsub($amount, $current); | ||||
|             } | ||||
|         $repetition                = $this->getRepetition($piggyBank); | ||||
|         if (null === $repetition) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function addAmount(PiggyBank $piggyBank, Account $account, string $amount, ?TransactionJournal $journal = null): bool | ||||
|     { | ||||
|         $currentAmount         = $this->getCurrentAmount($piggyBank, $account); | ||||
|         $pivot                 = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot; | ||||
|         $pivot->current_amount = bcadd($currentAmount, $amount); | ||||
|         $pivot->save(); | ||||
|         $currentAmount             = $repetition->currentamount ?? '0'; | ||||
|         $repetition->currentamount = bcadd($currentAmount, $amount); | ||||
|         $repetition->save(); | ||||
| 
 | ||||
|         app('log')->debug('addAmount [b]: Trigger change for positive amount.'); | ||||
|         event(new ChangedAmount($piggyBank, $amount, $journal, null)); | ||||
| @@ -101,36 +84,37 @@ trait ModifiesPiggyBanks | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public function canAddAmount(PiggyBank $piggyBank, Account $account, string $amount): bool | ||||
|     public function canAddAmount(PiggyBank $piggyBank, string $amount): bool | ||||
|     { | ||||
|         Log::debug('Now in canAddAmount'); | ||||
|         $today         = today(config('app.timezone'))->endOfDay(); | ||||
|         $leftOnAccount = $this->leftOnAccount($piggyBank, $account, $today); | ||||
|         $savedSoFar    = $this->getCurrentAmount($piggyBank); | ||||
|         $today         = today(config('app.timezone')); | ||||
|         $leftOnAccount = $this->leftOnAccount($piggyBank, $today); | ||||
|         $savedSoFar    = $this->getRepetition($piggyBank)->currentamount; | ||||
|         $maxAmount     = $leftOnAccount; | ||||
| 
 | ||||
|         app('log')->debug(sprintf('Left on account: %s on %s', $leftOnAccount, $today->format('Y-m-d H:i:s'))); | ||||
|         app('log')->debug(sprintf('Saved so far: %s', $savedSoFar)); | ||||
| 
 | ||||
| 
 | ||||
|         if (0 !== bccomp($piggyBank->target_amount, '0')) { | ||||
|             $leftToSave = bcsub($piggyBank->target_amount, $savedSoFar); | ||||
|         $leftToSave    = null; | ||||
|         if (0 !== bccomp($piggyBank->targetamount, '0')) { | ||||
|             $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar); | ||||
|             $maxAmount  = 1 === bccomp($leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount; | ||||
|             app('log')->debug(sprintf('Left to save: %s', $leftToSave)); | ||||
|             app('log')->debug(sprintf('Maximum amount: %s', $maxAmount)); | ||||
|         } | ||||
| 
 | ||||
|         $compare       = bccomp($amount, $maxAmount); | ||||
|         $result        = $compare <= 0; | ||||
| 
 | ||||
|         app('log')->debug(sprintf('Compare <= 0? %d, so canAddAmount is %s', $compare, var_export($result, true))); | ||||
|         app('log')->debug(sprintf('Left on account: %s on %s', $leftOnAccount, $today->format('Y-m-d'))); | ||||
|         app('log')->debug(sprintf('Saved so far: %s', $savedSoFar)); | ||||
|         app('log')->debug(sprintf('Left to save: %s', $leftToSave)); | ||||
|         app('log')->debug(sprintf('Maximum amount: %s', $maxAmount)); | ||||
|         app('log')->debug(sprintf('Compare <= 0? %d, so %s', $compare, var_export($result, true))); | ||||
| 
 | ||||
|         return $result; | ||||
|     } | ||||
| 
 | ||||
|     public function canRemoveAmount(PiggyBank $piggyBank, Account $account, string $amount): bool | ||||
|     public function canRemoveAmount(PiggyBank $piggyBank, string $amount): bool | ||||
|     { | ||||
|         $savedSoFar = $this->getCurrentAmount($piggyBank, $account); | ||||
|         $repetition = $this->getRepetition($piggyBank); | ||||
|         if (null === $repetition) { | ||||
|             return false; | ||||
|         } | ||||
|         $savedSoFar = $repetition->currentamount; | ||||
| 
 | ||||
|         return bccomp($amount, $savedSoFar) <= 0; | ||||
|     } | ||||
| @@ -155,16 +139,16 @@ trait ModifiesPiggyBanks | ||||
| 
 | ||||
|     public function setCurrentAmount(PiggyBank $piggyBank, string $amount): PiggyBank | ||||
|     { | ||||
|         $repetition                 = $this->getRepetition($piggyBank); | ||||
|         $repetition                = $this->getRepetition($piggyBank); | ||||
|         if (null === $repetition) { | ||||
|             return $piggyBank; | ||||
|         } | ||||
|         $max                        = $piggyBank->target_amount; | ||||
|         if (1 === bccomp($amount, $max) && 0 !== bccomp($piggyBank->target_amount, '0')) { | ||||
|         $max                       = $piggyBank->targetamount; | ||||
|         if (1 === bccomp($amount, $max) && 0 !== bccomp($piggyBank->targetamount, '0')) { | ||||
|             $amount = $max; | ||||
|         } | ||||
|         $difference                 = bcsub($amount, $repetition->current_amount); | ||||
|         $repetition->current_amount = $amount; | ||||
|         $difference                = bcsub($amount, $repetition->currentamount); | ||||
|         $repetition->currentamount = $amount; | ||||
|         $repetition->save(); | ||||
| 
 | ||||
|         if (-1 === bccomp($difference, '0')) { | ||||
| @@ -194,10 +178,81 @@ trait ModifiesPiggyBanks | ||||
|      */ | ||||
|     public function store(array $data): PiggyBank | ||||
|     { | ||||
|         $factory       = new PiggyBankFactory(); | ||||
|         $factory->user = $this->user; | ||||
|         $order                      = $this->getMaxOrder() + 1; | ||||
|         if (array_key_exists('order', $data)) { | ||||
|             $order = $data['order']; | ||||
|         } | ||||
|         $data['order']              = 31337; // very high when creating.
 | ||||
|         $piggyData                  = $data; | ||||
|         // unset fields just in case.
 | ||||
|         unset($piggyData['object_group_title'], $piggyData['object_group_id'], $piggyData['notes'], $piggyData['current_amount']); | ||||
| 
 | ||||
|         return $factory->store($data); | ||||
|         // validate amount:
 | ||||
|         if (array_key_exists('targetamount', $piggyData) && '' === (string)$piggyData['targetamount']) { | ||||
|             $piggyData['targetamount'] = '0'; | ||||
|         } | ||||
| 
 | ||||
|         $piggyData['startdate_tz']  = $piggyData['startdate']?->format('e'); | ||||
|         $piggyData['targetdate_tz'] = $piggyData['targetdate']?->format('e'); | ||||
| 
 | ||||
|         try { | ||||
|             /** @var PiggyBank $piggyBank */ | ||||
|             $piggyBank = PiggyBank::create($piggyData); | ||||
|         } catch (QueryException $e) { | ||||
|             app('log')->error(sprintf('Could not store piggy bank: %s', $e->getMessage()), $piggyData); | ||||
| 
 | ||||
|             throw new FireflyException('400005: Could not store new piggy bank.', 0, $e); | ||||
|         } | ||||
| 
 | ||||
|         // reset order then set order:
 | ||||
|         $this->resetOrder(); | ||||
|         $this->setOrder($piggyBank, $order); | ||||
| 
 | ||||
|         $this->updateNote($piggyBank, $data['notes']); | ||||
| 
 | ||||
|         // repetition is auto created.
 | ||||
|         $repetition                 = $this->getRepetition($piggyBank); | ||||
|         if (null !== $repetition && array_key_exists('current_amount', $data) && '' !== $data['current_amount']) { | ||||
|             $repetition->currentamount = $data['current_amount']; | ||||
|             $repetition->save(); | ||||
|         } | ||||
| 
 | ||||
|         $objectGroupTitle           = $data['object_group_title'] ?? ''; | ||||
|         if ('' !== $objectGroupTitle) { | ||||
|             $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); | ||||
|             if (null !== $objectGroup) { | ||||
|                 $piggyBank->objectGroups()->sync([$objectGroup->id]); | ||||
|                 $piggyBank->save(); | ||||
|             } | ||||
|         } | ||||
|         // try also with ID
 | ||||
|         $objectGroupId              = (int)($data['object_group_id'] ?? 0); | ||||
|         if (0 !== $objectGroupId) { | ||||
|             $objectGroup = $this->findObjectGroupById($objectGroupId); | ||||
|             if (null !== $objectGroup) { | ||||
|                 $piggyBank->objectGroups()->sync([$objectGroup->id]); | ||||
|                 $piggyBank->save(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $piggyBank; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Correct order of piggies in case of issues. | ||||
|      */ | ||||
|     public function resetOrder(): void | ||||
|     { | ||||
|         $set     = $this->user->piggyBanks()->orderBy('piggy_banks.order', 'ASC')->get(['piggy_banks.*']); | ||||
|         $current = 1; | ||||
|         foreach ($set as $piggyBank) { | ||||
|             if ($piggyBank->order !== $current) { | ||||
|                 app('log')->debug(sprintf('Piggy bank #%d ("%s") was at place %d but should be on %d', $piggyBank->id, $piggyBank->name, $piggyBank->order, $current)); | ||||
|                 $piggyBank->order = $current; | ||||
|                 $piggyBank->save(); | ||||
|             } | ||||
|             ++$current; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function setOrder(PiggyBank $piggyBank, int $newOrder): bool | ||||
| @@ -205,28 +260,21 @@ trait ModifiesPiggyBanks | ||||
|         $oldOrder         = $piggyBank->order; | ||||
|         // app('log')->debug(sprintf('Will move piggy bank #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder));
 | ||||
|         if ($newOrder > $oldOrder) { | ||||
|             PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') | ||||
|                 ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|                 ->where('accounts.user_id', $this->user->id) | ||||
|                 ->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder) | ||||
|             $this->user->piggyBanks()->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder) | ||||
|                 ->where('piggy_banks.id', '!=', $piggyBank->id) | ||||
|                 ->distinct()->decrement('piggy_banks.order') | ||||
|                 ->decrement('piggy_banks.order') | ||||
|             ; | ||||
| 
 | ||||
|             $piggyBank->order = $newOrder; | ||||
|             app('log')->debug(sprintf('[1] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); | ||||
|             $piggyBank->save(); | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
|         PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') | ||||
|             ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|             ->where('accounts.user_id', $this->user->id) | ||||
|             ->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder) | ||||
|             ->where('piggy_banks.id', '!=', $piggyBank->id) | ||||
|             ->distinct()->increment('piggy_banks.order') | ||||
|         ; | ||||
| 
 | ||||
|         $this->user->piggyBanks()->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder) | ||||
|             ->where('piggy_banks.id', '!=', $piggyBank->id) | ||||
|             ->increment('piggy_banks.order') | ||||
|         ; | ||||
|         $piggyBank->order = $newOrder; | ||||
|         app('log')->debug(sprintf('[2] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); | ||||
|         $piggyBank->save(); | ||||
| @@ -234,13 +282,13 @@ trait ModifiesPiggyBanks | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public function updateNote(PiggyBank $piggyBank, string $note): void | ||||
|     private function updateNote(PiggyBank $piggyBank, string $note): bool | ||||
|     { | ||||
|         if ('' === $note) { | ||||
|             $dbNote = $piggyBank->notes()->first(); | ||||
|             $dbNote?->delete(); | ||||
| 
 | ||||
|             return; | ||||
|             return true; | ||||
|         } | ||||
|         $dbNote       = $piggyBank->notes()->first(); | ||||
|         if (null === $dbNote) { | ||||
| @@ -249,40 +297,35 @@ trait ModifiesPiggyBanks | ||||
|         } | ||||
|         $dbNote->text = trim($note); | ||||
|         $dbNote->save(); | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public function update(PiggyBank $piggyBank, array $data): PiggyBank | ||||
|     { | ||||
|         $piggyBank     = $this->updateProperties($piggyBank, $data); | ||||
|         $piggyBank  = $this->updateProperties($piggyBank, $data); | ||||
|         if (array_key_exists('notes', $data)) { | ||||
|             $this->updateNote($piggyBank, (string)$data['notes']); | ||||
|         } | ||||
| 
 | ||||
|         // update the order of the piggy bank:
 | ||||
|         $oldOrder      = $piggyBank->order; | ||||
|         $newOrder      = (int)($data['order'] ?? $oldOrder); | ||||
|         $oldOrder   = $piggyBank->order; | ||||
|         $newOrder   = (int)($data['order'] ?? $oldOrder); | ||||
|         if ($oldOrder !== $newOrder) { | ||||
|             $this->setOrder($piggyBank, $newOrder); | ||||
|         } | ||||
| 
 | ||||
|         // update the accounts
 | ||||
|         $factory       = new PiggyBankFactory(); | ||||
|         $factory->user = $this->user; | ||||
|         $factory->linkToAccountIds($piggyBank, $data['accounts']); | ||||
| 
 | ||||
| 
 | ||||
|         // if the piggy bank is now smaller than the current relevant rep,
 | ||||
|         // remove money from the rep.
 | ||||
|         $currentAmount = $this->getCurrentAmount($piggyBank); | ||||
|         if (1 === bccomp($currentAmount, '100') && 0 !== bccomp($piggyBank->target_amount, '0')) { | ||||
|             $difference = bcsub($piggyBank->target_amount, $currentAmount); | ||||
|         $repetition = $this->getRepetition($piggyBank); | ||||
|         if (null !== $repetition && $repetition->currentamount > $piggyBank->targetamount && 0 !== bccomp($piggyBank->targetamount, '0')) { | ||||
|             $difference                = bcsub($piggyBank->targetamount, $repetition->currentamount); | ||||
| 
 | ||||
|             // an amount will be removed, create "negative" event:
 | ||||
|             event(new ChangedAmount($piggyBank, $difference, null, null)); | ||||
| 
 | ||||
|             // question is, from which account(s) to remove the difference?
 | ||||
|             // solution: just start from the top until there is no more money left to remove.
 | ||||
|             $this->removeAmountFromAll($piggyBank, app('steam')->positive($difference)); | ||||
|             $repetition->currentamount = $piggyBank->targetamount; | ||||
|             $repetition->save(); | ||||
|         } | ||||
| 
 | ||||
|         // update using name:
 | ||||
| @@ -323,19 +366,22 @@ trait ModifiesPiggyBanks | ||||
|         if (array_key_exists('name', $data) && '' !== $data['name']) { | ||||
|             $piggyBank->name = $data['name']; | ||||
|         } | ||||
|         if (array_key_exists('target_amount', $data) && '' !== $data['target_amount']) { | ||||
|             $piggyBank->target_amount = $data['target_amount']; | ||||
|         if (array_key_exists('account_id', $data) && 0 !== $data['account_id']) { | ||||
|             $piggyBank->account_id = (int)$data['account_id']; | ||||
|         } | ||||
|         if (array_key_exists('target_amount', $data) && '' === $data['target_amount']) { | ||||
|             $piggyBank->target_amount = '0'; | ||||
|         if (array_key_exists('targetamount', $data) && '' !== $data['targetamount']) { | ||||
|             $piggyBank->targetamount = $data['targetamount']; | ||||
|         } | ||||
|         if (array_key_exists('target_date', $data) && '' !== $data['target_date']) { | ||||
|             $piggyBank->target_date    = $data['target_date']; | ||||
|             $piggyBank->target_date_tz = $data['target_date']?->format('e'); | ||||
|         if (array_key_exists('targetamount', $data) && '' === $data['targetamount']) { | ||||
|             $piggyBank->targetamount = '0'; | ||||
|         } | ||||
|         if (array_key_exists('start_date', $data)) { | ||||
|             $piggyBank->start_date    = $data['start_date']; | ||||
|             $piggyBank->start_date_tz = $data['target_date']?->format('e'); | ||||
|         if (array_key_exists('targetdate', $data) && '' !== $data['targetdate']) { | ||||
|             $piggyBank->targetdate    = $data['targetdate']; | ||||
|             $piggyBank->targetdate_tz = $data['targetdate']?->format('e'); | ||||
|         } | ||||
|         if (array_key_exists('startdate', $data)) { | ||||
|             $piggyBank->startdate    = $data['startdate']; | ||||
|             $piggyBank->startdate_tz = $data['targetdate']?->format('e'); | ||||
|         } | ||||
|         $piggyBank->save(); | ||||
| 
 | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user