Files
timebank-cc-public/config/timebank_cc.php.example
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

1583 lines
72 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
use Illuminate\Validation\Rule;
return [
/*
|--------------------------------------------------------------------------
| Initial passwords for 1st login after installation,
| should be changed after login!
|--------------------------------------------------------------------------
|
*/
'init_passwords' => [
'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
],
/*
|--------------------------------------------------------------------------
| 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' => '<span class="font-semibold text-white leading-tight">', // HTML tags to wrap around highlighted text
'post-tags' => '</span>', // 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)
],
],
];