[ 'super-user' => 'SecurePassword', // Initial password, should be changed after 1st login 'super-admin' => 'SecurePassword', 'bank' => 'SecurePassword', 'admin' => 'SecurePassword', 'test-profile' => 'password', ], /* |-------------------------------------------------------------------------- | Central Bank (Level 0 Source Bank) |-------------------------------------------------------------------------- | Configuration for the central bank that is created during database seeding. | The central bank is the source of currency creation/removal in the system. | */ 'central_bank' => [ 'name' => 'Central Bank', 'full_name' => 'Central Bank', 'email' => 'bank@example.org', ], /* |-------------------------------------------------------------------------- | Mail addresses |-------------------------------------------------------------------------- | */ 'mail' => [ 'system_admin' => [ 'email' => 'admin@timebank.cc', 'name' => 'admin@timebank.cc', ], 'content_admin' => [ 'email' => 'info@timebank.cc', 'name' => config('app.name'), ], 'payments' => [ 'email' => 'noreply@timebank.cc', 'name' => config('app.name'), ], 'chat_messenger' => [ 'email' => 'support@timebank.cc', 'name' => config('app.name'), ], 'support' => [ 'email' => env('MAIL_SUPPORT_ADDRESS', 'support@timebank.cc'), 'name' => env('MAIL_SUPPORT_NAME', 'Timebank.cc Support Team'), ], 'platform_sign_off_signature' => [ 'en' => 'See you around and happy Timebanking!', 'nl' => 'Tot ziens en veel plezier met Timebanken!', 'fr' => 'À bientôt et bon Timebanking !', 'es' => '¡Hasta pronto y feliz Timebanking!', 'de' => 'Bis bald und viel Spaß beim Timebanking!', ], ], /* |-------------------------------------------------------------------------- | Platform specific translations |-------------------------------------------------------------------------- | */ 'platform_translations' => [ 'en' => [ 'platform_name' => 'Timebank.cc', 'platform_name_legal' => 'association Timebank.cc', 'platform_name_short' => 'Timebank', 'platform_slogan' => 'Your time is currency', 'platform_currency_name' => 'Hour', 'platform_currency_name_plural' => 'Hours', 'platform_currency_symbol' => 'H', 'platform_currency_position_end' => false, // true will place currency symbol behind currency amount 'platform_user' => 'timebanker', 'platform_users' => 'timebankers', 'platform_principles' => 'Timebank principles', ], 'nl' => [ 'platform_name' => 'Timebank.cc', 'platform_name_legal' => 'vereniging Timebank.cc', 'platform_name_short' => 'Timebank', 'platform_slogan' => 'Jouw tijd is deelbaar', 'platform_currency_name' => 'Uur', 'platform_currency_name_plural' => 'Uren', 'platform_currency_symbol' => 'H', 'platform_currency_position_end' => false, // true will place currency symbol behind currency amount 'platform_user' => 'timebanker', 'platform_users' => 'timebankers', 'platform_principles' => 'Timebank principes', ], 'fr' => [ 'platform_name' => 'Timebank.cc', 'platform_name_legal' => 'association Timebank.cc', 'platform_name_short' => 'Timebank', 'platform_slogan' => 'Ton temps est une monnaie', 'platform_currency_name' => 'Heure', 'platform_currency_name_plural' => 'Heures', 'platform_currency_symbol' => 'H', 'platform_currency_position_end' => false, // true will place currency symbol behind currency amount 'platform_user' => 'tempobanquier', 'platform_users' => 'tempobanquiers', 'platform_principles' => 'Principes de Timebank', ], 'es' => [ 'platform_name' => 'Timebank.cc', 'platform_name_legal' => 'asociación Timebank.cc', 'platform_name_short' => 'Timebank', 'platform_slogan' => 'Tu tiempo es compartible', 'platform_currency_name' => 'Hora', 'platform_currency_name_plural' => 'Horas', 'platform_currency_symbol' => 'H', 'platform_currency_position_end' => false, // true will place currency symbol behind currency amount 'platform_user' => 'tiempobanker', 'platform_users' => 'tiempobankers', 'platform_principles' => 'Principios de Timebank', ], 'de' => [ 'platform_name' => 'Timebank.cc', 'platform_name_legal' => 'Verein Timebank.cc', 'platform_name_short' => 'Timebank', 'platform_slogan' => 'Deine Zeit ist Währung', 'platform_currency_name' => 'Stunde', 'platform_currency_name_plural' => 'Stunden', 'platform_currency_symbol' => 'Std', 'platform_currency_position_end' => false, // true will place currency symbol behind currency amount 'platform_user' => 'Zeitbanker', 'platform_users' => 'Zeitbankers', 'platform_principles' => 'Timebank Prinzipien', ], ], /* |-------------------------------------------------------------------------- | Admin settings |-------------------------------------------------------------------------- | */ 'admin_settings' => [ 'log_lines' => 100, // Show last nr of lines of the log on admin dashboard 'activity_log_delete_records_older_than_days' => 366 // 1 year ], /* |-------------------------------------------------------------------------- | IP Address Retention |-------------------------------------------------------------------------- | Configure retention period for IP addresses stored in profile tables | (last_login_ip) and activity logs. IP addresses older than this period | should be anonymized or deleted for GDPR compliance. | */ 'ip_retention' => [ 'retention_days' => 180, // Retain IP addresses for 6 months (180 days) ], /* |-------------------------------------------------------------------------- | Logging settings |-------------------------------------------------------------------------- | Configure Laravel's default log rotation and retention. | These settings apply to the standard laravel.log files. | */ 'logging' => [ 'daily_retention_days' => 14, // Number of days to keep daily log files (default: 14) ], /* |-------------------------------------------------------------------------- | Presence System settings |-------------------------------------------------------------------------- | */ 'presence_settings' => [ 'keep_last_presence_updates' => 10, // nr of most recent activity log records to keep 'update_interval' => 60, // Update presence time in seconds ], /* |-------------------------------------------------------------------------- | Profile Session Timeouts |-------------------------------------------------------------------------- | | Define the inactivity timeout in minutes for each profile type. | After the specified timeout, the user's session will expire and they | will be logged out automatically. This provides security by ensuring | inactive sessions are terminated. | | The key should be the fully qualified class name of the model. | A default value is used if the active profile type isn't found here. | | IMPORTANT: These timeouts OVERRIDE the SESSION_LIFETIME setting from .env | They are enforced by ProfileSessionTimeout middleware. | | Security Best Practices: | - User profiles: Short timeout (30-120 min) for regular accounts | - Organizations: Medium timeout (30-60 min) for community profiles | - Banks: Short timeout (15-30 min) for financial operations | - Admins: Very short timeout (15-30 min) for privileged access | */ 'profile_timeouts' => [ App\Models\User::class => 120, // minutes App\Models\Organization::class => 60, App\Models\Bank::class => 30, App\Models\Admin::class => 30, ], 'profile_timeout_default' => 120, // minutes. Fallback default if type not listed or no profile active 'profile_inactive' => [ 'days_not_logged_in' => 350, // Mark profile as inactive after not logging in for this many days (~1 year) 're-activate_at_login' => true, // Remove inactive_at record at login 'messenger_hidden' => true, // Not searchable in chat messenger 'profile_search_hidden' => true, // Profile page is hidden from main search bar for non-Admin and non-Banks 'profile_hidden' => true, // Profile page is hidden for non-Admin and non-Banks 'profile_labeled' => true, // Profile has inactive label ], 'profile_email_unverified' => [ 'messenger_hidden' => true, // Not searchable in chat messenger 'profile_search_hidden' => true, // Profile page is hidden from main search bar for non-Admin and non-Banks 'profile_hidden' => false, // Profile page is hidden for non-Admin and non-Banks 'profile_labeled' => true, // Profile has email unverified label ], 'profile_incomplete' => [ 'messenger_hidden' => false, // Not searchable in chat messenger 'profile_search_hidden' => true, // Profile page is hidden from main search bar for non-Admin and non-Banks 'profile_hidden' => false, // Profile page is hidden for non-Admin and non-Banks 'profile_labeled' => true, // Profile has incomplete label 'check_fields' => ['about', 'about_short', 'motivation', 'cyclos_skills'], // One these fields that must be filled in to be considered as a complete profile 'check_fields_min_total_length' => 100, // Minimum total length of all check_fields that must be filled in to be considered as a complete profile 'check_relations' => ['tags', 'languages', 'locations'], // One these relations that must be filled in to be considered as a complete profile 'show_warning_modal' => true, // Show profile incomplete warning modal on edit profile page 'no_exchanges_yet_label' => true, // Show 'No exchanges yet, but ready to help' label when profile has never received a transaction ], /* |-------------------------------------------------------------------------- | Default Profile Properties |-------------------------------------------------------------------------- | */ 'profiles' => [ 'user' => [ 'limit_min' => 0, 'limit_max' => 6000, // 100 H 'profile_photo_path_new' => 'app-images/profile-user-new.svg', 'profile_photo_path_default' => 'app-images/profile-user-default.svg', 'messenger_can_create_groups' => true, // true: profile can start a group chat ], 'organization' => [ 'limit_min' => 0, 'limit_max' => 6000, // 100 H4 'profile_photo_path_new' => 'app-images/profile-organization-default.svg', 'profile_photo_path_default' => 'app-images/profile-organization-default.svg', 'messenger_can_create_groups' => true, // true: profile can start a group chat ], 'bank' => [ 'level' => 1, // by default a non-public system bank is created as this can be privately installed and later changed to a public level 2 bank 'limit_min' => 0, 'limit_max' => null, // unlimited H 'profile_photo_path_new' => 'app-images/profile-bank-default.svg', 'profile_photo_path_default' => 'app-images/profile-bank-default.svg', 'messenger_can_create_groups' => true, // true: profile can start a group chat ], 'admin' => [ 'limit_min' => 0, 'limit_max' => 0, 'profile_photo_path_new' => 'app-images/profile-admin-default.svg', 'profile_photo_path_default' => 'app-images/profile-admin-default.svg', 'messenger_can_create_groups' => true, // true: profile can start a group chat ], ], /* |-------------------------------------------------------------------------- | Default Account Properties |-------------------------------------------------------------------------- | | The default account properties that will be set into the database when new accounts are created. | The balance limits are in minutes. A negative balance limit should be set as 'limit_min' = -300 | | Receiving types - defines which transaction types the account can receive: | 1 => worked time | 2 => gift | 3 => donation | 4 => currency creation | 5 => currency removal | 6 => migration | | Note: When renaming the account names, make sure also translation keys exists for the new names! | */ // TODO JOERI: Check transaction types 'accounts' => [ 'user' => [ 'name' => 'personal', 'limit_min' => 0, 'limit_max' => 6000, // 100 H 'receiving_types' => [1,2,6], ], 'user_project' => [ 'name' => 'personal project', 'limit_min' => 0, 'receiving_types' => [1,3,6], ], 'organization' => [ 'name' => 'organization', 'limit_min' => 0, 'limit_max' => 12000, // 200 H, default value, manually set organizations with a big turn-over to a higher limit 'receiving_types' => [1,3,6], ], 'bank' => [ 'name' => 'banking system', 'limit_min' => 0, // The 'source' bank and the debit account should have limit_min = NULL, other banks can use this config 'limit_max' => 600000, // 10.000 H 'receiving_types' => [1,4,6], ], 'community' => [ 'name' => 'community', 'limit_min' => 0, // The 'source' bank and the debit account should have limit_min = NULL, other banks can use this config 'limit_max' => null, 'receiving_types' => [3,4], ], 'debit' => [ 'name' => 'debit', 'limit_min' => null, // The 'source' bank and the debit account should have limit_min = NULL, other banks can use this config 'limit_max' => 0, 'receiving_types' => [5], ], ], 'maxLengthHoursInput' => [ // Sets the default max length the amount component can have for the hours input box 'user' => 3, 'organization' => 3, 'bank' => 5, 'admin' => 10, 'transaction_types' => [], ], /* |-------------------------------------------------------------------------- | Bank Properties |-------------------------------------------------------------------------- | */ 'banks' => [ 'level' => [ 0 => 'Source', // Single source bank that has the debit account and can create / remove currency 1 => 'System', // System banks that are private and bridge between level 0 and 2 2 => 'Public', // Public banks that bring currency into circulation, or remove currency from circulation ], ], /* |-------------------------------------------------------------------------- | Profile type permission settings |-------------------------------------------------------------------------- | | Payment types - defines which transaction types the user type can pay: | 1 => worked time | 2 => gift | 3 => donation | 4 => currency creation | 5 => currency removal | 6 => migration | | Note: Internal migration payments between accounts of the same owner are always allowed. | External migration payments are defined below. */ // TODO JOERI: Check transaction types 'permissions' => [ 'user' => [ 'payment_types' => [1,2,3], ], 'user_project' => [ 'payment_types' => [1,2,3], ], 'organization' => [ 'payment_types' => [1,2,3], ], 'bank' => [ 'payment_types' => [1,2,3,5,6], ], 'admin' => [ 'payment_types' => [], // Admins do not have an account and can not make payments ], ], /* |-------------------------------------------------------------------------- | Public / Private settings |-------------------------------------------------------------------------- | */ 'account_info' => [ 'user' => [ 'balance_public' => true, //Privacy during transactions (payment flow) 'sumBalances_public' => true, //Privacy on profile pages (viewing profiles) 'countTransfersSince' => 366 * 2, // days ago 'countTransfersSince_humanReadable' => 'past 2 years', // Short description of countTransfersSince in base language: must have translation key! 'countTransfers_public' => true, 'countTransfersReceived_public' => true, 'countTransfersGiven_public' => true, 'countTransfersReceivedOrGiven_public' => true, 'lastTransferDate_public' => true, ], 'organization' => [ 'balance_public' => true, //Privacy during transactions (payment flow) 'sumBalances_public' => true, //Privacy on profile pages (viewing profiles) 'countTransfersSince' => 366, // days ago 'countTransfersSince_humanReadable' => 'past year', // Short description of countTransfersSince in base language: must have translation key! 'countTransfers_public' => true, 'countTransfersReceived_public' => true, 'countTransfersGiven_public' => true, 'countTransfersReceivedOrGiven_public' => true, 'lastTransferDate_public' => true, ], 'bank' => [ 'balance_public' => false, //Privacy during transactions (payment flow) 'sumBalances_public' => true, //Privacy on profile pages (viewing profiles) 'countTransfersSince' => 366, // days ago 'countTransfersSince_humanReadable' => 'past year', // Short description of countTransfersSince in base language: must have translation key! 'countTransfers_public' => true, 'countTransfersReceived_public' => true, 'countTransfersGiven_public' => true, 'countTransfersReceivedOrGiven_public' => true, 'lastTransferDate_public' => true, ], ], /* |-------------------------------------------------------------------------- | Verification rules and file size limits |-------------------------------------------------------------------------- | Here you can set the verification rules that will be used to verify data | that will be submitted in forms. | | Also you can set here the uploaded file properties and limits as well as | the default files that will be used when no file is uploaded by the user. */ 'rules' => [ 'phone' => 'nullable|phone:mobile,strict', //mobile phone for future sms verification, strict mode, no spaces, no dashes, no brackets 'phone_public' => 'boolean|nullable', 'comment' => 'nullable|string|max:1000', 'profile_user' => [ 'name' => [ 'required', 'string', 'unique:users,name', 'unique:organizations,name', 'unique:banks,name', 'unique:banks,full_name', 'unique:admins,name', 'unique:admins,full_name', 'min:3', 'max:40', function ($attribute, $value, $fail) { // Disallow the following words to be used inside the name: $disallowedWords = [ 'admin', 'administrator', 'superuser', 'super-user', 'super-admin', 'supervisor', 'webmaster', 'web-master', 'tijd-bank', 'tijdbank', 'bank', 'timebank', 'time-bank', 'moderator', 'regulator', 'belasting', 'tax', 'test', ]; // Disallowed names as they might conflict with (future) url paths $completelyDisallowedNames = [ 'test', 'debug', 'user', 'users', 'member', 'members', 'profile', 'organization', 'organizations', 'organisation', 'organisations', 'bank', 'banks', 'admin', 'transaction', 'transactions', 'transfer', 'transfers', 'statement', 'statements', 'payment', 'payments', 'pay', 'paid', 'invoice', 'request', 'requests', 'edit', 'show', 'update', 'message', 'messages', 'messenger', 'messengers', 'berichten', 'chat', 'talk', 'meet', 'drive', 'cloud', 'config', 'settings', 'agenda', 'calendar', 'news', 'nieuws', 'vote', 'poll', 'auth', 'authenticate', 'verify', 'verification', 'date', 'datum', 'confirm', 'mail', 'post', 'posts', 'blog', ]; // Check for disallowed substrings foreach ($disallowedWords as $word) { if (str_contains(strtolower($value), $word)) { $fail(trans('validation.custom.profile_user.name.disallowed', ['word' => $word])); } } // Check for completely disallowed names if (in_array(strtolower($value), array_map('strtolower', $completelyDisallowedNames))) { $fail(trans('validation.custom.profile_user.name.completely_disallowed', ['name' => $value])); } }, 'regex:/^[\p{L}0-9-_ ]+$/u', // letters (including accented), numbers, spaces, dashes and underscores ], 'full_name' => [ 'nullable', 'string', 'unique:organizations,name', 'unique:banks,name', 'unique:banks,full_name', 'unique:admins,name', 'unique:admins,full_name', 'min:3', 'max:40', 'regex:/^[\p{L}0-9-_ ]+$/u', // letters (including accented), numbers, spaces, dashes and underscores function ($attribute, $value, $fail) { // Disallow the following words to be used inside the name: $disallowedWords = [ 'admin', 'administrator', 'superuser', 'super-user', 'super-admin', 'supervisor', 'webmaster', 'web-master', 'tijd-bank', 'tijdbank', 'bank', 'timebank', 'time-bank', 'moderator', 'regulator', 'belasting', 'tax', 'test', ]; // Check for disallowed substrings foreach ($disallowedWords as $word) { if (str_contains(strtolower($value), $word)) { $fail(trans('validation.custom.profile_user.name.disallowed', ['word' => $word])); } } }, ], 'email' => 'required|email|unique:users,email|unique:organizations,email|unique:banks,email|unique:admins,email|max:40', // 'password' => 'required|min:6|same:passwordConfirmation', 'password' => 'required|min:6|confirmed', 'profile_photo' => 'nullable|mimes:gif,jpg,jpeg,png,svg|max:6144', // max 6 MB 'about' => 'nullable|string|max:1000', //Cyclos: no max characters 'about_max_input' => 1000, // should match validation rule! 'about_short' => 'nullable|string|max:150', //Cyclos: no max characters 'about_short_max_input' => 150, // should match validation rule! 'motivation' => 'nullable|string|max:300', //Cyclos: no max characters 'motivation_max_input' => 300, // should match validation rule! 'date_of_birth' => 'nullable|date', 'languages' => 'required', 'languages_id' => 'int', 'website' => 'nullable|regex:/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/', ], 'profile_organization' => [ 'name' => [ 'required', 'string', 'unique:users,name', 'unique:organizations,name', 'unique:banks,name', 'unique:banks,full_name', 'unique:admins,name', 'unique:admins,full_name', 'min:3', 'max:40', function ($attribute, $value, $fail) { // Disallow the following words to be used inside the name: $disallowedWords = [ 'admin', 'administrator', 'superuser', 'super-user', 'supervisor', 'webmaster', 'web-master', 'tijd-bank', 'tijdbank', 'bank', 'timebank', 'time-bank', 'moderator', 'regulator', 'belasting', 'tax', 'test', ]; // Disallowed names as they might conflict with (future) url paths $completelyDisallowedNames = [ 'test', 'debug', 'user', 'users', 'member', 'members', 'profile', 'organization', 'organizations', 'organisation', 'organisations', 'bank', 'banks', 'admin', 'transaction', 'transactions', 'transfer', 'transfers', 'statement', 'statements', 'payment', 'payments', 'pay', 'paid', 'invoice', 'request', 'requests', 'edit', 'show', 'update', 'message', 'messages', 'messenger', 'messengers', 'berichten', 'chat', 'talk', 'meet', 'drive', 'cloud', 'config', 'settings', 'agenda', 'calendar', 'news', 'nieuws', 'vote', 'poll', 'auth', 'authenticate', 'verify', 'verification', 'date', 'datum', 'confirm', 'mail', 'post', 'posts', 'blog', ]; // Check for disallowed substrings foreach ($disallowedWords as $word) { if (str_contains(strtolower($value), $word)) { $fail(trans('validation.custom.profile_user.name.disallowed', ['word' => $word])); } } // Check for completely disallowed names if (in_array(strtolower($value), array_map('strtolower', $completelyDisallowedNames))) { $fail(trans('validation.custom.profile_user.name.completely_disallowed', ['name' => $value])); } }, 'regex:/^[\p{L}0-9-_ ]+$/u', // letters (including accented), numbers, spaces, dashes and underscores ], 'full_name' => [ 'nullable', 'string', 'unique:users,name', 'unique:organizations,name', 'unique:banks,name', 'unique:banks,full_name', 'unique:admins,name', 'unique:admins,full_name', 'min:3', 'max:40', 'regex:/^[\p{L}0-9-_ ]+$/u', // letters (including accented), numbers, spaces, dashes and underscores ], 'email' => 'required|email|unique:users,email|unique:organizations,email|unique:banks,email|unique:admins,email|max:40', 'password' => 'required|string|min:8|confirmed', 'profile_photo' => 'nullable|mimes:gif,jpg,jpeg,png,svg|max:6144', // max 6 MB 'about' => 'nullable|string|max:1000', 'about_max_input' => 1000, // should match validation rule! 'about_short' => 'nullable|string|max:150', 'about_short_max_input' => 150, // should match validation rule! 'motivation' => 'nullable|string|max:200', 'motivation_max_input' => 200, // should match validation rule! 'languages' => 'required', 'languages_id' => 'int', 'website' => 'nullable|regex:/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/', ], 'profile_bank' => [ 'name' => [ 'required', 'string', 'unique:users,name', 'unique:organizations,name', 'unique:banks,name', 'unique:banks,full_name', 'unique:admins,name', 'unique:admins,full_name', 'min:3', 'max:40', 'regex:/^[\p{L}0-9-_ ]+$/u', // letters (including accented), numbers, spaces, dashes and underscores ], 'full_name' => [ 'nullable', 'string', 'unique:users,name', 'unique:organizations,name', 'unique:banks,name', 'unique:banks,full_name', 'unique:admins,name', 'unique:admins,full_name', 'min:3', 'max:40', 'regex:/^[\p{L}0-9-_ ]+$/u', // letters (including accented), numbers, spaces, dashes and underscores ], 'email' => 'required|email|unique:users,email|unique:organizations,email|unique:banks,email|unique:admins,email|max:40', 'password' => 'required|string|min:12|confirmed', 'profile_photo' => 'nullable|mimes:gif,jpg,jpeg,png,svg|max:6144', // max 6 MB 'about' => 'nullable|string|max:1000', 'about_max_input' => 1000, // should match validation rule! 'about_short' => 'nullable|string|max:150', 'about_short_max_input' => 150, // should match validation rule! 'motivation' => 'nullable|string|max:200', 'motivation_max_input' => 200, // should match validation rule! 'languages' => 'required', 'languages_id' => 'int', 'website' => 'nullable|regex:/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/', ], 'profile_admin' => [ 'name' => [ 'required', 'string', 'unique:users,name', 'unique:organizations,name', 'unique:banks,name', 'unique:banks,full_name', 'unique:admins,name', 'unique:admins,full_name', 'min:3', 'max:40', 'regex:/^[\p{L}0-9-_ ]+$/u', // letters (including accented), numbers, spaces, dashes and underscores ], 'full_name' => [ 'nullable', 'string', 'unique:users,name', 'unique:organizations,name', 'unique:banks,name', 'unique:banks,full_name', 'unique:admins,name', 'unique:admins,full_name', 'min:3', 'max:40', 'regex:/^[\p{L}0-9-_ ]+$/u', // letters (including accented), numbers, spaces, dashes and underscores ], 'email' => 'required|email|unique:users,email|unique:organizations,email|unique:banks,email|unique:admins,email|max:40', 'password' => 'required|string|min:12|confirmed', 'profile_photo' => 'nullable|mimes:gif,jpg,jpeg,png,svg|max:6144', // max 6 MB 'about' => 'nullable|string|max:1000', 'about_max_input' => 1000, // should match validation rule! 'about_short' => 'nullable|string|max:150', 'about_short_max_input' => 150, // should match validation rule! 'motivation' => 'nullable|string|max:200', 'motivation_max_input' => 200, // should match validation rule! 'languages' => 'required', 'languages_id' => 'int', 'website' => 'nullable|regex:/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/', ], ], /* |-------------------------------------------------------------------------- | Authentication Settings |-------------------------------------------------------------------------- | */ 'auth' => [ 'minimum_registration_age' => 18, // Minimum age for registration (GDPR Article 8 compliance) ], /* |-------------------------------------------------------------------------- | Base Language |-------------------------------------------------------------------------- | Translations are linked by their context to one base language. | | IMPORTANT: This language is also used as fallback locale, therefore all names, titles, terms, etc. must be at least in this language! | IMPORTANT: The base language can not be changed in an existing project, unless the new base language pre-exists for all translations! */ 'base_language' => 'en', // Do not change in existing project, see note above 'base_language_name' => 'English', // Do not change in existing project, see note above /* |-------------------------------------------------------------------------- | Search settings |-------------------------------------------------------------------------- | Configuration of Elasticsearch matching and highlighting. | More info: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html | */ 'main_search_bar' => [ 'boosted_fields' => [ // fields with a boost factor, 1,0 neutral, 1,5 boosted, 0,5 penalized 'profile' => [ 'name' => 3, 'full_name' => 3, 'cyclos_skills' => 2, 'tags' => 5, 'tag_categories' => 4, 'motivation' => 1, 'about_short' => 1, 'about' => 1, 'district' => 1, 'city' => 1, 'division' => 1, 'country' => 1, ], 'post' => [ 'title' => 2, // title is the most important field 'content' => 1, 'excerpt' => 1.5, 'post_category_name' => 2, // category name is important for posts ], ], 'boosted_models' => [ // search score multiplier 'user' => 1, 'organization' => 3, 'bank' => 3, 'post' => 4, ], 'search' => [ 'type' => 'best_fields', // 'best_fields', 'most_fields', 'cross_fields', 'phrase', 'phrase_prefix' 'prefix_length' => 4, //characters at the beginning of the word that must match 'fragment_size' => 80, //The size of the highlighted fragment in characters. 'fragmenter' => 'span', // 'simple' or 'span' 'number_of_fragments' => 2, // The maximum number of fragments to return. If the number of fragments is set to 0, no fragments are returned. Instead, the entire field contents are highlighted and returned. 'pre-tags' => '', // HTML tags to wrap around highlighted text 'post-tags' => '', // HTML tags to wrap around highlighted text 'order' => 'score', // 'score' or 'none', the order of the fragments 'max_results' => 500, // defines setSize property for maximum amount of search results. Setting a high number will consume a lot of cache memory! ], 'model_indices' => [ // Elasticsearch indices that will be searched (defined in Models and imported by Scout) 'posts_index', 'users_index', 'organizations_index', 'banks_index', 'calls_index', ], 'suggestions' => 4, // max number of suggestions to show in search bar. Showing suggestions slows down the reactivity of the search bar! 'category_ids_posts' => [4,5,6,7,8,113], //Include these posts category_id's 'cache_results' => 5 // minutes (TTL) to store search results in cache memory. TTL is extended whenever a search result is opened. ], /* |-------------------------------------------------------------------------- | Search Optimization settings (Optional) |-------------------------------------------------------------------------- | These settings work with the SearchOptimizationHelper class. | Add this section to timebank-cc.php config if you want | to use the advanced search optimization features. */ 'search_optimization' => [ 'enabled' => true, 'cache_ttl' => 3600, // 1 hour for category hierarchies 'analytics' => [ 'enabled' => true, 'track_searches' => true, 'max_recent_searches' => 10, 'retention_hours' => 24, 'track_location_data' => true, // Track location-based search patterns ], 'boost_factors' => [ 'verified_profiles' => 1.2, 'complete_profiles' => 1.1, 'recent_activity' => 1.05, 'exact_category_match' => 2.0, // Multiplier for exact category name matches 'fuzzy_category_match' => 1.0, // Multiplier for fuzzy category matches ], 'location_boosts' => [ 'same_district' => 1.0, //5.0, // Highest boost for same district 'same_city' => 1.1, //3.0, // High boost for same city 'same_division' => 1.5, //2.0, // Medium boost for same division/state 'same_country' => 1.0, //1.5, // Base boost for same country 'different_country' => 1.0, // Neutral for different countries 'no_location' => 1.0, //0.9, // Slight penalty for profiles without location ], 'performance' => [ 'enable_query_caching' => true, 'cache_search_results' => true, 'optimize_highlights' => true, 'batch_process_results' => true, 'enable_location_caching' => true, // Cache location hierarchy lookups ], ], /* |-------------------------------------------------------------------------- | Payment settings |-------------------------------------------------------------------------- | */ 'payment' => [ 'amount_rule' => 'required|integer|min:1', 'description_rule' => 'required|string|min:3|max:500', ], /* |-------------------------------------------------------------------------- | Post settings |-------------------------------------------------------------------------- | */ 'posts' => [ 'postable_is_auth_user' => true, // Post editor profile that is stored. Set to true: Users, set to false: active profile models stored in session (users, organizations, banks, admins) 'site-content-writer' => 'Timebank.cc', // Writer name for general site content such as static pages. This name can be a non-exsisting user / organization 'title_rule' => 'required|string|min:3|max:150', 'title_max_input' => 150, // should match tile_rule 'slug_rule' => ['required', 'string', 'min:3', 'max:150', 'regex:/^[\pL\pM\pN-]+$/u'], 'excerpt_rule' => 'nullable|string|string|max:500', 'excerpt_max_input' => 500, // should match excerpt_rule 'content_rule' => 'nullable|string|max:1048576', // max 1 MB in bytes 'content_max_input' => 10000, // 10000 characters is equivalent of a main newspaper article / 10 min of reading time 'image_rule' => 'nullable|image|mimes:jpg,jpeg,png,gif,webp|max:12288', // max 12 MB - check also the max file size in the .htaccess file and php.ini! 'media_owner_rule' => 'nullable|string|max:150', 'media_caption_rule' => 'nullable|string|max:300', 'media_caption_max_input' => 300, // should match media_caption_rule 'meeting_venue_rule' => 'nullable|string|max:50', 'meeting_address_rule' => 'nullable|string|max:100', 'meeting_transaction_types' => [1,2,3,], // Possible transaction types: 1 = Work, 2 = Gift, 3 = Donation 'static' => [ 'getting-started' => [ 'limit' => 1, // Maximum number of posts to show in the static getting-started page 'hideAuthor' => true, // Hide the author of the static posts in the getting-started page ], 'faq' => [ 'limit' => null, // Maximum number of posts to show in the static FAQ page 'hideAuthor' => true, // Hide the author of the static posts in the FAQ page ], 'organizations' => [ 'limit' => null, // Maximum number of posts to show in the static organizations page 'hideAuthor' => true, // Hide the author of the static posts in the organizations page ], 'principles' => [ 'limit' => 1, // Maximum number of posts to show in the static principles page 'hideAuthor' => true, // Hide the author of the static posts in the principles page ], 'events' => [ 'limit' => 5, // Maximum number of posts to show in the static events page 'hideAuthor' => true, // Hide the author of the static posts in the events page ], 'the-hague' => [ 'limit' => 1, // Maximum number of posts to show in the static the-hague page 'hideAuthor' => true, // Hide the author of the static posts in the the-hague page ], 'lekkernassuh' => [ 'limit' => 1, // Maximum number of posts to show in the static lekkernassuh page 'hideAuthor' => true, // Hide the author of the static posts in the lekkernassuh page ], 'amst-brus-lisb' => [ 'limit' => 1, // Maximum number of posts to show in the static ams-bru-lis page 'hideAuthor' => true, // Hide the author of the static posts in the ams-bru-lis page ], 'work-with-us' => [ 'limit' => 1, // Maximum number of posts to show in the static work-with-us page 'hideAuthor' => false, // Hide the author of the static posts in the work-with-us page ], 'philosophy' => [ 'limit' => 1, // Maximum number of posts to show in the static philosophy page 'hideAuthor' => true, // Hide the author of the static posts in the philosophy page ], 'open-source' => [ 'limit' => 1, // Maximum number of posts to show in the static open-source page 'hideAuthor' => true, // Hide the author of the static posts in the open-source page ], 'timebank-organization' => [ 'limit' => null, // Maximum number of posts to show in the static timebank-organization page 'hideAuthor' => true, // Hide the author of the static posts in the timebank-organization page ], 'history' => [ 'limit' => null, // Maximum number of posts to show in the static history page 'hideAuthor' => false, // Hide the author of the static posts in the history page ], 'press-media' => [ 'limit' => 10, // Maximum number of posts to show in the static press-media page 'hideAuthor' => true, // Hide the author of the static posts in the press-media page ], 'economics-and-research' => [ 'limit' => null, // Maximum number of posts to show in the static economics-and-research page 'hideAuthor' => false, // Hide the author of the static posts in the economics-and-research page ], 'meet-the-team' => [ 'limit' => 1, // Maximum number of posts to show in the static meet-the-team page 'hideAuthor' => false, // Hide the author of the static posts in the meet-the-team page ], 'messenger' => [ 'limit' => 1, // Maximum number of posts to show in the static messenger page 'hideAuthor' => true, // Hide the author of the static posts in the messenger page ], ] ], 'calls' => [ 'till_max_days' => 60, // Maximum number of days ahead a Call expiry date can be set for regular users. Set to null for no maximum. 'till_max_days_non_user' => 366, // Maximum number of days ahead for non-user callables (Organization, Bank). Set to null for no maximum. When both till_max_days and till_max_days_non_user are null, no max expiry is enforced for anyone. 'default_expiry_days' => null, // Default expiry in days from creation date. Set to null for no default, or e.g. 14 for 14 days 'content_max_input' => 200, // Max characters for call description 'expiry_warning_days' => 7, // Show an "Expires" badge on the call view when expiry is within this many days. Set to null to disable. // CSS filter applied to callable profile photos shown to unauthenticated (guest) users. // Set guest_photo_blur_px to 0 to disable all filters entirely. // blur_px: pixels of blur — controls dot size in the halftone effect (higher = larger/softer dots, lower = finer dots) // contrast: percentage — crushes blurred blobs into sharp dots (higher = more defined dots, e.g. 500 for strong effect) // saturate: percentage — 0 = greyscale, 100 = full colour, values in between desaturate partially // brightness: percentage — lifts overall brightness (100 = neutral, 110 = slightly brighter, lower = darker) // TODO remove this unused config? 'guest_photo_blur_px' => 2, 'guest_photo_contrast' => 150, // % 'guest_photo_saturate' => 60, // % 'guest_photo_brightness' => 130, // % 'carousel' => [ // --- Locality filtering ------------------------------------------- 'include_unknown_location' => true, 'include_same_division' => true, 'include_same_country' => true, // --- Exclusion --------------------------------------------------- // Note: paused, suppressed and expired calls are always excluded // regardless of config to prevent abuse. 'exclude_non_public' => false, 'exclude_own_calls' => true, // --- Pool size --------------------------------------------------- 'max_cards' => 12, 'pool_multiplier' => 5, // Fetch (pool_multiplier × max_cards) candidates from DB, score them in PHP, then show the top max_cards. Higher = better scoring quality but more DB load. // --- Boost factors ----------------------------------------------- 'boost_same_district' => 3.0, 'boost_location_city' => 2.0, 'boost_location_division' => 1.5, 'boost_location_country' => 1.1, 'boost_location_unknown' => 2.5, 'boost_like_count' => 0.05, 'boost_star_count' => 0.10, 'boost_recent_from' => 1.3, 'recent_days' => 14, 'boost_soon_till' => 1.2, 'soon_days' => 7, 'boost_callable_user' => 1.0, 'boost_callable_organization' => 1.2, 'boost_callable_bank' => 1.0, 'show_score' => false, 'show_score_for_admins' => true, ], 'welcome_carousel' => [ 'max_cards' => 12, 'pool_multiplier' => 5, // Fetch (pool_multiplier × max_cards) candidates from DB, score in PHP, then show the top max_cards. // Boost factors (no location boosts — no profile context for guests) 'boost_like_count' => 0.05, 'boost_star_count' => 0.10, 'boost_recent_from' => 1.3, 'recent_days' => 14, 'boost_soon_till' => 1.2, 'soon_days' => 7, 'boost_callable_user' => 1.0, 'boost_callable_organization' => 1.2, 'boost_callable_bank' => 1.0, 'show_score' => false, 'show_score_for_admins' => true, ], ], /* |-------------------------------------------------------------------------- | Tags settings |-------------------------------------------------------------------------- | */ 'tags' => [ 'allow_tag_transations_for_non_admins' => false, // Set to true if all profile types can also add a translation when creating a new skill tag 'name_rule' => [ 'required', 'string', 'min:3', 'max:80', function ($attribute, $value, $fail) { if (preg_match('/[!?@#\$*\_\+{}\[\]<>\/|=.,\\\\]/', $value)) { $fail(__('The :attribute cannot contain special characters like !?@#$*_+{}[]<>/\|=,.')); } }, function ($attribute, $value, $fail) { if (!preg_match('/\S+\s+\S+/', $value)) { // If the input doesn't have at least 2 words, fail the validation for this field $fail(__('The :attribute must be at least 2 words.')); } }, ], 'exists_in_current_locale_rule' => [ function ($attribute, $value, $fail) { $existsInCurrentLocale = Illuminate\Support\Facades\DB::table('taggable_tags') ->join('taggable_locales', 'taggable_tags.tag_id', '=', 'taggable_locales.taggable_tag_id') ->where('taggable_locales.locale', app()->getLocale()) ->where(function ($query) use ($value) { $query->where('taggable_tags.name', $value) ->orWhere('taggable_tags.normalized', $value); }) ->exists(); if ($existsInCurrentLocale) { $fail(__('This :attribute name already exists.')); } }, ], 'comment_rule' => 'string|max:500|nullable', ], /* |-------------------------------------------------------------------------- | Media Library settings |-------------------------------------------------------------------------- | */ 'media_library' => [ 'max_file_size' => 1024 * 1024 * 12, // 12 MB ], /* |-------------------------------------------------------------------------- | Custom Messenger settings |-------------------------------------------------------------------------- | */ 'messenger' => [ 'default_unread_mail_delay' => 8, // In hours. After this default delay an email will be send to notify an unread chat message. Users / profiles can change this in their settings ], /* |-------------------------------------------------------------------------- | WireChat Configuration |-------------------------------------------------------------------------- | Configuration for the WireChat messaging system including disappearing | messages, cleanup schedules, user permissions, and attachments. */ 'wirechat' => [ 'disappearing_messages' => [ // Allow users to mark messages as "kept" to prevent immediate deletion 'allow_users_to_keep' => true, // Duration in DAYS before regular messages are deleted 'duration' => 120, // 120 days (4 months) - timebank_cc custom value // Duration in DAYS before kept messages are also deleted // Even "kept" messages are eventually cleaned up (prevents orphaned data) 'kept_messages_duration' => 720, // 720 days (24 months / 2 years) // Laravel schedule frequency for cleanup job // Options: 'everyMinute', 'everyFiveMinutes', 'everyTenMinutes', 'hourly' 'cleanup_schedule' => 'hourly', ], 'profile_deletion' => [ // When a profile is permanently deleted, release their kept messages // This sets kept_at = null, allowing normal cleanup to process them 'release_kept_messages' => true, ], 'attachments' => [ 'storage_folder' => 'attachments', 'storage_disk' => 'public', 'disk_visibility' => 'public', // Use 'private' to enforce temporary URLs 'max_uploads' => 5, // Maximum number of files that can be uploaded at once // Media Upload Settings (images/videos) 'media_mimes' => ['png', 'jpg', 'jpeg', 'gif'], // Allowed media file types 'media_max_upload_size' => 12288, // Size in KB (12 MB) // File Upload Settings (documents) 'file_mimes' => ['zip', 'rar', 'txt', 'pdf'], // Allowed document file types 'file_max_upload_size' => 12288, // Size in KB (12 MB) ], ], /* |-------------------------------------------------------------------------- | Reaction settings |-------------------------------------------------------------------------- */ 'reactions' => [ 'star' => [ 'enabled' => true, 'only_with_interaction' => true, // Require interaction between profiles 'interaction' => 'hasTransactionsWith', // Method to check for interaction 'icon_file' => 'star.svg', 'display_name' => 'Star', 'description' => 'Give a star to recommend and show appreciation.', // Tooltip text 'disabled_reasons' => [ 'cannot_react_own_profile' => 'You cannot give yourself a star.', 'no_interaction' => 'You first need to have an exchange with each other.', ], ], 'bookmark' => [ 'enabled' => true, 'only_with_interaction' => false, // Anyone can bookmark 'interaction' => 'hasTransactionsWith', // Not used since only_with_interaction is false 'icon_file' => 'bookmark.svg', 'display_name' => 'Bookmark', 'description' => 'Save in your contact list.', // Tooltip text 'disabled_reasons' => [ 'cannot_react_own_profile' => 'You cannot bookmark your own profile.', 'no_interaction' => 'You need an interaction to bookmark.', ], ], 'reserve' => [ 'enabled' => true, 'only_with_interaction' => false, // Anyone can reserve 'interaction' => 'hasTransactionsWith', // Not used since only_with_interaction is false 'icon_file' => 'reserved.svg', 'display_name' => 'Reserve', 'description' => 'Puts you on the reservations lists.', // Tooltip text 'disabled_reasons' => [ 'cannot_react_own_profile' => 'You cannot reserve right now.', 'no_interaction' => 'You need an interaction to reserve this.', ], 'count_public_threshold' => 3 // Threshold before nr of reservations is shown publicly ], 'like' => [ 'enabled' => true, 'only_with_interaction' => false, 'interaction' => 'hasTransactionsWith', // WARNING: new interaction methods should always be tested on development server! 'icon_file' => 'heart-icon.svg', 'display_name' => 'Like', 'description' => 'Show appreciation.', 'disabled_reasons' => [ 'cannot_react_own_profile' => 'You cannot like your own content.', 'no_interaction' => 'You need an interaction to like this.', ], ], 'vote' => [ 'enabled' => false, // Disabled reaction type 'only_with_interaction' => false, 'interaction' => 'hasParticipatedWithExample', // WARNING: new interaction methods should always be tested on development server! 'icon_file' => 'hand-raised.svg', 'display_name' => 'Vote', 'description' => 'Cast a vote.', 'disabled_reasons' => [ 'cannot_react_own_profile' => 'You cannot vote for yourself.', 'no_interaction' => 'You need to have participated to vote.', ], ], 'dislike' => [ 'enabled' => false, 'only_with_interaction' => false, 'interaction' => 'hasCollaboratedWithExample', // WARNING: new interaction methods should always be tested on development server! 'icon_file' => 'thumbs-down.svg', 'display_name' => 'Dislike', 'description' => 'Show disapproval.', 'disabled_reasons' => [ 'cannot_react_own_profile' => 'You cannot dislike your own content.', 'no_interaction' => 'You need an interaction to dislike this.', ], ], ], /* |-------------------------------------------------------------------------- | Online settings |-------------------------------------------------------------------------- */ 'online' => [ 'contact_list' => [ 'dashboard' => [ 'enabled' => true, // enable / disable online contact list on dashboard 'reaction_types_for_user' => ['Star', 'Bookmark'], // show in contact list if exists in array of reaction types 'reaction_types_for_organization' => ['Bookmark'], // show in contact list if exists in array of reaction types 'reaction_types_for_bank' => ['Star, Bookmark'], // show in contact list if exists in array of reaction types ], ], ], /* |-------------------------------------------------------------------------- | Mailing (Bulk Mail Newsletter) Configuration |-------------------------------------------------------------------------- */ 'mailing' => [ 'send_delay_seconds' => 5, // Delay between individual emails 'batch_size' => 10, // Process emails in batches 'max_retries' => 3, // Maximum retry attempts for failed emails 'retry_delay_minutes' => 15, // Base delay before first retry (exponential backoff applies) 'retry_multiplier' => 2, // Multiplier for exponential backoff (delay * multiplier^attempt) 'max_retry_delay_hours' => 24, // Maximum delay between retries (caps exponential backoff) 'abandon_after_hours' => 72, // Completely abandon email retries after this time 'use_fallback_locale' => false, // Whether to use fallback locale when recipient's preferred locale is unavailable 'fallback_locale' => 'en', // Fallback locale to use when recipient's preferred locale has no translations 'from_address' => [ 'local_newsletter' => 'noreply@timebank.cc', 'general_newsletter' => 'noreply@timebank.cc', 'system_message' => 'admin@timebank.cc' ], 'copy_to_mailpit' => true, // Set to true to send a copy of every outgoing email to Mailpit. Run: php artisan queue:restart after changing. 'mailpit_host' => 'localhost', // Mailpit SMTP host 'mailpit_port' => 1025, // Mailpit SMTP port 'bounce_address' => env('MAIL_BOUNCE_ADDRESS', 'bounces@timebank.cc'), 'templates' => [ 'wrapper' => 'emails.newsletter.wrapper', 'news_block' => 'emails.newsletter.blocks.news', 'article_block' => 'emails.newsletter.blocks.article', 'event_block' => 'emails.newsletter.blocks.event', 'image_block' => 'emails.newsletter.blocks.image' ], /* |-------------------------------------------------------------------------- | Bounce Handling Thresholds |-------------------------------------------------------------------------- | Configure how many hard bounces are required before taking action. | This provides a conservative approach to prevent false positives. */ 'bounce_thresholds' => [ // Number of hard bounces before email is suppressed from future mailings 'suppression_threshold' => 1, // Number of hard bounces before email_verified_at is set to null 'verification_reset_threshold' => 1, // Time window in days to count bounces (prevents old bounces from accumulating) 'counting_window_days' => 365, // Only count these bounce types toward thresholds 'counted_bounce_types' => ['hard'], // Specific bounce reasons that count as definitive hard bounces 'definitive_hard_bounce_patterns' => [ 'user unknown', 'no such user', 'mailbox unavailable', 'does not exist', 'invalid recipient', 'address rejected', '5.1.1', // User unknown '5.1.2', // Domain not found '5.1.3', // Invalid address '550', // Mailbox unavailable '551', // User not local ], // Automatic cleanup settings 'automatic_cleanup' => [ // Day of week for cleanup (0 = Sunday, 1 = Monday, etc.) 'day_of_week' => 1, // Monday // Time to run cleanup (24-hour format) 'time' => '03:00', // Number of days after which to delete soft bounces 'cleanup_days' => 90, // Only cleanup these bounce types (hard bounces are kept for suppression) 'cleanup_bounce_types' => ['soft', 'unknown'], ], ] ], /* |-------------------------------------------------------------------------- | Footer Navigation Configuration |-------------------------------------------------------------------------- | Configure footer sections and links for white-labeling. | Each section can be reordered, hidden, or customized per platform. | All titles should have translation keys. */ 'footer' => [ 'sections' => [ [ 'title' => 'Who we are', 'order' => 1, 'visible' => true, 'links' => [ ['route' => 'static-philosophy', 'title' => 'Our philosophy', 'order' => 1, 'visible' => true], ['route' => 'static-timebank-organization', 'title' => 'Timebank organization', 'order' => 2, 'visible' => true], ['route' => 'static-history', 'title' => 'History', 'order' => 3, 'visible' => true], ['route' => 'static-press-media', 'title' => 'Press and media', 'order' => 4, 'visible' => true], ['route' => 'static-research', 'title' => 'Economics and research', 'order' => 5, 'visible' => true], ], ], [ 'title' => 'Community', 'order' => 2, 'visible' => true, 'links' => [ ['route' => 'static-events', 'title' => 'Events', 'order' => 1, 'visible' => true], ['route' => 'static-the-hague', 'title' => 'The Hague', 'order' => 2, 'visible' => true], ['route' => 'static-lekkernassuh', 'title' => 'Lekkernassûh', 'order' => 3, 'visible' => true], ['route' => 'static-amst-brus-lisb', 'title' => 'Amsterdam, Brussels, Lisbon', 'order' => 4, 'visible' => true], ['route' => 'static-work-w-us', 'title' => 'Work with us', 'order' => 5, 'visible' => true], ['route' => 'static-open-source', 'title' => 'Open source', 'order' => 6, 'visible' => true], ], ], [ 'title' => 'Help', 'order' => 3, 'visible' => true, 'links' => [ ['route' => 'static-getting-started', 'title' => 'Getting started', 'order' => 1, 'visible' => true], ['route' => 'static-faq', 'title' => 'FAQ', 'order' => 2, 'visible' => true], ['route' => 'static-organizations', 'title' => 'Organizations', 'order' => 3, 'visible' => true], ['route' => 'static-principles', 'title' => 'messages.platform_principles', 'order' => 4, 'visible' => true], ['route' => 'static-privacy', 'title' => 'Privacy policy', 'order' => 5, 'visible' => true], ], ], [ 'title' => 'Contact us', 'order' => 4, 'visible' => true, 'links' => [ ['route' => 'static-team', 'title' => 'Meet the team', 'order' => 1, 'visible' => true], ['route' => 'static-report-issue', 'title' => 'Report an issue', 'order' => 2, 'visible' => true], // ['route' => 'static-report-error', 'title' => 'Report an issue', 'order' => 2, 'visible' => false], ['route' => 'static-messenger', 'title' => 'Chat messenger', 'order' => 3, 'visible' => true, 'auth_required' => true], ['url' => 'mailto:info@timebank.cc', 'title' => 'info@timebank.cc', 'order' => 4, 'visible' => true], ], ], ], 'tagline' => 'Your time is currency', ], /* |-------------------------------------------------------------------------- | Search Engine Indexing |-------------------------------------------------------------------------- */ 'seo' => [ 'allow_indexing_guest' => true, // Allow search engines to index guest pages 'allow_indexing_auth' => false, // Allow search engines to index authenticated pages ], /* |-------------------------------------------------------------------------- | Profile Session Timeouts |-------------------------------------------------------------------------- | | Define the inactivity timeout in minutes for each profile type. | After the specified timeout, the user's session will expire and they | will be logged out automatically. This provides security by ensuring | inactive sessions are terminated. | | The key should be the fully qualified class name of the model. | A default value is used if the active profile type isn't found here. | | IMPORTANT: These timeouts OVERRIDE the SESSION_LIFETIME setting from .env | They are enforced by ProfileSessionTimeout middleware. | | Security Best Practices: | - User profiles: Short timeout (30-120 min) for regular accounts | - Organizations: Medium timeout (30-60 min) for community profiles | - Banks: Short timeout (15-30 min) for financial operations | - Admins: Very short timeout (15-30 min) for privileged access | */ 'profile_timeouts' => [ App\Models\User::class => 120, // minutes App\Models\Organization::class => 120, App\Models\Bank::class => 120, App\Models\Admin::class => 120, ], 'profile_timeout_default' => 120, // minutes. Fallback default if type not listed or no profile active 'profile_inactive' => [ 'days_not_logged_in' => 366 * 2, // Profile marked as inactive after this many days without login // TODO change back to 2??? 're-activate_at_login' => true, // Remove inactive_at record at login 'messenger_hidden' => false, // Not searchable in chat messenger is true 'profile_search_hidden' => true, // Profile page is hidden from main search bar for non-Admin and non-Banks 'profile_hidden' => true, // Profile page is hidden for non-Admin and non-Banks 'profile_labeled' => true, // Profile has inactive label ], 'profile_email_unverified' => [ 'messenger_hidden' => true, // True means not searchable in chat messenger 'profile_search_hidden' => true, // Profile page is hidden from main search bar for non-Admin and non-Banks 'profile_hidden' => false, // Profile page is hidden for non-Admin and non-Banks 'profile_labeled' => true, // Profile has email unverified label ], 'profile_incomplete' => [ 'messenger_hidden' => false, // True means not searchable in chat messenger 'profile_search_hidden' => true, // Profile page is hidden from main search bar for non-Admin and non-Banks 'profile_hidden' => false, // Profile page is hidden for non-Admin and non-Banks 'profile_labeled' => false, // Profile has incomplete label 'check_fields' => ['about', 'about_short', 'motivation', 'cyclos_skills'], // One these fields that must have data to be considered as a complete profile 'check_fields_min_total_length' => 10, // Minimum total length of all check_fields that must be filled in to be considered as a complete profile 'check_relations' => ['tags', 'languages', 'locations'], // One these relations that must be filled in to be considered as a complete profile 'show_warning_modal' => true, // Show profile incomplete warning modal on edit profile page 'no_exchanges_yet_label' => true, // Show 'No exchanges yet, but ready to help' label when profile has never received a transaction ], /* |-------------------------------------------------------------------------- | Delete Profile Settings |-------------------------------------------------------------------------- | Configure what happens to account balances when a profile is deleted. | | The logic follows this elseif structure: | 1. If 'donate_balances_to_organization_account_specified' is true: | User can select an organization to donate balance to | 2. Else if 'transfer_balances_to_bank_client' is true: | Transfer balances to a bank the profile was a client of | 3. Else if 'transfer_balances_to_account_id' is set (not null): | Transfer balances to this specific account id | 4. Else if 'transfer_balances_to_debit_account' is true: | Transfer balances to debit account (removes currency from circulation) */ 'delete_profile' => [ // Grace period: Days to wait before permanent deletion (allows restoration) 'grace_period_days' => 1, // Profiles can be restored within this many days after deletion // NOTE: These are days AFTER the profile is marked inactive (not days since last login) // Total time = days_not_logged_in (350) + days_after_inactive 'days_after_inactive' => [ 'warning_1' => 0, // First warning 'warning_2' => 30, // Second warning 'warning_final' => 60, // Final warning 'run_delete' => 90, // Delete profile ], 'account_balances' => [ // If accounts of deleted profile are not 0? 'donate_balances_to_organization_account_specified' => true, // Allow profile to donate all balances to an organization they specify - only when self-deleting // elseif other options are valid when auto-deleting or when no donation to organisation is specified when self-deleteing 'transfer_balances_to_bank_client' => false, // BANKCLIENTS ARE NOT USED YET - Transfer all balances to a (local) bank the profile was a bankClient of // elseif 'transfer_balances_to_account_id' => null, // Or transfer balances to this specific account id. Set to null to disable setting // else 'transfer_balances_to_debit_account' => true, // Set always to true as dead currency needs to be cleaned ], 'log_trimming' => [ // Automatic log file trimming for inactive profile processing logs 'retention_days' => 30, // Keep log entries for this many days (default: 30) 'schedule_frequency' => 'monthly', // Laravel schedule frequency: daily, weekly, monthly (default: monthly) 'schedule_time' => '03:30', // Time to run log trimming (24-hour format, default: 03:30) ], ], ];