From f9f733f985c0863d97f59de3978347549a7f67df Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 29 Jun 2026 21:18:22 +0200 Subject: [PATCH] Expand more views. --- .../Controllers/PreferencesController.php | 7 + app/Support/ExpandedForm.php | 17 + .../v1/src/components/passport/Clients.vue | 411 +------------ .../passport/PersonalAccessTokens.vue | 283 +-------- resources/assets/v3/js/pages/generic.js | 1 - resources/assets/v3/js/pages/profile/oauth.js | 303 ++++++++++ resources/assets/v3/vite.config.js | 3 + resources/views/attachments/delete.blade.php | 4 +- resources/views/budgets/create.blade.php | 4 +- resources/views/budgets/edit.blade.php | 4 +- resources/views/budgets/index.blade.php | 4 +- resources/views/budgets/no-budget.blade.php | 2 +- resources/views/categories/create.blade.php | 4 +- resources/views/categories/edit.blade.php | 4 +- .../views/categories/no-category.blade.php | 2 +- resources/views/currencies/create.blade.php | 4 +- resources/views/currencies/edit.blade.php | 4 +- .../views/exchange-rates/rates.blade.php | 2 +- resources/views/form/hidden.blade.php | 1 + resources/views/new-user/index.blade.php | 2 +- resources/views/piggy-banks/create.blade.php | 4 +- resources/views/piggy-banks/edit.blade.php | 4 +- resources/views/preferences/index.blade.php | 531 +++++++++------- .../views/profile/change-email.blade.php | 28 +- .../views/profile/change-password.blade.php | 40 +- .../views/profile/delete-account.blade.php | 1 + resources/views/profile/index.blade.php | 207 +++---- .../profile/logout-other-sessions.blade.php | 7 +- .../profile/mfa/backup-codes-intro.blade.php | 20 +- .../profile/mfa/backup-codes-post.blade.php | 16 +- .../views/profile/mfa/disable-mfa.blade.php | 16 +- .../views/profile/mfa/enable-mfa.blade.php | 25 +- resources/views/profile/mfa/index.blade.php | 33 +- .../views/profile/new-backup-codes.blade.php | 30 +- resources/views/profile/oauth/index.blade.php | 565 ++++++++++++++++-- resources/views/recurring/create.blade.php | 4 +- resources/views/recurring/edit.blade.php | 4 +- resources/views/recurring/old-index.blade.php | 166 ----- .../views/rules/rule-group/edit.blade.php | 4 +- .../rule-group/select-transactions.blade.php | 4 +- resources/views/rules/rule/create.blade.php | 8 +- resources/views/rules/rule/edit.blade.php | 6 +- .../views/settings/link/create.blade.php | 4 +- .../views/settings/link/delete.blade.php | 4 +- resources/views/settings/link/edit.blade.php | 4 +- resources/views/settings/users/edit.blade.php | 4 +- .../views/settings/users/index.blade.php | 4 +- resources/views/tags/create.blade.php | 24 +- resources/views/tags/delete.blade.php | 23 +- resources/views/tags/edit.blade.php | 28 +- resources/views/tags/index.blade.php | 4 +- resources/views/tags/show.blade.php | 1 + .../views/transactions/bulk/edit.blade.php | 4 +- .../views/transactions/convert.blade.php | 4 +- .../views/transactions/links/delete.blade.php | 4 +- .../views/transactions/mass/edit.blade.php | 4 +- resources/views/webhooks/delete.blade.php | 4 +- 57 files changed, 1432 insertions(+), 1477 deletions(-) create mode 100644 resources/assets/v3/js/pages/profile/oauth.js create mode 100644 resources/views/form/hidden.blade.php delete mode 100644 resources/views/recurring/old-index.blade.php diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index f8eb3e703d..9d2b0cd591 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -125,6 +125,13 @@ final class PreferencesController extends Controller } $fiscalYearStart = sprintf('%s-%s', Carbon::now()->format('Y'), (string) $fiscalYearStartStr); $tjOptionalFields = Preferences::get('transaction_journal_optional_fields', [])->data; + + // missing fields will give an error unless set, so: + $tjOptionalFields['external_url'] = $tjOptionalFields['external_url'] ?? false; + $tjOptionalFields['location'] = $tjOptionalFields['location'] ?? false; + $tjOptionalFields['links'] = $tjOptionalFields['links'] ?? false; + + $availableDarkModes = config('firefly.available_dark_modes'); // notifications settings diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 0abd117ca2..d1ef179f69 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -325,6 +325,23 @@ class ExpandedForm return $html; } + /** + * @throws FireflyException + */ + public function hidden(string $name, mixed $value): string + { + try { + $html = view('form.hidden', ['name' => $name, 'value' => $value])->render(); + } catch (Throwable $e) { + Log::debug(sprintf('Could not render hidden(): %s', $e->getMessage())); + $html = sprintf('Could not render hidden: %s', $e->getMessage()); + + throw new FireflyException($html, 0, $e); + } + + return $html; + } + /** * @throws FireflyException */ diff --git a/resources/assets/v1/src/components/passport/Clients.vue b/resources/assets/v1/src/components/passport/Clients.vue index 786dc6c88f..4b4d6c1b9f 100644 --- a/resources/assets/v1/src/components/passport/Clients.vue +++ b/resources/assets/v1/src/components/passport/Clients.vue @@ -25,280 +25,7 @@ diff --git a/resources/assets/v1/src/components/passport/PersonalAccessTokens.vue b/resources/assets/v1/src/components/passport/PersonalAccessTokens.vue index 28e23fd51d..5fd746ea63 100644 --- a/resources/assets/v1/src/components/passport/PersonalAccessTokens.vue +++ b/resources/assets/v1/src/components/passport/PersonalAccessTokens.vue @@ -25,168 +25,7 @@ diff --git a/resources/assets/v3/js/pages/generic.js b/resources/assets/v3/js/pages/generic.js index cad2a829fc..90d03b702b 100644 --- a/resources/assets/v3/js/pages/generic.js +++ b/resources/assets/v3/js/pages/generic.js @@ -18,7 +18,6 @@ * along with this program. If not, see . */ -// CSS import '../boot/bootstrap.js'; import sidebar from '../pages/shared/sidebar.js'; import dates from './shared/dates.js'; diff --git a/resources/assets/v3/js/pages/profile/oauth.js b/resources/assets/v3/js/pages/profile/oauth.js new file mode 100644 index 0000000000..a439f0e076 --- /dev/null +++ b/resources/assets/v3/js/pages/profile/oauth.js @@ -0,0 +1,303 @@ +/* + * oauth.js + * Copyright (c) 2026 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import '../../boot/bootstrap.js'; +import sidebar from '../../pages/shared/sidebar.js'; +import dates from '../shared/dates.js'; +import i18next from "i18next"; + +let index = function () { + return { + clients: [], + i18next: null, + + clientSecret: null, + + createForm: { + errors: [], + name: '', + redirect_uris: '', + confidential: true + }, + + editForm: { + errors: [], + name: '', + redirect_uris: '' + }, + + accessToken: null, + + tokens: [], + scopes: [], + + form: { + name: '', + scopes: [], + errors: [] + }, + // showCreateTokenForm + + + + init() { + this.i18next = i18next; + this.getClients(); + this.getTokens(); + + $('#modal-create-token').on('shown.bs.modal', () => { + $('#create-token-name').focus(); + }); + $('#modal-create-client').on('shown.bs.modal', () => { + $('#create-client-name').focus(); + }); + + $('#modal-edit-client').on('shown.bs.modal', () => { + $('#edit-client-name').focus(); + }); + const textBox = document.getElementById("secret_box"); + textBox.onfocus = function () { + textBox.select(); + + // Work around Chrome's little problem + textBox.onmouseup = function () { + // Prevent further mouseup intervention + textBox.onmouseup = null; + return false; + }; + }; + + const tokenBox = document.getElementById("token_box"); + tokenBox.onfocus = function () { + tokenBox.select(); + + // Work around Chrome's little problem + tokenBox.onmouseup = function () { + // Prevent further mouseup intervention + tokenBox.onmouseup = null; + return false; + }; + }; + }, + /** + * Get all of the personal access tokens for the user. + */ + getTokens() { + console.log('getTokens()'); + axios.get('./oauth/personal-access-tokens') + .then(response => { + console.log(response.data); + this.tokens = response.data; + }); + }, + + /** + * Show the form for creating new tokens. + */ + showCreateTokenForm() { + console.log('showCreateTokenForm()'); + $('#modal-create-token').modal('show'); + }, + + /** + * Create a new personal access token. + */ + storePat() { + console.log('storePat()'); + this.accessToken = null; + + this.form.errors = []; + + axios.post('./oauth/personal-access-tokens', this.form) + .then(response => { + console.log('Successful POST new token, reset form content.'); + this.form.name = ''; + this.form.scopes = []; + this.form.errors = []; + + this.getTokens(); + this.showAccessToken(response.data.accessToken); + + }) + .catch(error => { + console.warn('Bad POST new token, show error.'); + if (typeof error.response.data === 'object') { + this.form.errors = _.flatten(_.toArray(error.response.data.errors)); + } else { + this.form.errors = ['Something went wrong. Please try again.']; + } + }); + }, + + + /** + * Show the given access token to the user. + */ + showAccessToken(accessToken) { + console.log('showAccessToken'); + $('#modal-create-token').modal('hide'); + + this.accessToken = accessToken; + + $('#modal-access-token').modal('show'); + }, + getClients() { + axios.get('./oauth/clients') + .then(response => { + console.log(response.data); + this.clients = response.data; + }); + }, + showCreateClientForm() { + $('#modal-create-client').modal('show'); + }, + /** + * Persist the client to storage using the given form. + */ + persistClient(method, uri, form, modal) { + form.errors = []; + + axios[method](uri, form) + .then(response => { + this.getClients(); + + form.name = ''; + form.redirect_uris = ''; + form.errors = []; + + $(modal).modal('hide'); + + if (response.data.plainSecret) { + this.showClientSecret(response.data.plainSecret); + } + }) + .catch(error => { + + if (typeof error.response.data === 'object') { + for (const [key, value] of Object.entries(error.response.data.errors)) { + console.log(`${key}: ${value}`); + form.errors.push(value); + } + } else { + form.errors = ['Something went wrong. Please try again.']; + } + console.log(form.errors); + }); + }, + /** + * Revoke the given token. + */ + revoke(token) { + axios.delete('./oauth/personal-access-tokens/' + token.id) + .then(response => { + this.getTokens(); + }); + }, + + /** + * Show the given client secret to the user. + */ + showClientSecret(clientSecret) { + this.clientSecret = clientSecret; + + $('#modal-client-secret').modal('show'); + }, + regenerateSecret(client) { + axios.post('./oauth/clients/regenerate/' + client.id) + .then(response => { + this.clientSecret = response.data.plainSecret; + + $('#modal-client-secret').modal('show'); + }); + + }, + + /** + * Destroy the given client. + */ + destroy(client) { + axios.delete('./oauth/clients/' + client.id) + .then(response => { + this.getClients(); + }); + }, + /** + * Create a new OAuth client for the user. + */ + store() { + this.persistClient( + 'post', + './oauth/clients', + this.createForm, + '#modal-create-client' + ); + }, + /** + * Edit the given client. + */ + edit(client) { + this.editForm.id = client.id; + this.editForm.name = client.name; + this.editForm.redirect_uris = client.redirect_uris.join(','); + + $('#modal-edit-client').modal('show'); + }, + + /** + * Update the client being edited. + */ + update() { + this.persistClient( + 'put', + './oauth/clients/' + this.editForm.id, + this.editForm, + '#modal-edit-client' + ); + }, + } +}; + + +const comps = { + index, + sidebar, + dates +}; + +function loadPage(comps) { + console.log('loadPage'); + Object.keys(comps).forEach(comp => { + let data = comps[comp](); + Alpine.data(comp, () => data); + console.log(comp); + }); + Alpine.start(); +} + +// wait for load until bootstrapped event is received. +document.addEventListener('firefly-iii-bootstrapped', () => { + console.log('Loaded through event listener.'); + loadPage(comps); +}); +// or is bootstrapped before event is triggered. +if (window.bootstrapped) { + console.log('Loaded through window variable.'); + loadPage(comps); +} diff --git a/resources/assets/v3/vite.config.js b/resources/assets/v3/vite.config.js index 5d9789e172..efd1111bc0 100644 --- a/resources/assets/v3/vite.config.js +++ b/resources/assets/v3/vite.config.js @@ -65,6 +65,9 @@ export default defineConfig(({command, mode, isSsrBuild, isPreview}) => { // object groups + // profile + 'js/pages/profile/oauth.js', + // rules // recurring transactions diff --git a/resources/views/attachments/delete.blade.php b/resources/views/attachments/delete.blade.php index 360d98ec5a..bbbb6b00e9 100644 --- a/resources/views/attachments/delete.blade.php +++ b/resources/views/attachments/delete.blade.php @@ -17,8 +17,8 @@ {{ trans('form.attachment_areYouSure', ['name' => $attachment->filename]) }}

- diff --git a/resources/views/budgets/create.blade.php b/resources/views/budgets/create.blade.php index 411034ed8b..d3338250d3 100644 --- a/resources/views/budgets/create.blade.php +++ b/resources/views/budgets/create.blade.php @@ -41,8 +41,8 @@
{!! ExpandedForm::optionsList('create','budget') !!}
- diff --git a/resources/views/budgets/edit.blade.php b/resources/views/budgets/edit.blade.php index b1a5c33b6a..df1cf96ff9 100644 --- a/resources/views/budgets/edit.blade.php +++ b/resources/views/budgets/edit.blade.php @@ -44,8 +44,8 @@
{!! ExpandedForm::optionsList('update','budget') !!}
- diff --git a/resources/views/budgets/index.blade.php b/resources/views/budgets/index.blade.php index dd7baf09d0..188964d6ca 100644 --- a/resources/views/budgets/index.blade.php +++ b/resources/views/budgets/index.blade.php @@ -402,8 +402,8 @@

{{ __('firefly.createBudget') }}

- diff --git a/resources/views/budgets/no-budget.blade.php b/resources/views/budgets/no-budget.blade.php index 5641ac2762..9d318cd940 100644 --- a/resources/views/budgets/no-budget.blade.php +++ b/resources/views/budgets/no-budget.blade.php @@ -16,7 +16,7 @@

{{ $subTitle }}

-
+
@if(null !== ($periods ?? null) && count($periods) > 0) diff --git a/resources/views/categories/create.blade.php b/resources/views/categories/create.blade.php index 90d267c41c..ad615c6193 100644 --- a/resources/views/categories/create.blade.php +++ b/resources/views/categories/create.blade.php @@ -36,8 +36,8 @@
{!! ExpandedForm::optionsList('create','category') !!}
- diff --git a/resources/views/categories/edit.blade.php b/resources/views/categories/edit.blade.php index 45b7c32698..67683d7a90 100644 --- a/resources/views/categories/edit.blade.php +++ b/resources/views/categories/edit.blade.php @@ -43,8 +43,8 @@
{!! ExpandedForm::optionsList('update','category') !!}
- diff --git a/resources/views/categories/no-category.blade.php b/resources/views/categories/no-category.blade.php index d43f4d29b5..9bad6d5f1f 100644 --- a/resources/views/categories/no-category.blade.php +++ b/resources/views/categories/no-category.blade.php @@ -16,7 +16,7 @@

{{ $subTitle }}

-
+
@if(count($periods) > 0) diff --git a/resources/views/currencies/create.blade.php b/resources/views/currencies/create.blade.php index bb735a7a08..4307572d74 100644 --- a/resources/views/currencies/create.blade.php +++ b/resources/views/currencies/create.blade.php @@ -29,8 +29,8 @@
{!! ExpandedForm::optionsList('create','currency') !!}
- diff --git a/resources/views/currencies/edit.blade.php b/resources/views/currencies/edit.blade.php index de1f1b1ae8..9f3dc11234 100644 --- a/resources/views/currencies/edit.blade.php +++ b/resources/views/currencies/edit.blade.php @@ -33,8 +33,8 @@
{!! ExpandedForm::optionsList('update','currency') !!}
- diff --git a/resources/views/exchange-rates/rates.blade.php b/resources/views/exchange-rates/rates.blade.php index 82c1ceeb42..07583f12a1 100644 --- a/resources/views/exchange-rates/rates.blade.php +++ b/resources/views/exchange-rates/rates.blade.php @@ -21,7 +21,7 @@

{{ __('firefly.header_exchange_rates_table') }}

-
+