. * */ namespace Vvveb\Controller; use function Vvveb\__; use function Vvveb\arrayInsertArrayAfter; use function Vvveb\availableCurrencies; use function Vvveb\availableLanguages; use function Vvveb\clearLanguageCache; use function Vvveb\filter; use function Vvveb\setLanguage; use Vvveb\Sql\taxonomySQL; use Vvveb\System\Cache; use Vvveb\System\Core\FrontController; use Vvveb\System\Core\Request; use Vvveb\System\Core\View; use Vvveb\System\Event; use Vvveb\System\Extensions\Plugins; use Vvveb\System\Functions\Str; use Vvveb\System\Images; use Vvveb\System\PageCache; use Vvveb\System\Session; use Vvveb\System\Sites; use Vvveb\System\Traits\Permission; use Vvveb\System\User\Admin; #[\AllowDynamicProperties] class Base { use Permission; public $view; public $request; public $sesssion; protected $global; protected function setSite($site_id = false) { //if no id set default if ($site_id) { $site = Sites::getSiteById($site_id); } if (! $site_id || ! $site) { $site = Sites::getDefault(); } $site_id = $site['id']; $this->session->set('site', $site); $this->session->set('site_id', $site_id); $this->session->set('site_url', $site['url']); $this->session->set('site', $site['id']); $this->session->set('host', $site['host']); $this->session->set('state', $site['state'] ?? 'live'); return $site_id; } protected function getTaxonomies() { $categories = new taxonomySQL(); $taxonomies = $categories->getAll($this->global); return $taxonomies['taxonomy'] ?? []; } protected function customPost() { //custom posts -- add to menu $default_custom_posts = [ 'post' => [ 'type' => 'post', 'plural' => 'posts', 'icon' => 'icon-document-text-outline', 'comments'=> true, ], 'page' => [ 'type' => 'page', 'plural' => 'pages', 'icon' => 'icon-document-outline', 'comments'=> false, ], ]; $custom_posts_types = \Vvveb\getSetting('post', 'types', []); $custom_posts_types += $default_custom_posts; list($custom_posts_types) = Event::trigger('Vvveb\postTypes', __FUNCTION__, $custom_posts_types); $custom_post_menu = \Vvveb\config('custom-post-menu', []); $posts_menu = []; foreach ($custom_posts_types as $type => $settings) { if ($type == 'page') { continue; } $posts_menu[$type] = $custom_post_menu; $posts_menu[$type]['name'] = $posts_menu[$type]['items']['posts']['name'] = __(ucfirst($settings['plural'])); $posts_menu[$type]['icon'] = $settings['icon'] ?? ''; $posts_menu[$type]['icon-img'] = (isset($settings['icon-img']) && $settings['icon-img']) ? Images::image($settings['icon-img'], $type) : ''; $posts_menu[$type]['url'] .= "&type=$type"; if (isset($settings['comments']) && ! $settings['comments']) { unset($posts_menu[$type]['items']['comments']); } foreach ($posts_menu[$type]['items'] as $item => &$values) { if (isset($values['url'])) { $values['url'] .= "&type=$type"; } } $admin_path = \Vvveb\adminPath(); //add taxonomies for post type foreach ($this->taxonomies as $taxonomy) { if ($taxonomy['post_type'] != $type) { continue; } $key = $taxonomy['post_type'] . '_' . $taxonomy['type'] . $taxonomy['taxonomy_id']; $module = "content/{$taxonomy['type']}"; $icon = $taxonomy['type'] == 'tags' ? 'la la-tags' : 'la la-boxes'; $tax = [$key => [ 'name' => __($taxonomy['name']), //'subtitle' => __('(Flat)'), 'url' => "{$admin_path}index.php?module=$module&type={$taxonomy['post_type']}&taxonomy_id={$taxonomy['taxonomy_id']}", 'module' => $module, 'action' => 'index', 'icon' => $icon, ]]; $posts_menu[$type]['items'] = arrayInsertArrayAfter('taxonomy-heading', $posts_menu[$type]['items'], $tax); } } return $posts_menu; } protected function customProduct() { //custom products -- add to menu $default_custom_products = [ 'product' => [ 'type' => 'product', 'plural' => 'products', 'icon' => 'icon-cube-outline', ], ]; $custom_products_types = \Vvveb\getSetting('product', 'types', []); $custom_products_types += $default_custom_products; list($custom_products_types) = Event::trigger('Vvveb\postTypes', __FUNCTION__, $custom_products_types); $custom_product_menu = \Vvveb\config('custom-product-menu', []); $products_menu = []; foreach ($custom_products_types as $type => $settings) { if ($type == 'page') { continue; } $products_menu[$type] = $custom_product_menu; $products_menu[$type]['name'] = $products_menu[$type]['items']['products']['name'] = __(ucfirst($settings['plural'])); $products_menu[$type]['icon'] = $settings['icon'] ?? ''; $products_menu[$type]['icon-img'] = (isset($settings['icon-img']) && $settings['icon-img']) ? Images::image($settings['icon-img'], $type) : ''; $products_menu[$type]['url'] .= "&type=$type"; foreach ($products_menu[$type]['items'] as $item => &$values) { if (isset($values['url'])) { $values['url'] .= "&type=$type"; } } $admin_path = \Vvveb\adminPath(); //add taxonomies for post type foreach ($this->taxonomies as $taxonomy) { if ($taxonomy['post_type'] != $type) { continue; } $key = $taxonomy['post_type'] . '_' . $taxonomy['type'] . $taxonomy['taxonomy_id']; $module = "content/{$taxonomy['type']}"; $icon = $taxonomy['type'] == 'tags' ? 'la la-tags' : 'la la-boxes'; $tax = [$key => [ 'name' => __($taxonomy['name']), //'subtitle' => __('(Flat)'), 'url' => "{$admin_path}index.php?module=$module&type={$taxonomy['post_type']}&taxonomy_id={$taxonomy['taxonomy_id']}", 'module' => $module, 'action' => 'index', 'icon' => $icon, ]]; $products_menu[$type]['items'] = arrayInsertArrayAfter('taxonomy-heading', $products_menu[$type]['items'], $tax); } } return $products_menu; } protected function language($defaultLanguage = false, $defaultLanguageId = false, $defaultLocale = false) { $languages = availableLanguages(); $default_language = $this->session->get('default_language'); $default_language_id = $this->session->get('default_language_id'); $default_locale = $this->session->get('default_locale'); $site_language = false; if (($lang = ($this->request->post['language'] ?? false)) && ! is_array($lang)) { $language = filter('/[A-Za-z_-]+/', $lang, 10); if (isset($languages[$language])) { $this->session->set('language', $language); $this->session->set('language_id', $languages[$language]['language_id']); $this->session->set('locale', $languages[$language]['locale']); $this->session->set('rtl', $languages[$language]['rtl'] ?? false); $default_language = false; //recheck default language clearLanguageCache($language); } } if (! $default_language) { foreach ($languages as $code => $lang) { //set site language if ($defaultLanguageId && ($defaultLanguageId == $lang['language_id'])) { $site_language = $code; $site_language_id = $lang['language_id']; $site_locale = $lang['locale']; } //set global default language if ($lang['default']) { $default_language = $code; $default_language_id = $lang['language_id']; $default_locale = $lang['locale']; break; } } if ($site_language) { $default_language = $site_language; $default_language_id = $site_language_id; $default_locale = $site_locale; } //no valid default site or global language? set english as default if (! $default_language) { $default_language = 'en_US'; $default_language_id = 1; $default_locale = 'en-us'; } $this->session->set('default_language', $default_language); $this->session->set('default_language_id', $default_language_id); $this->session->set('default_locale', $default_locale); } $language = $this->session->get('language') ?? $default_language; $language_id = $this->session->get('language_id') ?? $default_language_id; $locale = $this->session->get('locale') ?? $default_locale; $rtl = $this->session->get('rtl') ?? false; //if no default language configured then set first language as current language if (! isset($languages[$language])) { $default_language = key($languages); $lang = $languages[$default_language] ?? []; if ($lang) { $default_language_id = $lang['language_id'] ?? $defaultLanguageId; $default_locale = $lang['locale'] ?? $defaultLocale; $default_rtl = $lang['rtl'] ?? false; } } //if no language configured then set default language as current language if (! $language) { $language = $default_language; $language_id = $default_language_id; $locale = $default_locale; $rtl = $default_rtl; $this->session->set('language', $language); $this->session->set('language_id', $language_id); $this->session->set('locale', $locale); $this->session->set('rtl', $rtl); } $this->global['language'] = $language; $this->global['locale'] = $locale; $this->global['language_id'] = $language_id; $this->global['rtl'] = $rtl; $this->global['default_language'] = $default_language; $this->global['default_locale'] = $default_locale; $this->global['default_language_id'] = $default_language_id; setLanguage($language); if (! defined('CLI')) { $view = $this->view; $view->languagesList = $languages; } } protected function currency($defaultCurrency = false, $defaultCurrencyId = false) { if (($currency = ($this->request->post['currency'] ?? false)) && ! is_array($currency)) { $currency = filter('/[A-Za-z_-]+/', $currency, 50); $currencies = availableCurrencies(); if (isset($currencies[$currency])) { $this->session->set('currency_id', $currencies[$currency]['currency_id']); $this->session->set('currency', $currency); \Vvveb\System\Cart\Cart::getInstance($this->global)->updateCart(); } } $currency = $this->session->get('currency'); $currency_id = $this->session->get('currency_id'); if (! $currency || ! $currency_id) { $currencies = availableCurrencies(); if ($currencies) { foreach ($currencies as $code => $c) { if ($defaultCurrency && ($defaultCurrency == $c['code']) || ($defaultCurrencyId && ($defaultCurrencyId == $c['currency_id']))) { $currency = $c['code']; $currency_id = $c['currency_id']; break; } } //if no site currency configured set first available currency if (! $currency) { $c = reset($currencies); $currency = $c['code']; $currency_id = $c['currency_id']; } } $this->session->set('currency', $currency); $this->session->set('currency_id', $currency_id); } $this->global['currency'] = $currency; $this->global['currency_id'] = $currency_id; } function init() { //$this->session->delete('csrf'); //$this->session = Session::getInstance(); //$this->request = Request::getInstance(); $view = View :: getInstance(); $view->removeVattrs(false); if (isset($this->request->get['errors']) && $this->request->get['errors']) { $view->errors['get'] = htmlspecialchars($this->request->get['errors']); } if (isset($this->request->get['success']) && $this->request->get['success']) { $view->success['get'] = htmlspecialchars($this->request->get['success']); } //prevent admin loading in iframe $this->response->addHeader('X-Frame-Options', 'SAMEORIGIN'); $this->response->addHeader('X-Content-Type-Options', 'nosniff'); if (! $this->session->get('csrf')) { $this->session->set('csrf', Str::random()); } $admin = Admin::current(); if (! $admin) { return $this->requireLogin(); } $this->checkCsrf(); if (($site_id = ($this->request->post['site'] ?? false)) && is_numeric($site_id)) { $this->setSite($site_id); } $site_id = $this->session->get('site_id'); if (! $site_id) { $site_id = $this->setSite(); } $this->language(); $this->currency(); $adminPath = \Vvveb\adminPath(); //change site status (live, under maintenance etc) if ($state = ($this->request->post['state'] ?? false)) { if (Admin::hasPermission('settings/site/save')) { if (Sites::setSiteDataById($site_id, 'state', $state)) { $this->session->set('state', $state); PageCache::getInstance()->purge(); } } else { $message = __('Your role does not have permission to access this action!'); $this->view->errors[] = $message; $this->view->adminPath = $adminPath; } } $page = $this->request->get['page'] ?? 1; $limit = $this->request->get['limit'] ?? 10; $this->global['site_id'] = $site_id; $this->global['host'] = $this->session->get('host'); $this->global['site_url'] = $this->session->get('site_url'); $this->global['admin_id'] = $admin['admin_id']; $this->global['state'] = $state; $this->global['page'] = $page; $this->global['start'] = ($page - 1) * $limit; $this->global['limit'] = $limit; //Check permissions $className = get_class($this); if ($className != 'Vvveb\Controller\Error403') { $this->permission(); $this->setPermissions(); } //load plugins for active site if safe mode is not selected if (! isset($admin['safemode']) || ! $admin['safemode']) { Plugins :: loadPlugins($site_id); } if ($errors = $this->session->get('errors')) { if (is_array($errors)) { $view->errors = ($view->errors ?? []) + $errors; } else { $view->errors['session'] = $errors; } $this->session->delete('errors'); } if ($success = $this->session->get('success')) { if (is_array($success)) { $view->success = ($view->success ?? []) + $success; } else { $view->success['session'] = $success; } $this->session->delete('success'); } //don't initialize menu items for CLI if (defined('CLI')) { return; } $cacheDriver = Cache :: getInstance(); $cacheKey = $this->global['admin_id'] . '.' . $this->global['site_id'] . '.' . $this->global['language_id']; if ($menu = $cacheDriver->get('admin-menu', $cacheKey)) { } else { $menu = \Vvveb\config('admin-menu', []); //custom posts -- add to menu $this->taxonomies = $this->getTaxonomies(); $posts_menu = $this->customPost(); $menu = arrayInsertArrayAfter('edit', $menu, $posts_menu); //products - add to menu $products_menu = $this->customProduct(); $menu = arrayInsertArrayAfter('sales', $menu, $products_menu); list($menu) = Event::trigger(__CLASS__, __FUNCTION__ . '-menu', $menu); $urls = []; $this->getPermissionsFromUrl($menu, $urls); $permissions = Admin::hasPermission($urls); //$urls = array_map(fn ($value) => $value ? ($permissions[$value] ?? false) : false, $urls); $urls = array_map(function ($value) use ($permissions) { return $value ? ($permissions[$value] ?? false) : false; }, $urls); $this->setPermissionsFromUrl($menu, $urls); $cacheDriver->set('admin-menu', $cacheKey, $menu); } $view->menu = $menu; $view->global = $this->global; //send to view for button visibillity check $this->view->actionPermissions = $this->actionPermissions ?? []; $this->view->modulePermissions = $this->modulePermissions ?? []; $view->adminPath = $adminPath; $view->mediaPath = PUBLIC_PATH . 'media'; $view->publicPath = PUBLIC_PATH . 'media'; } protected function redirect($url = '/', $parameters = [], $stop = true) { $redirect = \Vvveb\url($url, $parameters); if ($redirect) { $url = $redirect; } header("Location: $url"); if ($stop) { $this->session->close(); FrontController::closeConnections(); PageCache::getInstance()->cleanUp(); die(0); } } /** * Call this method if the action requires login, if the user is not logged in, a login form will be shown. * If Keycloak auto-login is enabled, redirects to Keycloak instead of showing login form. * */ protected function requireLogin() { // Check if Keycloak auto-login is enabled $keycloakAutoLogin = \Vvveb\config('keycloak.auto_login', false); // Check if user explicitly wants classic login (via ?classic_login=1 parameter) $forceClassicLogin = isset($this->request->get['classic_login']) && $this->request->get['classic_login'] == '1'; // If Keycloak auto-login is enabled and user doesn't force classic login, redirect to Keycloak if ($keycloakAutoLogin && !$forceClassicLogin) { $adminPath = \Vvveb\adminPath(); $redirectUrl = $adminPath . 'index.php?module=keycloak/login'; // Preserve the original URL for redirect after login $originalUrl = $_SERVER['REQUEST_URI'] ?? ''; if ($originalUrl && strpos($originalUrl, 'module=keycloak') === false) { $redirectUrl .= '&redirect=' . urlencode($originalUrl); } header('Location: ' . $redirectUrl); exit; } // Otherwise, show classic login form //return \Vvveb\System\Core\FrontController::redirect('user/login'); //$view = view :: getInstance(); $admin_path = \Vvveb\adminPath(); $this->view->redir = $_SERVER['REQUEST_URI'] ?? ''; $this->view->adminPath = $admin_path; $this->view->action = "{$admin_path}index.php?module=user/login"; $this->view->template('user/login.html'); $this->view->render(); die(0); } /** * Call this method on form posts to protect against csrf attacks. * */ protected function checkCsrf() { if (! defined('CLI') && (($method = $this->request->getMethod()) == 'post') && (! ($csrf = $this->session->get('csrf')) || ! ($postCsrf = ($this->request->post['csrf'] ?? false)) || ($csrf != $postCsrf))) { $this->notFound('Invalid csrf!', 403); } } /** * Shows a "Not found", "Internal server error" or "Permission denied" page. * * @param unknown_type $code * @param mixed $statusCode * @param mixed $service * @param mixed $message */ protected function notFound($message = false, $statusCode = 404, $service = false) { return FrontController::notFound($service, $message, $statusCode); } /** * Generates the documentation link for current page. * * @param unknown_type $code * @param null|mixed $module * @param null|mixed $action */ protected function getDocUrlForPage($module = null, $action = null) { $module = $module ?? $this->request->get['module'] ?? ''; $action = $action ?? $this->request->get['origaction'] ?? ''; $type = $type ?? $this->request->get['type'] ?? ''; $action = $action ? '/' . $action : ''; $url = 'https://docs.vvveb.com/'; if ($type == 'post' || $type == 'page'/* || $type == 'product'*/) { $type = $type ? '/' . $type : ''; } else { $type = ''; } $documentionList = include DIR_SYSTEM . 'data/documentation-map.php'; if (isset($documentionList[$module . $action . $type])) { $url .= $documentionList[$module . $action . $type]; } else { $url .= str_replace('/', '-', $module . $action . $type); } return $url; } function goToHelp() { $url = $this->getDocUrlForPage(); return header("Location: $url"); } }