render(
$this->viewData()
);
return $view ?? ob_get_clean();
}
public static function register()
{
parent::register();
// Add wildcard customer profile pages
// add a custom permalink endpoint
add_action('init', function () {
$pageSlug = (new StoreSettings())->getCustomerDashboardPageSlug();
$customerProfilePageId = (new StoreSettings())->getCustomerProfilePageId();
static::$slug = $pageSlug;
if($customerProfilePageId && $pageSlug) {
add_rewrite_rule(
'^'.$pageSlug.'/(.+)?$',
'index.php?page_id='.$customerProfilePageId,
'top'
);
}
});
}
public function render(?array $viewData = null)
{
if (!is_user_logged_in()) {
ob_start();
$redirectUrl = $this->resolveLoginRedirectUrl(
(new StoreSettings())->getCustomerProfilePage()
);
if (defined('FLUENT_AUTH_VERSION') && (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->isEnabled()) {
?>
';
} else {
?>
renderCustomerAppContainer();
}
/**
* Resolve where a logged-out visitor should land once they have logged in.
*
* `redirect_to` is attacker-supplied, so it is only honoured when
* wp_validate_redirect() accepts it. That compares the parsed host against the
* site host. A string-prefix comparison must not be used here: a hostile host
* can be built by suffixing the site host, or by placing the site host in the
* userinfo position ahead of an `@`, and both keep the site URL as a prefix
* while resolving somewhere else entirely.
*
* @param string $fallbackUrl Where to send the visitor when no usable target was supplied.
* @return string
*/
public function resolveLoginRedirectUrl($fallbackUrl)
{
if (empty($_GET['redirect_to']) || !is_string($_GET['redirect_to'])) {
return $fallbackUrl;
}
$intendedRedirectUrl = sanitize_url(wp_unslash($_GET['redirect_to']));
$validatedUrl = wp_validate_redirect($intendedRedirectUrl, '');
return $validatedUrl ? $validatedUrl : $fallbackUrl;
}
public function renderCustomerAppContainer()
{
// Enqueue global styles
Vite::enqueueStyle( 'fluent-cart-customer-profile-global',
'public/customer-profile/style/customer-profile-global.scss',
);
$customEndpointContent = $this->maybeCustomEndpointContent();
if(!$customEndpointContent) {
(new static())->enqueueStyles();
}
$colors = self::generateCssColorVariables(Arr::get($this->shortCodeAttributes, 'colors', ''));
add_action('fluent_cart/customer_menu', array($this, 'renderCustomerMenu'));
add_action('fluent_cart/customer_app', function () use ($customEndpointContent) {
if($customEndpointContent) {
echo $customEndpointContent; // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
AssetLoader::loadCustomerDashboardAssets();
$this->renderCustomerApp();
}
});
App::make('view')->render('frontend.customer_app', [
'wp_page_title' => get_the_title(),
'colors' => $colors,
'active_tab' => apply_filters('fluent_cart/customer_portal/active_tab', ''),
]);
}
public function maybeCustomEndpointContent() {
global $wp;
$requestedPath = $wp->request;
// remove static::$slug from the requested path
if (static::$slug && Str::startsWith($requestedPath, static::$slug)) {
$requestedPath = Str::replaceFirst(static::$slug, '', $requestedPath);
$requestedPath = trim($requestedPath, '/');
}
if($requestedPath) {
$paths = explode('/', $requestedPath);
$requestedPath = array_shift($paths);
}
$reserved = ['dashboard', 'purchase-history', 'subscriptions', 'licenses', 'downloads', 'profile'];
if(!$requestedPath || in_array($requestedPath, $reserved)) {
return ''; // No specific path requested, return early
}
// Maybe it's a custom endpoint path
$customEndpoints = apply_filters('fluent_cart/customer_portal/custom_endpoints', []);
if(empty($customEndpoints) || !isset($customEndpoints[$requestedPath])) {
return ''; // No custom endpoints defined, return early
}
ob_start();
$endpoint = $customEndpoints[$requestedPath];
add_filter('fluent_cart/customer_portal/active_tab', function ($activeTab) use ($requestedPath) {
return $requestedPath;
});
if (isset($endpoint['render_callback']) && is_callable($endpoint['render_callback'])) {
call_user_func($endpoint['render_callback']);
return ob_get_clean();
}
if(isset($endpoint['page_id'])) {
$pageId = (int) $endpoint['page_id'];
// Create a custom query to fetch the two pages
$args = array(
'post_type' => 'page',
'post__in' => [$pageId],
'posts_per_page' => 1,
'orderby' => 'post__in', // Preserve the order of the IDs
);
$page_query = new \WP_Query($args);
// Check if the query has posts
if ($page_query->have_posts()) :
while ($page_query->have_posts()) : $page_query->the_post();
?>
' . esc_html__('No content found!', 'fluent-cart') . '';
endif;
}
return ob_get_clean();
}
public function renderCustomerApp()
{
echo '