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

1491 lines
67 KiB
Plaintext

<?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',
],
'user_admin' => [
'email' => 'info@timebank.cc',
'name' => 'info@timebank.cc',
],
'content_admin' => [
'email' => 'info@timebank.cc',
'name' => config('app.name'),
],
'payments' => [
'email' => 'payments@timebank.cc',
'name' => config('app.name'),
],
'chat_messenger' => [
'email' => 'chats@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 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' => 365 // 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' => 365 * 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' => 365, // 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' => 365, // 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' => 1,
'full_name' => 1,
'cyclos_skills' => 1.5, // lower than tags, to promote use of tags
'tags' => 2,
'tag_categories' => 1.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' => 50, // 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',
],
'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' => 5.0, // Highest boost for same district
'same_city' => 3.0, // High boost for same city
'same_division' => 2.0, // Medium boost for same division/state
'same_country' => 1.5, // Base boost for same country
'different_country' => 1.0, // Neutral for different countries
'no_location' => 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
],
'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
],
'research' => [
'limit' => null, // Maximum number of posts to show in the static research page
'hideAuthor' => false, // Hide the author of the static posts in the 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
],
]
],
/*
|--------------------------------------------------------------------------
| 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' => 'news@timebank.cc',
'general_newsletter' => 'news@timebank.cc',
'system_message' => 'system@timebank.cc'
],
'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' => 'Economic 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],
],
],
[
'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' => false, // 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 => 60,
App\Models\Bank::class => 30,
App\Models\Admin::class => 360, // TODO: change to 30 for production
],
'profile_timeout_default' => 120, // minutes. Fallback default if type not listed or no profile active
'profile_inactive' => [
'days_not_logged_in' => 365 * 2, // Profile marked as inactive after this many days without login
'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, // 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' => 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' => 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' => 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
],
/*
|--------------------------------------------------------------------------
| 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
// elseif
'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 this to true to transfer balance to debit account (removes dead currency)
],
'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)
],
],
];