showUser($id); } if (strtolower($type) === strtolower(__('organization'))) { return $this->showOrganization($id); } if (strtolower($type) === strtolower(__('bank'))) { return $this->showBank($id); } return view('profile.not_found'); } public function showActive() { $profile = getActiveProfile(); return $this->show(__(class_basename($profile)), $profile->id); } public function showUser($id) { $user = User::select([ 'id', 'name', 'full_name', 'profile_photo_path', 'about', 'about_short', 'motivation', 'date_of_birth', 'website', 'phone_public', 'phone', 'cyclos_skills', 'lang_preference', 'email_verified_at', 'created_at', 'last_login_at', 'love_reactant_id', 'inactive_at', 'deleted_at' ]) ->with([ 'organizations:id,name,profile_photo_path', 'accounts:id,name,accountable_type,accountable_id', 'languages:id,name,lang_code,flag', 'tags:tag_id', 'socials:id,name,icon,urL_structure', 'locations.district.translations:district_id,name', 'locations.city.translations:city_id,name', 'locations.division.translations:division_id,name', 'locations.country.translations:country_id,name', 'loveReactant.reactions.reacter.reacterable', 'loveReactant.reactionCounters', ]) ->find($id); if (!$user) { return view('profile.not_found'); } $states = $this->getActiveStates($user); $canManageProfiles = $this->getCanManageProfiles(); $canViewIncompleteProfiles = $this->canViewIncompleteProfiles(); $isViewingOwnProfile = getActiveProfile() && get_class(getActiveProfile()) === User::class && getActiveProfile()->id === $user->id; // For admins/banks: Always show incomplete label if profile is incomplete (ignore config) if ($canManageProfiles) { $states['hidden'] = false; $states['inactiveLabel'] = !$user->isActive(); $states['inactiveSince'] = $user->inactive_at ? \Illuminate\Support\Carbon::parse($user->inactive_at)->diffForHumans() : ''; $states['emailUnverifiedLabel'] = !$user->isEmailVerified(); $states['incompleteLabel'] = $user->hasIncompleteProfile($user); } // When viewing own profile, never hide but show all labels if ($isViewingOwnProfile) { $states['hidden'] = false; $states['inactiveLabel'] = !$user->isActive(); $states['inactiveSince'] = $user->inactive_at ? \Illuminate\Support\Carbon::parse($user->inactive_at)->diffForHumans() : ''; $states['emailUnverifiedLabel'] = !$user->isEmailVerified(); $states['incompleteLabel'] = $user->hasIncompleteProfile($user); // Show incomplete warning modal when viewing own profile $states['showIncompleteWarning'] = timebank_config('profile_incomplete.show_warning_modal') && $user->hasIncompleteProfile($user); } // Check if profile should be hidden if ($states['hidden'] && !$canManageProfiles && !$isViewingOwnProfile) { // Only allow Admin and Bank profiles to view incomplete-only profiles // Inactive profiles should be hidden from everyone except admins/banks $isIncompleteOnly = ($states['isIncomplete'] ?? false) && !($states['inactive'] ?? false) && !($states['emailUnverifiedLabel'] ?? false); // Show profile only if it's incomplete-only AND viewer is admin/bank if (!($isIncompleteOnly && $canViewIncompleteProfiles)) { return view('profile.not_found'); } } $user->reactionCounter = $user->loveReactant->reactionCounters->first() ? (int)$user->loveReactant->reactionCounters->first()->weight : null; $profile = $user; $header = __('Personal profile'); return $profile != null ? view('profile.show', array_merge(compact('profile', 'header'), $states)) : abort(403); } public function showOrganization($id) { $organization = Organization::select([ 'id', 'name', 'profile_photo_path', 'about', 'motivation', 'website', 'phone_public', 'phone', 'cyclos_skills', 'lang_preference', 'email_verified_at', 'created_at', 'last_login_at', 'love_reactant_id', 'inactive_at', 'deleted_at' ]) ->with([ 'managers:id,name,profile_photo_path', 'accounts:id,name,accountable_type,accountable_id', 'languages:id,name,lang_code,flag', 'tags:tag_id', 'socials:id,name,icon,urL_structure', 'locations.district.translations:district_id,name', 'locations.city.translations:city_id,name', 'locations.division.translations:division_id,name', 'locations.country.translations:country_id,name', 'loveReactant.reactions.reacter.reacterable', // 'loveReactant.reactions.type', 'loveReactant.reactionCounters', // 'loveReactant.reactionTotal', ]) ->find($id); if (!$organization) { return view('profile.not_found'); } $states = $this->getActiveStates($organization); $canManageProfiles = $this->getCanManageProfiles(); $canViewIncompleteProfiles = $this->canViewIncompleteProfiles(); $isViewingOwnProfile = getActiveProfile() && get_class(getActiveProfile()) === Organization::class && getActiveProfile()->id === $organization->id; // For admins/banks: Always show incomplete label if profile is incomplete (ignore config) if ($canManageProfiles) { $states['hidden'] = false; $states['inactiveLabel'] = !$organization->isActive(); $states['inactiveSince'] = $organization->inactive_at ? \Illuminate\Support\Carbon::parse($organization->inactive_at)->diffForHumans() : ''; $states['emailUnverifiedLabel'] = !$organization->isEmailVerified(); $states['incompleteLabel'] = $organization->hasIncompleteProfile($organization); } // When viewing own profile, never hide but show all labels if ($isViewingOwnProfile) { $states['hidden'] = false; $states['inactiveLabel'] = !$organization->isActive(); $states['inactiveSince'] = $organization->inactive_at ? \Illuminate\Support\Carbon::parse($organization->inactive_at)->diffForHumans() : ''; $states['emailUnverifiedLabel'] = !$organization->isEmailVerified(); $states['incompleteLabel'] = $organization->hasIncompleteProfile($organization); // Show incomplete warning modal when viewing own profile $states['showIncompleteWarning'] = timebank_config('profile_incomplete.show_warning_modal') && $organization->hasIncompleteProfile($organization); } // Check if profile should be hidden if ($states['hidden'] && !$canManageProfiles && !$isViewingOwnProfile) { // Only allow Admin and Bank profiles to view incomplete-only profiles // Inactive profiles should be hidden from everyone except admins/banks $isIncompleteOnly = ($states['isIncomplete'] ?? false) && !($states['inactive'] ?? false) && !($states['emailUnverifiedLabel'] ?? false); // Show profile only if it's incomplete-only AND viewer is admin/bank if (!($isIncompleteOnly && $canViewIncompleteProfiles)) { return view('profile.not_found'); } } $organization->reactionCounter = $organization->loveReactant->reactionCounters->first() ? (int)$organization->loveReactant->reactionCounters->first()->weight : null; $profile = $organization; $header = __('Organization profile'); return $profile != null ? view('profile.show', array_merge(compact('profile', 'header'), $states)) : abort(403); } public function showBank($id) { $bank = Bank::select([ 'id', 'name', 'profile_photo_path', 'about', 'motivation', 'website', 'phone_public', 'phone', 'cyclos_skills', 'lang_preference', 'email_verified_at', 'created_at', 'last_login_at', 'love_reactant_id', 'inactive_at', 'deleted_at' ]) ->with([ 'managers:id,name,profile_photo_path', 'accounts:id,name,accountable_type,accountable_id', 'languages:id,name,lang_code,flag', 'tags:tag_id', 'socials:id,name,icon,urL_structure', 'locations.district.translations:district_id,name', 'locations.city.translations:city_id,name', 'locations.division.translations:division_id,name', 'locations.country.translations:country_id,name', 'loveReactant.reactions.reacter.reacterable', // 'loveReactant.reactions.type', 'loveReactant.reactionCounters', // 'loveReactant.reactionTotal', ]) ->find($id); if (!$bank) { return view('profile.not_found'); } $states = $this->getActiveStates($bank); $canManageProfiles = $this->getCanManageProfiles(); $canViewIncompleteProfiles = $this->canViewIncompleteProfiles(); $isViewingOwnProfile = getActiveProfile() && get_class(getActiveProfile()) === Bank::class && getActiveProfile()->id === $bank->id; // For admins/banks: Always show incomplete label if profile is incomplete (ignore config) if ($canManageProfiles) { $states['hidden'] = false; $states['inactiveLabel'] = !$bank->isActive(); $states['inactiveSince'] = $bank->inactive_at ? \Illuminate\Support\Carbon::parse($bank->inactive_at)->diffForHumans() : ''; $states['emailUnverifiedLabel'] = !$bank->isEmailVerified(); $states['incompleteLabel'] = $bank->hasIncompleteProfile($bank); } // When viewing own profile, never hide but show all labels if ($isViewingOwnProfile) { $states['hidden'] = false; $states['inactiveLabel'] = !$bank->isActive(); $states['inactiveSince'] = $bank->inactive_at ? \Illuminate\Support\Carbon::parse($bank->inactive_at)->diffForHumans() : ''; $states['emailUnverifiedLabel'] = !$bank->isEmailVerified(); $states['incompleteLabel'] = $bank->hasIncompleteProfile($bank); // Show incomplete warning modal when viewing own profile $states['showIncompleteWarning'] = timebank_config('profile_incomplete.show_warning_modal') && $bank->hasIncompleteProfile($bank); } // Check if profile should be hidden if ($states['hidden'] && !$canManageProfiles && !$isViewingOwnProfile) { // Only allow Admin and Bank profiles to view incomplete-only profiles // Inactive profiles should be hidden from everyone except admins/banks $isIncompleteOnly = ($states['isIncomplete'] ?? false) && !($states['inactive'] ?? false) && !($states['emailUnverifiedLabel'] ?? false); // Show profile only if it's incomplete-only AND viewer is admin/bank if (!($isIncompleteOnly && $canViewIncompleteProfiles)) { return view('profile.not_found'); } } $bank->reactionCounter = $bank->loveReactant->reactionCounters->first() ? (int)$bank->loveReactant->reactionCounters->first()->weight : null; $profile = $bank; $header = __('Bank profile'); return $profile != null ? view('profile.show', array_merge(compact('profile', 'header'), $states)) : abort(403); } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit() { $type = strtolower(class_basename(getActiveProfile())); $profile = getActiveProfile(); // Check if profile is incomplete - show warning if config allows $showIncompleteWarning = false; if (timebank_config('profile_incomplete.show_warning_modal') && method_exists($profile, 'hasIncompleteProfile') && $profile->hasIncompleteProfile($profile)) { $showIncompleteWarning = true; } return view('profile-' . $type . '.edit', compact('showIncompleteWarning')); } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { // } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { // } /** * Redirects the authenticated user to their profile settings page with locale-specific routing. * * This method is used by the route profile.settings.no_locale. * The email footer template uses this route because when sending the locale of the * recipient is unknown. * * @return \Illuminate\Http\RedirectResponse Redirects to the localized profile settings page. */ public function settingsNoLocale() { $user = auth()->user(); $locale = $user->lang_preference ?? config('app.fallback_locale'); // Load the route translations for the specific locale $routeTranslations = include resource_path("lang/{$locale}/routes.php"); $translatedRoute = $routeTranslations['profile.settings'] ?? 'profile/settings'; $localizedUrl = url("/{$locale}/{$translatedRoute}"); return redirect($localizedUrl . '#message_settings'); } /** * Show the user settings screen. * * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ public function settings(Request $request) { $type = strtolower(class_basename(getActiveProfile())); if ($type === 'user') { return view('profile.settings', [ 'request' => $request, 'user' => $request->user(), ]); } else { return view('profile-' . $type . '.settings'); } } public function manage() { return view('profiles.manage'); } /** * Determines the inactive status flags for a given profile. * * @param mixed $profile The profile object to check for inactivity. * @return array An associative array containing: * - 'inactive' (bool): Whether the profile is inactive. * - 'hidden' (bool): Whether the profile should be hidden. * - 'inactiveLabel' (bool): Whether the profile should be labeled as inactive. * - 'inactiveSince' (string): Human-readable duration since the profile became inactive. */ private function getActiveStates($profile) { $inactive = false; $hidden = false; $inactiveLabel = false; $inactiveSince = ''; $emailUnverifiedLabel = false; $isIncomplete = false; $incompleteLabel = false; $noExchangesYetLabel = false; $removedSince = ''; if (method_exists($profile, 'isActive') && !$profile->isActive()) { $inactive = true; if (timebank_config('profile_inactive.profile_hidden')) { $hidden = true; } if (timebank_config('profile_inactive.profile_labeled')) { $inactiveLabel = true; $inactiveSince = $profile->inactive_at ? \Illuminate\Support\Carbon::parse($profile->inactive_at)->diffForHumans() : ''; } } if (method_exists($profile, 'isEmailVerified') && !$profile->isEmailVerified()) { $emailUnverifiedLabel = false; if (timebank_config('profile_email_unverified.profile_hidden')) { $hidden = true; } if (timebank_config('profile_email_unverified.profile_labeled')) { $emailUnverifiedLabel = true; } } if (method_exists($profile, 'hasIncompleteProfile') && $profile->hasIncompleteProfile($profile)) { $isIncomplete = true; if (timebank_config('profile_incomplete.profile_hidden')) { $hidden = true; } if (timebank_config('profile_incomplete.profile_labeled')) { $incompleteLabel = true; } } if ( timebank_config('profile_incomplete.no_exchanges_yet_label') && method_exists($profile, 'hasNeverReceivedTransaction') && $profile->hasNeverReceivedTransaction($profile) ) { $noExchangesYetLabel = true; } if ( (method_exists($profile, 'isRemoved') && $profile->isRemoved()) || (!empty($profile->deleted_at) && \Carbon\Carbon::parse($profile->deleted_at)->isPast()) ) { $hidden = true; $removedSince = !empty($profile->deleted_at) ? \Carbon\Carbon::parse($profile->deleted_at)->diffForHumans() : ''; } return compact('inactive', 'hidden', 'inactiveLabel', 'inactiveSince', 'emailUnverifiedLabel', 'isIncomplete', 'incompleteLabel', 'noExchangesYetLabel', 'removedSince'); } }