I know it's bad form to submit a large PR like this but this fixes almost everything in https://github.com/firefly-iii/firefly-iii/issues/9183 and I was too lazy to create a branch for it.

This commit is contained in:
James Cole
2024-10-08 07:21:23 +02:00
parent 5597327448
commit 1e472ee095
38 changed files with 2261 additions and 548 deletions

View File

@@ -80,4 +80,10 @@ return [
'revenue_accounts' => 'Revenue accounts',
'liabilities_accounts' => 'Liabilities',
'placeholder' => '[Placeholder]',
// mfa
'profile_mfa' => 'Multi-factor authentication',
'mfa_enableMFA' => 'Enable multi-factor authentication',
'mfa_backup_codes' => 'Backup codes',
'mfa_disableMFA' => 'Disable multi-factor authentication',
];

View File

@@ -134,5 +134,40 @@ return [
'bill_warning_end_date_zero' => 'Your bill **":name"** is due to end on :date. This moment will pass **TODAY!**',
'bill_warning_extension_date_zero' => 'Your bill **":name"** is due to be extended or cancelled on :date. This moment will pass **TODAY!**',
'bill_warning_please_action' => 'Please take the appropriate action.',
// user has enabled MFA
'enabled_mfa_subject' => 'You have enabled multi-factor authentication',
'enabled_mfa_slack' => 'You (:email) have enabled multi-factor authentication. Is this not correct? Check your settings!',
'have_enabled_mfa' => 'You have enabled multi-factor authentication on your Firefly III account ":email". This means that you will need to use an authenticator app to log in from now on.',
'enabled_mfa_warning' => 'If you did not enable this, please contact your administrator immediately or check out the Firefly III documentation.',
'disabled_mfa_subject' => 'You have disabled multi-factor authentication!',
'disabled_mfa_slack' => 'You (:email) have disabled multi-factor authentication. Is this not correct? Check your settings!',
'have_disabled_mfa' => 'You have disabled multi-factor authentication on your Firefly III account ":email".',
'disabled_mfa_warning' => 'If you did not disable this, please contact your administrator immediately or check out the Firefly III documentation.',
'new_backup_codes_subject' => 'You have generated new back-up codes',
'new_backup_codes_slack' => 'You (:email) have generated new back-up codes. These can be used to login to Firefly III. Is this not correct? Check your settings!',
'new_backup_codes_intro' => 'You (:email) have generated new back-up codes. These can be used to login to Firefly III if you lose access to your authenticator app.',
'new_backup_codes_warning' => 'Please store these codes in a safe place. If you lose them, you will not be able to log in to Firefly III. If you did not do this, please contact your administrator immediately or check out the Firefly III documentation.',
'used_backup_code_subject' => 'You have used a back-up code to login',
'used_backup_code_slack' => 'You (:email) have used a back-up code to login',
'used_backup_code_intro' => 'You (:email) have used a back-up code to login to Firefly III. You now have one less back-up code to login with. Please remove it from your list.',
'used_backup_code_warning' => 'If you did not do this, please contact your administrator immediately or check out the Firefly III documentation.',
// few left:
'mfa_few_backups_left_subject' => 'You have only :count backup code(s) left!',
'mfa_few_backups_left_slack' => 'You (:email) have only :count backup code(s) left!',
'few_backup_codes_intro' => 'You (:email) have used most of your backup codes, and now have only :count left. Please generate new ones as soon as possible.',
'few_backup_codes_warning' => 'Without backup codes, you cannot recover your MFA login if you lose access to your code generator.',
// NO left:
'mfa_no_backups_left_subject' => 'You have NO backup codes left!',
'mfa_no_backups_left_slack' => 'You (:email) NO backup codes left!',
'no_backup_codes_intro' => 'You (:email) have used ALL of your backup codes. Please generate new ones as soon as possible.',
'no_backup_codes_warning' => 'Without backup codes, you cannot recover your MFA login if you lose access to your code generator.',
];
// Ignore this comment

View File

@@ -1320,18 +1320,18 @@ return [
'pref_custom_fiscal_year_label' => 'Enabled',
'pref_custom_fiscal_year_help' => 'In countries that use a financial year other than January 1 to December 31, you can switch this on and specify start / end days of the fiscal year',
'pref_fiscal_year_start_label' => 'Fiscal year start date',
'pref_two_factor_auth' => '2-step verification',
'pref_two_factor_auth_help' => 'When you enable 2-step verification (also known as two-factor authentication), you add an extra layer of security to your account. You sign in with something you know (your password) and something you have (a verification code). Verification codes are generated by an application on your phone, such as Authy or Google Authenticator.',
'pref_enable_two_factor_auth' => 'Enable 2-step verification',
'pref_two_factor_auth_disabled' => '2-step verification code removed and disabled',
'pref_two_factor_auth' => 'Multi-factor authentication',
'pref_two_factor_auth_help' => 'When you enable multi-factor authentication (also known as two-factor authentication), you add an extra layer of security to your account. You sign in with something you know (your password) and something you have (a verification code). Verification codes are generated by an application on your phone, such as Authy or Google Authenticator.',
'pref_enable_two_factor_auth' => 'Enable multi-factor authentication',
'pref_two_factor_auth_disabled' => 'Multi-factor authentication verification code removed and disabled',
'pref_two_factor_auth_remove_it' => 'Don\'t forget to remove the account from your authentication app!',
'pref_two_factor_auth_code' => 'Verify code',
'pref_two_factor_auth_code_help' => 'Scan the QR code with an application on your phone such as Authy or Google Authenticator and enter the generated code.',
'pref_two_factor_auth_code_help' => 'Scan the QR code with an application on your phone such as Authy or Google Authenticator and enter the generated code. The QR code changes every time you visit this page. Make sure you use the most recent one.',
'pref_two_factor_auth_reset_code' => 'Reset verification code',
'pref_two_factor_auth_disable_2fa' => 'Disable 2FA',
'pref_two_factor_auth_disable_2fa' => 'Disable MFA',
'2fa_use_secret_instead' => 'If you cannot scan the QR code, feel free to use the secret instead: <code>:secret</code>.',
'2fa_backup_codes' => 'Store these backup codes for access in case you lose your device.',
'2fa_already_enabled' => '2-step verification is already enabled.',
'2fa_already_enabled' => 'Multi-factor authentication verification is already enabled.',
'wrong_mfa_code' => 'This MFA code is not valid.',
'pref_save_settings' => 'Save settings',
'saved_preferences' => 'Preferences saved!',
@@ -1413,7 +1413,27 @@ return [
'administration_role_view_reports' => 'View reports',
'administration_role_full' => 'Full access',
// mfa
'enable_mfa' => 'Enable multi-factor authentication',
'mfa_index_title' => 'Multi-factor authentication',
'mfa_index_intro' => 'Firefly III supports multi-factor authentication (MFA). You can enable MFA for your account to add an extra layer of security. Applications like Authy, Google Authenticator and FreeOTP can be used to generate the codes you need to log in. Security keys are not supported by Firefly III but you can use a security key as a storage device for your MFA secret.',
'mfa_index_enabled' => 'Multi-factor authentication is enabled for your account.',
'mfa_index_disabled' => 'Multi-factor authentication is not enabled for your account.',
'mfa_index_owner' => 'The owner of this instance will always be able to disable multi-factor authentication for your account.',
'current_password_confirm_mfa' => 'Enter your current password',
'mfa_warning_code_changes' => 'You may get a MFA dialog after you entered your password and a MFA code. In that case, please wait for your application to generate a new MFA code, and do not recycle the one you just used.',
'mfa_already_disabled' => 'Multi-factor authentication is not enabled, so you cannot disable it.',
'disable_mfa_page' => 'Disable multi-factor authentication',
'disable_mfa_intro' => 'You can disable multi-factor authentication. To do so, please enter your password and a multi-factor authentication code. If you want to disable multi-factor authentication because you have lost access to your code generator, <a href="https://docs.firefly-iii.org/references/faq/firefly-iii/using/#i-lost-my-2fa-token-generator-or-2fa-has-stopped-working">please refer to the documentation instead</a>.',
'pref_disable_mfa' => 'Disable multi-factor authentication',
'mfa_not_enabled' => 'Multi-factor authentication is not enabled.',
'mfa_backup_codes_intro' => 'Firefly III can generate backup codes for you. These codes can be used to log in when you cannot use your code generator. You can generate a new set of codes at any time. If you generate a new set, the old set will be invalidated.',
'mfa_backup_codes_quick' => 'If you are very fast coming from the setup page of multi-factor authentication, your app may not have generated a new code yet. Please know that MFA codes can only be used once. Make sure you use a different code from the previous one.',
'mfa_backup_codes_title' => 'Multi-factor authentication backup codes',
'mfa_backup_codes_post_title' => 'Multi-factor authentication backup codes',
// profile:
'manage_mfa_settings' => 'Manage multi-factor authentication settings',
'purge_data_title' => 'Purge data from Firefly III',
'purge_data_expl' => '"Purging" means "deleting that which is already deleted". In normal circumstances, Firefly III deletes nothing permanently. It just hides it. The button below deletes all of these previously "deleted" records FOREVER.',
'delete_stuff_header' => 'Delete and purge data',
@@ -1471,6 +1491,7 @@ return [
'delete_your_account_password' => 'Enter your password to continue.',
'password' => 'Password',
'are_you_sure' => 'Are you sure? You cannot undo this.',
'are_you_sure_confirm' => 'Are you sure?',
'delete_account_button' => 'DELETE your account',
'invalid_current_password' => 'Invalid current password!',
'password_changed' => 'Password changed!',

View File

@@ -25,162 +25,162 @@
declare(strict_types=1);
return [
'filter_must_be_in' => 'Filter ":filter" must be one of: :values',
'filter_not_string' => 'Filter ":filter" is expected to be a string of text',
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'nog_logged_in' => 'You are not logged in.',
'bad_type_source' => 'Firefly III can\'t determine the transaction type based on this source account.',
'bad_type_destination' => 'Firefly III can\'t determine the transaction type based on this destination account.',
'missing_where' => 'Array is missing "where"-clause',
'missing_update' => 'Array is missing "update"-clause',
'invalid_where_key' => 'JSON contains an invalid key for the "where"-clause',
'invalid_update_key' => 'JSON contains an invalid key for the "update"-clause',
'invalid_query_data' => 'There is invalid data in the %s:%s field of your query.',
'invalid_query_account_type' => 'Your query contains accounts of different types, which is not allowed.',
'invalid_query_currency' => 'Your query contains accounts that have different currency settings, which is not allowed.',
'iban' => 'This is not a valid IBAN.',
'zero_or_more' => 'The value cannot be negative.',
'more_than_zero' => 'The value must be more than zero.',
'more_than_zero_correct' => 'The value must be zero or more.',
'no_asset_account' => 'This is not an asset account.',
'date_or_time' => 'The value must be a valid date or time value (ISO 8601).',
'source_equals_destination' => 'The source account equals the destination account.',
'unique_account_number_for_user' => 'It looks like this account number is already in use.',
'unique_iban_for_user' => 'It looks like this IBAN is already in use.',
'reconciled_forbidden_field' => 'This transaction is already reconciled, you cannot change the ":field"',
'deleted_user' => 'Due to security constraints, you cannot register using this email address.',
'rule_trigger_value' => 'This value is invalid for the selected trigger.',
'rule_action_expression' => 'Invalid expression. :error',
'rule_action_value' => 'This value is invalid for the selected action.',
'file_already_attached' => 'Uploaded file ":name" is already attached to this object.',
'file_attached' => 'Successfully uploaded file ":name".',
'file_zero' => 'The file is zero bytes in size.',
'must_exist' => 'The ID in field :attribute does not exist in the database.',
'all_accounts_equal' => 'All accounts in this field must be equal.',
'group_title_mandatory' => 'A group title is mandatory when there is more than one transaction.',
'transaction_types_equal' => 'All splits must be of the same type.',
'invalid_transaction_type' => 'Invalid transaction type.',
'invalid_selection' => 'Your selection is invalid.',
'belongs_user' => 'This value is linked to an object that does not seem to exist.',
'belongs_user_or_user_group' => 'This value is linked to an object that does not seem to exist in your current financial administration.',
'no_access_group' => 'The user has no access to this user group.',
'no_accepted_roles_defined' => 'No access roles have been defined for this endpoint, access denied.',
'at_least_one_transaction' => 'Need at least one transaction.',
'recurring_transaction_id' => 'Need at least one transaction.',
'need_id_to_match' => 'You need to submit this entry with an ID for the API to be able to match it.',
'too_many_unmatched' => 'Too many submitted transactions cannot be matched to their respective database entries. Make sure existing entries have a valid ID.',
'id_does_not_match' => 'Submitted ID #:id does not match expected ID. Make sure it matches or omit the field.',
'at_least_one_repetition' => 'Need at least one repetition.',
'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.',
'require_currency_info' => 'The content of this field is invalid without currency information.',
'not_transfer_account' => 'This account is not an account that can be used for transfers.',
'require_currency_amount' => 'The content of this field is invalid without foreign amount information.',
'require_foreign_currency' => 'This field requires a number',
'require_foreign_dest' => 'This field value must match the currency of the destination account.',
'require_foreign_src' => 'This field value must match the currency of the source account.',
'equal_description' => 'Transaction description should not equal global description.',
'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.',
'file_too_large' => 'File ":name" is too large.',
'belongs_to_user' => 'The value of :attribute is unknown.',
'accepted' => 'The :attribute must be accepted.',
'bic' => 'This is not a valid BIC.',
'at_least_one_trigger' => 'Rule must have at least one trigger.',
'at_least_one_active_trigger' => 'Rule must have at least one active trigger.',
'at_least_one_action' => 'Rule must have at least one action.',
'at_least_one_active_action' => 'Rule must have at least one active action.',
'base64' => 'This is not valid base64 encoded data.',
'model_id_invalid' => 'The given ID seems invalid for this model.',
'less' => ':attribute must be less than 10,000,000',
'active_url' => 'The :attribute is not a valid URL.',
'after' => 'The :attribute must be a date after :date.',
'date_after' => 'The start date must be before the end date.',
'alpha' => 'The :attribute may only contain letters.',
'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.',
'alpha_num' => 'The :attribute may only contain letters and numbers.',
'array' => 'The :attribute must be an array.',
'unique_for_user' => 'There already is an entry with this :attribute.',
'before' => 'The :attribute must be a date before :date.',
'unique_object_for_user' => 'This name is already in use.',
'unique_account_for_user' => 'This account name is already in use.',
'filter_must_be_in' => 'Filter ":filter" must be one of: :values',
'filter_not_string' => 'Filter ":filter" is expected to be a string of text',
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'nog_logged_in' => 'You are not logged in.',
'bad_type_source' => 'Firefly III can\'t determine the transaction type based on this source account.',
'bad_type_destination' => 'Firefly III can\'t determine the transaction type based on this destination account.',
'missing_where' => 'Array is missing "where"-clause',
'missing_update' => 'Array is missing "update"-clause',
'invalid_where_key' => 'JSON contains an invalid key for the "where"-clause',
'invalid_update_key' => 'JSON contains an invalid key for the "update"-clause',
'invalid_query_data' => 'There is invalid data in the %s:%s field of your query.',
'invalid_query_account_type' => 'Your query contains accounts of different types, which is not allowed.',
'invalid_query_currency' => 'Your query contains accounts that have different currency settings, which is not allowed.',
'iban' => 'This is not a valid IBAN.',
'zero_or_more' => 'The value cannot be negative.',
'more_than_zero' => 'The value must be more than zero.',
'more_than_zero_correct' => 'The value must be zero or more.',
'no_asset_account' => 'This is not an asset account.',
'date_or_time' => 'The value must be a valid date or time value (ISO 8601).',
'source_equals_destination' => 'The source account equals the destination account.',
'unique_account_number_for_user' => 'It looks like this account number is already in use.',
'unique_iban_for_user' => 'It looks like this IBAN is already in use.',
'reconciled_forbidden_field' => 'This transaction is already reconciled, you cannot change the ":field"',
'deleted_user' => 'Due to security constraints, you cannot register using this email address.',
'rule_trigger_value' => 'This value is invalid for the selected trigger.',
'rule_action_expression' => 'Invalid expression. :error',
'rule_action_value' => 'This value is invalid for the selected action.',
'file_already_attached' => 'Uploaded file ":name" is already attached to this object.',
'file_attached' => 'Successfully uploaded file ":name".',
'file_zero' => 'The file is zero bytes in size.',
'must_exist' => 'The ID in field :attribute does not exist in the database.',
'all_accounts_equal' => 'All accounts in this field must be equal.',
'group_title_mandatory' => 'A group title is mandatory when there is more than one transaction.',
'transaction_types_equal' => 'All splits must be of the same type.',
'invalid_transaction_type' => 'Invalid transaction type.',
'invalid_selection' => 'Your selection is invalid.',
'belongs_user' => 'This value is linked to an object that does not seem to exist.',
'belongs_user_or_user_group' => 'This value is linked to an object that does not seem to exist in your current financial administration.',
'no_access_group' => 'The user has no access to this user group.',
'no_accepted_roles_defined' => 'No access roles have been defined for this endpoint, access denied.',
'at_least_one_transaction' => 'Need at least one transaction.',
'recurring_transaction_id' => 'Need at least one transaction.',
'need_id_to_match' => 'You need to submit this entry with an ID for the API to be able to match it.',
'too_many_unmatched' => 'Too many submitted transactions cannot be matched to their respective database entries. Make sure existing entries have a valid ID.',
'id_does_not_match' => 'Submitted ID #:id does not match expected ID. Make sure it matches or omit the field.',
'at_least_one_repetition' => 'Need at least one repetition.',
'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.',
'require_currency_info' => 'The content of this field is invalid without currency information.',
'not_transfer_account' => 'This account is not an account that can be used for transfers.',
'require_currency_amount' => 'The content of this field is invalid without foreign amount information.',
'require_foreign_currency' => 'This field requires a number',
'require_foreign_dest' => 'This field value must match the currency of the destination account.',
'require_foreign_src' => 'This field value must match the currency of the source account.',
'equal_description' => 'Transaction description should not equal global description.',
'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.',
'file_too_large' => 'File ":name" is too large.',
'belongs_to_user' => 'The value of :attribute is unknown.',
'accepted' => 'The :attribute must be accepted.',
'bic' => 'This is not a valid BIC.',
'at_least_one_trigger' => 'Rule must have at least one trigger.',
'at_least_one_active_trigger' => 'Rule must have at least one active trigger.',
'at_least_one_action' => 'Rule must have at least one action.',
'at_least_one_active_action' => 'Rule must have at least one active action.',
'base64' => 'This is not valid base64 encoded data.',
'model_id_invalid' => 'The given ID seems invalid for this model.',
'less' => ':attribute must be less than 10,000,000',
'active_url' => 'The :attribute is not a valid URL.',
'after' => 'The :attribute must be a date after :date.',
'date_after' => 'The start date must be before the end date.',
'alpha' => 'The :attribute may only contain letters.',
'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.',
'alpha_num' => 'The :attribute may only contain letters and numbers.',
'array' => 'The :attribute must be an array.',
'unique_for_user' => 'There already is an entry with this :attribute.',
'before' => 'The :attribute must be a date before :date.',
'unique_object_for_user' => 'This name is already in use.',
'unique_account_for_user' => 'This account name is already in use.',
// Ignore this comment
'between.numeric' => 'The :attribute must be between :min and :max.',
'between.file' => 'The :attribute must be between :min and :max kilobytes.',
'between.string' => 'The :attribute must be between :min and :max characters.',
'between.array' => 'The :attribute must have between :min and :max items.',
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'date' => 'The :attribute is not a valid date.',
'date_format' => 'The :attribute does not match the format :format.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
'digits_between' => 'The :attribute must be between :min and :max digits.',
'email' => 'The :attribute must be a valid email address.',
'filled' => 'The :attribute field is required.',
'exists' => 'The selected :attribute is invalid.',
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'integer' => 'The :attribute must be an integer.',
'ip' => 'The :attribute must be a valid IP address.',
'json' => 'The :attribute must be a valid JSON string.',
'max.numeric' => 'The :attribute may not be greater than :max.',
'max.file' => 'The :attribute may not be greater than :max kilobytes.',
'max.string' => 'The :attribute may not be greater than :max characters.',
'max.array' => 'The :attribute may not have more than :max items.',
'mimes' => 'The :attribute must be a file of type: :values.',
'min.numeric' => 'The :attribute must be at least :min.',
'lte.numeric' => 'The :attribute must be less than or equal :value.',
'min.file' => 'The :attribute must be at least :min kilobytes.',
'min.string' => 'The :attribute must be at least :min characters.',
'min.array' => 'The :attribute must have at least :min items.',
'not_in' => 'The selected :attribute is invalid.',
'numeric' => 'The :attribute must be a number.',
'scientific_notation' => 'The :attribute cannot use the scientific notation.',
'numeric_native' => 'The native amount must be a number.',
'numeric_destination' => 'The destination amount must be a number.',
'numeric_source' => 'The source amount must be a number.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values is present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute and :other must match.',
'size.numeric' => 'The :attribute must be :size.',
'amount_min_over_max' => 'The minimum amount cannot be larger than the maximum amount.',
'size.file' => 'The :attribute must be :size kilobytes.',
'size.string' => 'The :attribute must be :size characters.',
'size.array' => 'The :attribute must contain :size items.',
'unique' => 'The :attribute has already been taken.',
'string' => 'The :attribute must be a string.',
'url' => 'The :attribute format is invalid.',
'timezone' => 'The :attribute must be a valid zone.',
'2fa_code' => 'The :attribute field is invalid.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'file' => 'The :attribute must be a file.',
'in_array' => 'The :attribute field does not exist in :other.',
'present' => 'The :attribute field must be present.',
'amount_zero' => 'The total amount cannot be zero.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.',
'unique_object_group' => 'The group name must be unique',
'starts_with' => 'The value must start with :values.',
'unique_webhook' => 'You already have a webhook with this combination of URL, trigger, response and delivery.',
'unique_existing_webhook' => 'You already have another webhook with this combination of URL, trigger, response and delivery.',
'same_account_type' => 'Both accounts must be of the same account type',
'same_account_currency' => 'Both accounts must have the same currency setting',
'between.numeric' => 'The :attribute must be between :min and :max.',
'between.file' => 'The :attribute must be between :min and :max kilobytes.',
'between.string' => 'The :attribute must be between :min and :max characters.',
'between.array' => 'The :attribute must have between :min and :max items.',
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'date' => 'The :attribute is not a valid date.',
'date_format' => 'The :attribute does not match the format :format.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
'digits_between' => 'The :attribute must be between :min and :max digits.',
'email' => 'The :attribute must be a valid email address.',
'filled' => 'The :attribute field is required.',
'exists' => 'The selected :attribute is invalid.',
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'integer' => 'The :attribute must be an integer.',
'ip' => 'The :attribute must be a valid IP address.',
'json' => 'The :attribute must be a valid JSON string.',
'max.numeric' => 'The :attribute may not be greater than :max.',
'max.file' => 'The :attribute may not be greater than :max kilobytes.',
'max.string' => 'The :attribute may not be greater than :max characters.',
'max.array' => 'The :attribute may not have more than :max items.',
'mimes' => 'The :attribute must be a file of type: :values.',
'min.numeric' => 'The :attribute must be at least :min.',
'lte.numeric' => 'The :attribute must be less than or equal :value.',
'min.file' => 'The :attribute must be at least :min kilobytes.',
'min.string' => 'The :attribute must be at least :min characters.',
'min.array' => 'The :attribute must have at least :min items.',
'not_in' => 'The selected :attribute is invalid.',
'numeric' => 'The :attribute must be a number.',
'scientific_notation' => 'The :attribute cannot use the scientific notation.',
'numeric_native' => 'The native amount must be a number.',
'numeric_destination' => 'The destination amount must be a number.',
'numeric_source' => 'The source amount must be a number.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values is present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute and :other must match.',
'size.numeric' => 'The :attribute must be :size.',
'amount_min_over_max' => 'The minimum amount cannot be larger than the maximum amount.',
'size.file' => 'The :attribute must be :size kilobytes.',
'size.string' => 'The :attribute must be :size characters.',
'size.array' => 'The :attribute must contain :size items.',
'unique' => 'The :attribute has already been taken.',
'string' => 'The :attribute must be a string.',
'url' => 'The :attribute format is invalid.',
'timezone' => 'The :attribute must be a valid zone.',
'2fa_code' => 'The :attribute field is invalid.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'file' => 'The :attribute must be a file.',
'in_array' => 'The :attribute field does not exist in :other.',
'present' => 'The :attribute field must be present.',
'amount_zero' => 'The total amount cannot be zero.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.',
'unique_object_group' => 'The group name must be unique',
'starts_with' => 'The value must start with :values.',
'unique_webhook' => 'You already have a webhook with this combination of URL, trigger, response and delivery.',
'unique_existing_webhook' => 'You already have another webhook with this combination of URL, trigger, response and delivery.',
'same_account_type' => 'Both accounts must be of the same account type',
'same_account_currency' => 'Both accounts must have the same currency setting',
// Ignore this comment
'secure_password' => 'This is not a secure password. Please try again. For more information, visit https://bit.ly/FF3-password',
'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.',
'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.',
'invalid_account_info' => 'Invalid account information.',
'attributes' => [
'secure_password' => 'This is not a secure password. Please try again. For more information, visit https://bit.ly/FF3-password',
'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.',
'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.',
'invalid_account_info' => 'Invalid account information.',
'attributes' => [
'email' => 'email address',
'description' => 'description',
'amount' => 'amount',
@@ -219,59 +219,60 @@ return [
],
// validation of accounts:
'withdrawal_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.',
'withdrawal_source_bad_data' => '[a] Could not find a valid source account when searching for ID ":id" or name ":name".',
'withdrawal_dest_need_data' => '[a] Need to get a valid destination account ID and/or valid destination account name to continue.',
'withdrawal_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
'withdrawal_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.',
'withdrawal_source_bad_data' => '[a] Could not find a valid source account when searching for ID ":id" or name ":name".',
'withdrawal_dest_need_data' => '[a] Need to get a valid destination account ID and/or valid destination account name to continue.',
'withdrawal_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
'withdrawal_dest_iban_exists' => 'This destination account IBAN is already in use by an asset account or a liability and cannot be used as a withdrawal destination.',
'deposit_src_iban_exists' => 'This source account IBAN is already in use by an asset account or a liability and cannot be used as a deposit source.',
'withdrawal_dest_iban_exists' => 'This destination account IBAN is already in use by an asset account or a liability and cannot be used as a withdrawal destination.',
'deposit_src_iban_exists' => 'This source account IBAN is already in use by an asset account or a liability and cannot be used as a deposit source.',
'reconciliation_source_bad_data' => 'Could not find a valid reconciliation account when searching for ID ":id" or name ":name".',
'reconciliation_source_bad_data' => 'Could not find a valid reconciliation account when searching for ID ":id" or name ":name".',
'generic_source_bad_data' => '[e] Could not find a valid source account when searching for ID ":id" or name ":name".',
'generic_source_bad_data' => '[e] Could not find a valid source account when searching for ID ":id" or name ":name".',
'deposit_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.',
'deposit_source_bad_data' => '[b] Could not find a valid source account when searching for ID ":id" or name ":name".',
'deposit_dest_need_data' => '[b] Need to get a valid destination account ID and/or valid destination account name to continue.',
'deposit_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
'deposit_dest_wrong_type' => 'The submitted destination account is not of the right type.',
'deposit_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.',
'deposit_source_bad_data' => '[b] Could not find a valid source account when searching for ID ":id" or name ":name".',
'deposit_dest_need_data' => '[b] Need to get a valid destination account ID and/or valid destination account name to continue.',
'deposit_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
'deposit_dest_wrong_type' => 'The submitted destination account is not of the right type.',
// Ignore this comment
'transfer_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.',
'transfer_source_bad_data' => '[c] Could not find a valid source account when searching for ID ":id" or name ":name".',
'transfer_dest_need_data' => '[c] Need to get a valid destination account ID and/or valid destination account name to continue.',
'transfer_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
'need_id_in_edit' => 'Each split must have transaction_journal_id (either valid ID or 0).',
'transfer_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.',
'transfer_source_bad_data' => '[c] Could not find a valid source account when searching for ID ":id" or name ":name".',
'transfer_dest_need_data' => '[c] Need to get a valid destination account ID and/or valid destination account name to continue.',
'transfer_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
'need_id_in_edit' => 'Each split must have transaction_journal_id (either valid ID or 0).',
'ob_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.',
'lc_source_need_data' => 'Need to get a valid source account ID to continue.',
'ob_dest_need_data' => '[d] Need to get a valid destination account ID and/or valid destination account name to continue.',
'ob_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
'reconciliation_either_account' => 'To submit a reconciliation, you must submit either a source or a destination account. Not both, not neither.',
'ob_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.',
'lc_source_need_data' => 'Need to get a valid source account ID to continue.',
'ob_dest_need_data' => '[d] Need to get a valid destination account ID and/or valid destination account name to continue.',
'ob_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
'reconciliation_either_account' => 'To submit a reconciliation, you must submit either a source or a destination account. Not both, not neither.',
'generic_invalid_source' => 'You can\'t use this account as the source account.',
'generic_invalid_destination' => 'You can\'t use this account as the destination account.',
'generic_invalid_source' => 'You can\'t use this account as the source account.',
'generic_invalid_destination' => 'You can\'t use this account as the destination account.',
'generic_no_source' => 'You must submit source account information or submit a transaction journal ID.',
'generic_no_destination' => 'You must submit destination account information or submit a transaction journal ID.',
'generic_no_source' => 'You must submit source account information or submit a transaction journal ID.',
'generic_no_destination' => 'You must submit destination account information or submit a transaction journal ID.',
'gte.numeric' => 'The :attribute must be greater than or equal to :value.',
'gt.numeric' => 'The :attribute must be greater than :value.',
'gte.file' => 'The :attribute must be greater than or equal to :value kilobytes.',
'gte.string' => 'The :attribute must be greater than or equal to :value characters.',
'gte.array' => 'The :attribute must have :value items or more.',
'gte.numeric' => 'The :attribute must be greater than or equal to :value.',
'gt.numeric' => 'The :attribute must be greater than :value.',
'gte.file' => 'The :attribute must be greater than or equal to :value kilobytes.',
'gte.string' => 'The :attribute must be greater than or equal to :value characters.',
'gte.array' => 'The :attribute must have :value items or more.',
'amount_required_for_auto_budget' => 'The amount is required.',
'auto_budget_amount_positive' => 'The amount must be more than zero.',
'auto_budget_period_mandatory' => 'The auto budget period is a mandatory field.',
'auto_budget_period_mandatory' => 'The auto budget period is a mandatory field.',
// no access to administration:
'no_auth_user_group' => 'You have to be logged in to access this administration.',
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
'no_auth_user_group' => 'You have to be logged in to access this administration.',
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
'existing_mfa_code' => 'Please enter a valid code',
];
// Ignore this comment

View File

@@ -0,0 +1,6 @@
@component('mail::message')
{{ trans('email.have_disabled_mfa', ['email' => $user->email]) }}
{{ trans('email.disabled_mfa_warning') }}
@endcomponent

View File

@@ -0,0 +1,6 @@
@component('mail::message')
{{ trans('email.have_enabled_mfa', ['email' => $user->email]) }}
{{ trans('email.enabled_mfa_warning') }}
@endcomponent

View File

@@ -0,0 +1,6 @@
@component('mail::message')
{{ trans('email.few_backup_codes_intro', ['email' => $user->email, 'count' => $count]) }}
{{ trans('email.few_backup_codes_warning') }}
@endcomponent

View File

@@ -0,0 +1,6 @@
@component('mail::message')
{{ trans('email.new_backup_codes_intro', ['email' => $user->email]) }}
{{ trans('email.new_backup_codes_warning') }}
@endcomponent

View File

@@ -0,0 +1,6 @@
@component('mail::message')
{{ trans('email.no_backup_codes_intro', ['email' => $user->email]) }}
{{ trans('email.no_backup_codes_warning') }}
@endcomponent

View File

@@ -0,0 +1,6 @@
@component('mail::message')
{{ trans('email.used_backup_code_intro', ['email' => $user->email]) }}
{{ trans('email.used_backup_code_warning') }}
@endcomponent

View File

@@ -22,12 +22,14 @@
<li role="presentation">
<a href="#oauth" aria-controls="messages" role="tab" data-toggle="tab">{{ 'oauth'|_ }}</a>
</li>
{#
{% if true == isInternalAuth %}
<li role="presentation">
<a href="#mfa" aria-controls="settings" role="tab"
data-toggle="tab">{{ 'pref_two_factor_auth'|_ }}</a>
</li>
{% endif %}
#}
<li role="presentation">
<a href="#delete" aria-controls="settings" role="tab"
data-toggle="tab">{{ 'delete_stuff_header'|_ }}</a>
@@ -52,6 +54,16 @@
<li>
<a href="{{ route('profile.change-password') }}">{{ 'change_your_password'|_ }}</a>
</li>
{% if enabled2FA == true %}
<li>
<a href="{{ route('profile.mfa.index') }}">{{ 'manage_mfa_settings'|_ }}</a>
</li>
{% endif %}
{% if enabled2FA == false %}
<li>
<a href="{{ route('profile.mfa.index') }}">{{ 'enable_mfa'|_ }}</a>
</li>
{% endif %}
{% endif %}
<li><a href="{{ route('logout') }}" class="logout-link">{{ 'logout'|_ }}</a>
@@ -103,6 +115,7 @@
<div id="passport_clients"></div>
</div>
{#
{% if true == isInternalAuth %}
<!-- MFA -->
<div role="tabpanel" class="tab-pane" id="mfa">
@@ -141,6 +154,7 @@
</div>
</div>
{% endif %}
#}
<!-- purge stuff -->
<div role="tabpanel" class="tab-pane" id="delete">

View File

@@ -0,0 +1,46 @@
{% extends './layout/default' %}
{% block breadcrumbs %}
{{ Breadcrumbs.render(Route.getCurrentRoute.getName) }}
{% endblock %}
{% block content %}
<form method="POST" action="{{ route('profile.mfa.backup-codes.post') }}" accept-charset="UTF-8" class="form-horizontal" id="preferences_code">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-6 col-lg-offset-3 col-md-12 col-sm-12 col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'mfa_backup_codes_title'|_ }}</h3>
</div>
<div class="box-body">
<div class="form group">
<p>
{{ 'mfa_backup_codes_intro'|_ }}
</p>
<p class="text-danger">
{{ 'mfa_backup_codes_quick'|_ }}
</p>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-lg-offset-3 col-md-12 col-sm-12 col-xs-12">
<div class="box">
<div class="box-body">
{{ ExpandedForm.password('password', {helpText: 'current_password_confirm_mfa'|_}) }}
{{ ExpandedForm.text('code', code) }}
</div>
<div class="box-footer">
<button type="submit" class="btn btn-success">{{ 'pref_save_settings'|_ }}</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,28 @@
{% extends './layout/default' %}
{% block breadcrumbs %}
{{ Breadcrumbs.render(Route.getCurrentRoute.getName) }}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-6 col-lg-offset-3 col-md-12 col-sm-12 col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'mfa_backup_codes_post_title'|_ }}</h3>
</div>
<div class="box-body">
<div class="form group">
<p>
{{ '2fa_backup_codes'|_ }}
</p>
<textarea rows="10" class="form-control" readonly>{{ codes }}</textarea>
</div>
</div>
<div class="box-footer">
<a class="btn btn-success" href="{{ route('profile.mfa.index') }}">{{ '2fa_i_have_them'|_ }}</a>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,48 @@
{% extends './layout/default' %}
{% block breadcrumbs %}
{{ Breadcrumbs.render(Route.getCurrentRoute.getName) }}
{% endblock %}
{% block content %}
<form method="POST" action="{{ route('profile.mfa.disableMFA.post') }}" accept-charset="UTF-8" class="form-horizontal">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-6 col-lg-offset-3 col-md-12 col-sm-12 col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'disable_mfa_page'|_ }}</h3>
</div>
<div class="box-body">
<p class="hidden-print">
{{ 'disable_mfa_intro'|_ }}
</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-lg-offset-3 col-md-12 col-sm-12 col-xs-12">
<div class="box">
<div class="box-body">
{{ ExpandedForm.password('password', {helpText: 'current_password_confirm_mfa'|_}) }}
{{ ExpandedForm.text('code', code) }}
</div>
<div class="box-footer">
<button type="submit" class="btn btn-danger">{{ 'pref_disable_mfa'|_ }}</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}
{% block scripts %}
<script type="text/javascript" nonce="{{ JS_NONCE }}">
$(function () {
"use strict";
// Focus first visible form element.
$("form#preferences_code input:enabled:visible:first").first().select();
});
</script>
{% endblock %}

View File

@@ -5,7 +5,7 @@
{% endblock %}
{% block content %}
<form method="POST" action="{{ route('profile.code.store') }}" accept-charset="UTF-8" class="form-horizontal" id="preferences_code">
<form method="POST" action="{{ route('profile.mfa.enableMFA.post') }}" accept-charset="UTF-8" class="form-horizontal" id="preferences_code">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-6 col-lg-offset-3 col-md-12 col-sm-12 col-xs-12">
@@ -14,7 +14,7 @@
<h3 class="box-title">{{ 'pref_two_factor_auth_code'|_ }}</h3>
</div>
<div class="box-body">
<p class="text-info hidden-print">
<p class="hidden-print">
{{ 'pref_two_factor_auth_code_help'|_ }}
</p>
<div class="form group">
@@ -24,10 +24,9 @@
<p class="hidden-print">
{{ trans('firefly.2fa_use_secret_instead', {secret: secret|escape})|raw }}
</p>
<p>
{{ '2fa_backup_codes'|_ }}
<p class="hidden-print text-danger">
{{ 'mfa_warning_code_changes'|_ }}
</p>
<pre>{{ codes }}</pre>
</div>
</div>
</div>
@@ -37,6 +36,7 @@
<div class="col-lg-6 col-lg-offset-3 col-md-12 col-sm-12 col-xs-12">
<div class="box">
<div class="box-body">
{{ ExpandedForm.password('password', {helpText: 'current_password_confirm_mfa'|_}) }}
{{ ExpandedForm.text('code', code) }}
</div>
<div class="box-footer">

View File

@@ -0,0 +1,48 @@
{% extends './layout/default' %}
{% block breadcrumbs %}
{{ Breadcrumbs.render(Route.getCurrentRoute.getName) }}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-12 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ 'mfa_index_title'|_ }}
</h3>
</div>
<div class="box-body">
<p>
{% if enabledMFA == true %}
{{ 'mfa_index_enabled'|_ }}
{% endif %}
{% if enabledMFA == false %}
{{ 'mfa_index_disabled'|_ }}
{% endif %}
</p>
<p>
{{ 'mfa_index_intro'|_ }}
</p>
<p>
{{ 'mfa_index_owner'|_ }}
</p>
{% if enabledMFA == true %}
<div class="btn-group">
<a href="{{ route('profile.mfa.disableMFA') }}" class="btn btn-danger"><em class="fa fa-unlock-alt"></em> {{ 'pref_two_factor_auth_disable_2fa'|_ }}</a>
<a href="{{ route('profile.mfa.backup-codes') }}" class="btn btn-default"><em class="fa fa-calculator"></em> {{ 'pref_two_factor_new_backup_codes'|_ }}</a>
</div>
{% endif %}
{% if enabledMFA == false %}
<p>
<a class="btn btn-info" href="{{ route('profile.mfa.enableMFA') }}"><em class="fa fa-calculator"></em> {{ 'pref_enable_two_factor_auth'|_ }}</a>
</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}