'page_title.welcome', 'dashboard' => 'page_title.dashboard', 'search' => 'page_title.search', 'search.results' => 'page_title.search', 'transactions.index' => 'page_title.transactions', 'transactions.show' => 'page_title.transactions', 'profile.show' => 'page_title.profile', 'profile.edit' => 'page_title.profile', 'profile.settings' => 'page_title.settings', 'user-profile-information.update' => 'page_title.settings', 'login' => 'page_title.login', 'register' => 'page_title.register', 'post.index' => 'page_title.posts', 'post.show' => 'page_title.posts', 'admin.index' => 'page_title.admin', 'contacts' => 'Contacts', 'static-faq' => 'FAQ', 'static-getting-started' => 'Getting started', 'static-privacy' => 'Privacy', 'static-organizations' => 'Organizations', 'static-principles' => 'Principles', 'static-report-issue' => 'Report an issue', 'static-events' => 'Events', 'static-messenger' => 'Messages', 'static-report-error' => 'Report an error', ]; // Get the translation key for this route, or default to welcome $translationKey = $routeMap[$routeName] ?? 'page_title.welcome'; return __($translationKey); } /** * Sanitize HTML content to prevent XSS attacks while preserving rich text formatting. * Allows safe HTML tags like paragraphs, headings, links, images, lists, etc. * * IMPORTANT: This method uses Laravel's cache directory for HTMLPurifier cache, * avoiding permission issues with the vendor directory on production servers. * * @param string|null $html * @return string */ public static function sanitizeHtml(?string $html): string { if (empty($html)) { return ''; } // Create HTMLPurifier configuration $config = \HTMLPurifier_Config::createDefault(); // Use Laravel's cache directory instead of vendor directory // This avoids "Directory not writable" errors on production servers $cacheDir = storage_path('framework/cache/htmlpurifier'); // Create cache directory if it doesn't exist if (!is_dir($cacheDir)) { mkdir($cacheDir, 0755, true); } $config->set('Cache.SerializerPath', $cacheDir); // Allow target="_blank" for links - must be set before getHTMLDefinition() $config->set('Attr.AllowedFrameTargets', ['_blank']); // Enable HTML5 mode and allow data-* attributes $config->set('HTML.DefinitionID', 'html5-definitions'); $config->set('HTML.DefinitionRev', 1); // Allow rich text formatting elements including data-list attribute $config->set('HTML.Allowed', 'p,br,strong,b,em,i,u,strike,del,ins,' . 'h1,h2,h3,h4,h5,h6,' . 'ul,ol,li[data-list],' . 'a[href|target|title|rel],' . 'img[src|alt|width|height|title],' . 'blockquote,pre,code,' . 'table,thead,tbody,tr,th,td,' . 'span[class|contenteditable],div[class]' ); // Get HTML definition and add custom data-list attribute // Use maybeGetRawHTMLDefinition to avoid caching warnings if ($def = $config->maybeGetRawHTMLDefinition()) { // Add data-list as an enumerated attribute with specific allowed values $def->addAttribute('li', 'data-list', new \HTMLPurifier_AttrDef_Enum( array('bullet', 'ordered') )); } // Create purifier and clean the HTML $purifier = new \HTMLPurifier($config); return $purifier->purify($html); } } /** * Get the translated page title for the current route. * * @param string|null $routeName * @return string */ if (!function_exists('page_title')) { function page_title(?string $routeName = null): string { return \App\Helpers\StringHelper::getPageTitle($routeName); } }