<?php
namespace SabaiApps\Directories\Component\Dashboard;

use SabaiApps\Directories\Component\AbstractComponent;
use SabaiApps\Directories\Component\System;
use SabaiApps\Directories\Component\View;
use SabaiApps\Directories\Component\Entity;
use SabaiApps\Directories\Component\Display;
use SabaiApps\Directories\Context;
use SabaiApps\Directories\Application;
use SabaiApps\Directories\Request;
use SabaiApps\Framework\User\AbstractIdentity;

class DashboardComponent extends AbstractComponent implements
	System\IMainRouter,
	System\ISlugs,
	View\IModes,
	Display\IButtons,
	IPanels {

	const VERSION = '2.0.6', PACKAGE = 'directories-pro';

	public static function description() {
		return 'Adds a frontend dashboard to your site where users can manage their own content items.';
	}

	public static function interfaces() {
		return array(
			'Notification\IEvents',
		);
	}

	protected function _init() {
		// Compat with <v1.4.0-dev.68
		if ( isset( $this->_config['panels'] )
			&& ! isset( $this->_config['panel'] )
		) {
			$this->_config['panel'] = array( 'panels' => $this->_config['panels'] );
		}
	}

	public function systemSlugs() {
		return array(
			'dashboard' => array(
				'admin_title'  => __( 'Frontend Dashboard', 'directories-pro' ),
				'title'        => __( 'Dashboard', 'directories-pro' ),
				'wp_shortcode' => 'drts-dashboard',
			),
		);
	}

	protected function _sortPanels( array $panels ) {
		if ( ! empty( $this->_config['panel']['panels']['default'] ) ) {
			$new_panels = array();
			foreach ( $this->_config['panel']['panels']['default'] as $panel_name ) {
				if ( ! isset( $panels[ $panel_name ] ) ) {
					continue;
				}

				$new_panels[ $panel_name ] = $panels[ $panel_name ];
			}
			$panels = $new_panels;
		}
		return $panels;
	}

	public function systemMainRoutes( $lang = null ) {
		$base   = '/' . $this->getSlug( 'dashboard', $lang );
		$routes = array(
			$base                                      => array(
				'controller'      => 'Panel',
				'access_callback' => true,
				'title_callback'  => true,
				'callback_path'   => 'dashboard',
				'priority'        => 3,
			),
			$base . '/request_guest_access'            => array(
				'controller'      => 'RequestGuestAccess',
				'callback_path'   => 'request_guest_access',
				'access_callback' => true,
				'title_callback'  => true,
				'priority'        => 3,
			),
			$base . '/:user_name'                      => array(
				'controller'      => 'Panel',
				'access_callback' => true,
				'title_callback'  => true,
				'callback_path'   => 'user',
				'priority'        => 4,
			),
			$base . '/:user_name/:panel_name'          => array(
				'controller'      => 'Panel',
				'access_callback' => true,
				'title_callback'  => true,
				'callback_path'   => 'panel',
				'priority'        => 4,
			),
			$base . '/:user_name/:panel_name/posts'    => array(
				'access_callback' => true,
				'callback_path'   => 'posts',
				'priority'        => 3,
			),
			$base . '/:user_name/:panel_name/posts/:entity_id' => array(
				'format'          => array( ':entity_id' => '\d+' ),
				'controller'      => 'EditPost',
				'access_callback' => true,
				'callback_path'   => 'edit_post',
				'priority'        => 3,
			),
			$base . '/:user_name/:panel_name/posts/:entity_id/delete' => array(
				'controller'      => 'DeletePost',
				'access_callback' => true,
				'callback_path'   => 'delete_post',
				'priority'        => 3,
			),
			$base . '/:user_name/:panel_name/posts/:entity_id/submit' => array(
				'controller'      => 'SubmitPost',
				'access_callback' => true,
				'callback_path'   => 'submit_post',
				'priority'        => 3,
			),
			$base . '/:user_name/:panel_name/posts/:entity_id/assign' => array(
				'controller'      => 'AssignPost',
				'access_callback' => true,
				'callback_path'   => 'assign_post',
				'priority'        => 3,
			),
			$base . '/:user_name/:panel_name/settings' => array(
				'controller'    => 'AccountSettings',
				'callback_path' => 'account_settings',
				'priority'      => 3,
			),
			$base . '/:user_name/:panel_name/settings/change_password' => array(
				'controller'    => 'ChangePassword',
				'callback_path' => 'change_password',
				'priority'      => 3,
			),
			$base . '/:user_name/:panel_name/settings/delete_account' => array(
				'controller'    => 'DeleteAccount',
				'callback_path' => 'delete_account',
				'priority'      => 3,
			),
			$base . '/:user_name/:panel_name/settings/:section' => array(
				'controller'      => 'AccountSettings',
				'callback_path'   => 'account_settings_section',
				'access_callback' => true,
				'priority'        => 10,
				'format'          => array( ':section' => '[a-z0-9_]+' ),
			),
		);

		return $routes;
	}

	public function systemOnAccessMainRoute( Context $context, $path, $accessType, array &$route ) {
		switch ( $path ) {
			case 'dashboard':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ! isset( $context->is_dashboard ) ) {
						$context->is_dashboard = true;
					}
					return true;
				}

				if ( $this->_application->getUser()->isAnonymous() ) {
					if ( ! $this->_application->Dashboard_GuestAccess_isGranted()
						|| $context->getRequest()->asBool( 'logout' )
					) {
						$this->_application->Dashboard_GuestAccess_revoke();
						$context->setUnauthorizedError( $route['path'] );
						return false;
					}
				}

				$panel = $link = null;
				foreach ( array_keys( $this->_sortPanels( $this->_application->Dashboard_Panels() ) ) as $panel_name ) {
					if ( ! $_panel = $this->_application->Dashboard_Panels_impl( $panel_name, true ) ) {
						continue;
					}

					// Get panel links
					$panel_settings  = $this->_config['panel']['panel_settings'][ $panel_name ] ?? array();
					$panel_settings += (array) $_panel->dashboardPanelInfo( 'default_settings' );
					if ( ! $links = $_panel->dashboardPanelLinks( $panel_settings ) ) {
						continue;
					}

					$panel = $panel_name;
					$link  = current( array_keys( $links ) );
					break;
				}
				$context->dashboard_panel      = $panel;
				$context->dashboard_panel_link = $link;
				return true;
			case 'user':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ! $user_name = $context->getRequest()->asStr( 'user_name' ) ) {
						$context->setNotFoundError();
						return false;
					}
					if ( $user_name === '_guest' ) {
						if ( $this->_application->getUser()->isAnonymous()
							&& $this->_application->Dashboard_GuestAccess_isEnabled()
						) {
							if ( ! $this->_application->Dashboard_GuestAccess_isGranted( false ) ) {
								$context->setUnauthorizedError( $route['path'] );
								return false;
							}
						} else {
							$context->setNotFoundError();
							return false;
						}
					} else {
						$identity = $this->_application->getPlatform()->getUserIdentityFetcher()->fetchByUsername( urldecode( $user_name ) );
						if ( ! $identity
							|| $identity->isAnonymous()
						) {
							$context->setNotFoundError();
							return false;
						}
						if ( empty( $this->_config['panel']['enable_public'] )
							|| ! $this->_isPublicDashboardAllowed()
						) {
							// Public dashboard not enabled or allowed, so return false if not viewing own profile
							if ( (int) $this->_application->getUser()->id !== (int) $identity->id ) {
								$context->setNotFoundError();
								return false;
							}
						}
						if ( $this->_application->getUser()->isAnonymous()
							|| (int) $this->_application->getUser()->id !== (int) $identity->id
						) {
							$context->dashboard_user = $identity;
						}
					}
				}
				$panel = $link = null;
				foreach ( array_keys( $this->_sortPanels( $this->_application->Dashboard_Panels() ) ) as $panel_name ) {
					if ( ! $_panel = $this->_application->Dashboard_Panels_impl( $panel_name, true ) ) {
						continue;
					}

					// Get panel links
					$panel_settings  = $this->_config['panel']['panel_settings'][ $panel_name ] ?? array();
					$panel_settings += (array) $_panel->dashboardPanelInfo( 'default_settings' );
					if ( ! $links = $_panel->dashboardPanelLinks( $panel_settings, isset( $context->dashboard_user ) ? $context->dashboard_user : null ) ) {
						continue;
					}

					$panel = $panel_name;
					$link  = current( array_keys( $links ) );
					break;
				}
				$context->dashboard_panel      = $panel;
				$context->dashboard_panel_link = $link;
				return true;
			case 'panel':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $panel_name = $context->getRequest()->asStr( 'panel_name' ) )
						|| ( ! $panel = $this->_application->Dashboard_Panels_impl( $panel_name, true ) )
					) {
						$context->setNotFoundError();
						return false;
					}

					// Get panel links
					$panel_settings  = $this->_config['panel']['panel_settings'][ $panel_name ] ?? array();
					$panel_settings += (array) $panel->dashboardPanelInfo( 'default_settings' );
					if ( ! $links = $panel->dashboardPanelLinks( $panel_settings, isset( $context->dashboard_user ) ? $context->dashboard_user : null ) ) {
						$context->setNotFoundError();
						return false;
					}

					if ( ( ! $link = $context->getRequest()->asStr( 'link' ) )
						|| ! isset( $links[ $link ] )
					) {
						$link = current( array_keys( $links ) );
					}
					$context->dashboard_panel      = $panel_name;
					$context->dashboard_panel_link = $link;
				}

				if ( $this->_application->getUser()->isAnonymous()
					&& ! isset( $context->dashboard_user )
				) {
					// $context->setUnauthorizedError($route['path']);
					// return false;
				}
				return true;
			case 'posts':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					$panel = $this->_application->Dashboard_Panels_impl( $context->dashboard_panel );
					if ( ( ! $panel instanceof Panel\PostsPanel )
						|| ( ! $bundle = $this->_application->Entity_Bundle( $context->dashboard_panel_link ) )
					) {
						$context->setNotFoundError();
						return false;
					}
					$context->bundle = $bundle;
				}
				return true;
			case 'guest_posts':
				$context->is_dashboard = false;
				return $this->_application->getUser()->isAnonymous();
			case 'edit_post':
			case 'edit_guest_post':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $entity = $this->_getRequestedEntity( $context ) )
						|| ( $path === 'edit_post' && $context->bundle->name !== $entity->getBundleName() )
					) {
						$context->setNotFoundError();
						return false;
					}
					$context->entity = $entity;
					return true;
				}
				return $this->_application->Entity_IsRoutable( $context->entity, 'edit', $context->entity );
			case 'delete_post':
			case 'delete_guest_post':
				return $this->_application->Entity_IsRoutable( $context->entity, 'delete', $context->entity );
			case 'submit_post':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					return true;
				}

				$context->action = 'submit';
				return $this->_application->Entity_IsRoutable( $context->bundle, $context->action, $context->entity );
			case 'assign_post':
				return $this->_application->Entity_IsRoutable( $context->entity, 'assign', $context->entity );
			case 'request_guest_access':
				if ( ! $this->_application->getUser()->isAnonymous()
					|| ! $this->_application->Dashboard_GuestAccess_isEnabled()
				) {
					return false;
				}

				if ( $this->_application->Dashboard_GuestAccess_validateRequest( $context->getRequest() ) ) {
					$context->setRedirect( '/' . $this->getSlug( 'dashboard' ) );
					return false;
				}
				$context->is_dashboard = false;
				return true;
			case 'account_settings_section':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ! $section = $context->getRequest()->asStr( 'section' ) ) {
						$context->setNotFoundError();
						return false;
					}
					$context->section = $section;
				}
				return true;
		}
	}

	protected function _getRequestedEntity( Context $context, $key = 'entity_id', $entityType = 'post' ) {
		if ( ( $entity_id = $context->getRequest()->asInt( $key ) )
			&& ( $entity = $this->_application->Entity_Entity( $entityType, $entity_id ) )
		) {
			return $entity;
		}
		return false;
	}

	public function systemMainRouteTitle( Context $context, $path, $titleType, array $route ) {
		switch ( $path ) {
			case 'dashboard':
				return $this->getTitle( 'dashboard' );
			case 'user':
			case 'panel':
				return isset( $context->dashboard_user ) ? $context->dashboard_user->name : $this->getTitle( 'dashboard' );
			case 'request_guest_access':
				return ucfirst( __( 'Request guest access', 'directories-pro' ) );
		}
	}

	public function onCoreResponseSendView( $context ) {
		if ( ! $context->is_dashboard
			|| $context->getRequest()->isAjax()
		) {
			return;
		}

		// Wrap content with dashboard template
		$context->dashboard_templates = ( $templates = $context->getTemplates() ) ? array_reverse( $templates ) : array();
		$context->clearTemplates()->addTemplate( 'dashboard_dashboard' );
		$context->dashboard_id         = 'drts-dashboard';
		$context->dashboard_menu_title = __( 'Dashboard menu', 'directories-pro' );
		$context->dashboard_main_title = '';
		$context->accordion            = ! empty( $this->_config['panel']['accordion'] );

		// Show logout button?
		if ( ! empty( $this->_config['panel']['logout_btn'] )
			&& ! $context->dashboard_user // make sure this is not another user's dashboard
		) {
			if ( ! $this->_application->getUser()->isAnonymous() ) {
				$logout_url = $this->_application->getPlatform()->getLogoutUrl();
			} else {
				$logout_url = $this->_application->Url( '/' . $this->getSlug( 'dashboard' ), array( 'logout' => 1 ) );
			}
			$context->logout_btn = '<a href="' . $logout_url . '" class="drts-dashboard-logout-button ' . DRTS_BS_PREFIX . 'btn ' . DRTS_BS_PREFIX . 'btn-outline-danger">'
				. '<i class="fa-fw fas fa-sign-out-alt"></i> ' . $this->_application->H( __( 'Logout', 'directories-pro' ) ) . '</a>';
		}

		// Get panels
		if ( $panels = $this->getActivePanels( isset( $context->dashboard_user ) ? $context->dashboard_user : null ) ) {
			// Add classes to current panel and link
			if ( $context->dashboard_panel
				&& $context->dashboard_panel_link
			) {
				$current_panel_name = $context->dashboard_panel;
				if ( isset( $panels[ $current_panel_name ] ) ) {
					$current_link_name                       = $context->dashboard_panel_link;
					$panels[ $current_panel_name ]['active'] = $panels[ $current_panel_name ]['links'][ $current_link_name ]['active'] = true;
					$panels[ $current_panel_name ]['links'][ $current_link_name ]['attr']['class'] .= ' ' . DRTS_BS_PREFIX . 'active';
					$context->dashboard_main_title = $panels[ $current_panel_name ]['links'][ $current_link_name ]['attr']['data-title']; // get title without html
				}
			}

			// Remove URL path from first link
			$panel_names = array_keys( $panels );
			$link_names  = array_keys( $panels[ $panel_names[0] ]['links'] );
			$panels[ $panel_names[0] ]['links'][ $link_names[0] ]['url'] = '/' . $this->getSlug( 'dashboard' );
		}
		$context->panels = $panels;

		// Load all form field JS/CSS files for dashboard panels
		$this->_application->Form_Scripts();

		$this->_application->Action( 'dashboard_panels_loaded', array( $panels ) );
	}

	public function viewGetModeNames() {
		return array( 'dashboard_dashboard' );
	}

	public function viewGetMode( $name ) {
		return new ViewMode\DashboardViewMode( $this->_application, $name );
	}

	public function displayGetButtonNames( Entity\Model\Bundle $bundle ) {
		$ret = array();
		if ( empty( $bundle->info['is_taxonomy'] )
			&& empty( $bundle->info['is_user'] )
		) {
			$ret[] = 'dashboard_posts_edit';
			$ret[] = 'dashboard_posts_delete';
			if ( ! empty( $bundle->info['public'] ) ) {
				$ret[] = 'dashboard_posts_submit';
			}
			$ret[] = 'dashboard_posts_assign';
		}
		return $ret;
	}

	public function displayGetButton( $name ) {
		return new DisplayButton\PostDisplayButton( $this->_application, $name );
	}

	public function onDirectoryAdminSettingsFormFilter( &$form ) {
		// Moved from Directory to System component
		$this->onSystemAdminSettingsFormFilter( $form );
	}

	public function onSystemAdminSettingsFormFilter( &$form ) {
		$form['tabs'][ $this->_name ]   = array(
			'#title'  => __( 'Dashboard', 'directories-pro' ),
			'#weight' => 15,
		);
		$form['fields'][ $this->_name ] = array(
			'#component' => $this->_name,
			'#tab'       => $this->_name,
		) + $this->_application->Dashboard_SettingsForm( $this->_config, $this->_isPublicDashboardAllowed(), array( $this->_name ) );
	}

	public function onDirectoryAdminDirectoryAdded( $directory, $values ) {
		$config = $this->_config;
		$config['panel']['panels'][ 'posts_' . $directory->name ] = null;
		$this->_application->System_Component_saveConfig( $this->_name, $config, false );
	}

	public function onViewQueryEntities( Context $context ) {
		// Abort if not viewing dashboard or no specific status requested
		if ( (string) $context->view !== 'dashboard_dashboard'
			|| ( ! $status = $context->getRequest()->asStr( 'status' ) )
		) {
			return;
		}

		$context->url_params['status'] = $status;
	}

	public function dashboardGetPanelNames() {
		$ret = array( 'account' );
		foreach ( $this->_application->Entity_Bundles() as $bundle ) {
			if ( ! $bundle->group
				|| $bundle->entitytype_name !== 'post'
				|| ! empty( $bundle->info['is_taxonomy'] )
				|| ! empty( $bundle->info['parent'] )
				|| ! empty( $bundle->info['parent_field'] )
				|| ! empty( $bundle->info['is_user'] )
				|| empty( $bundle->info['public'] )
			) {
				continue;
			}

			$ret[] = 'posts_' . $bundle->group;
		}
		foreach ( $this->_application->Filter( 'dashboard_panel_custom_names', array() ) as $custom_name ) {
			$ret[] = 'custom_' . $custom_name;
		}
		return $ret;
	}

	public function dashboardGetPanel( $name ) {
		switch ( $name ) {
			case 'account':
				return new Panel\AccountPanel( $this->_application, $name );
			default:
				if ( strpos( $name, 'posts_' ) === 0 ) {
					return new Panel\PostsPanel( $this->_application, $name );
				} elseif ( strpos( $name, 'custom_' ) === 0 ) {
					return new Panel\CustomPanel( $this->_application, $name );
				}
		}
	}

	public function onWordPressDoShortcodeFilter( &$ret, $shortcode, $component ) {
		if ( strpos( $shortcode, 'drts-dashboard' ) !== 0 ) {
			return;
		}

		$path          = '/' . $this->getSlug( 'dashboard' );
		$url           = (string) $this->_application->Url( $path );
		$url_requested = (string) Request::url( false );
		if ( strpos( $url_requested, $url ) === 0
			&& ( $_path = substr( $url_requested, strlen( $url ) ) )
			&& ( $_path = trim( $_path, '/' ) )
		) {
			$ret['path'] = array(
				'path'   => $path . '/' . $_path, // add dashboard panel path
				'params' => empty( $_REQUEST ) ? array() : $_REQUEST,
			);
			// Set user name as title if viewing other user's profile
			if ( ( $_paths = explode( '/', $_path ) )
				&& urldecode( $_paths[0] ) !== $this->_application->getUser()->username
				&& ( $identity = $this->_application->getPlatform()->getUserIdentityFetcher()->fetchByUsername( $_paths[0] ) )
				&& ( ! $identity->isAnonymous() )
			) {
				$ret['title'] = $identity->name;
			}
		}
	}

	/**
	 * @param AbstractIdentity|null $identity
	 * @param string|null           $language
	 * @return array
	 */
	public function getActivePanels( ?AbstractIdentity $identity = null, $language = null ) {
		$panels           = array();
		$panels_available = $this->_application->Dashboard_Panels();
		$panels_enabled   = isset( $this->_config['panel']['panels']['default'] ) ? $this->_config['panel']['panels']['default'] : array_keys( $panels_available );
		if ( ! isset( $language ) ) {
			$language = $this->_application->getPlatform()->getCurrentLanguage();
		}
		if ( ! empty( $panels_enabled ) ) {
			foreach ( $panels_enabled as $panel_name ) {
				if ( ! isset( $panels_available[ $panel_name ] )
					|| ( ! $panel = $this->_application->Dashboard_Panels_impl( $panel_name, true ) )
					|| ( isset( $identity ) && ! $panel->dashboardPanelInfo( 'public' ) )
					|| ( $this->_application->getUser()->isAnonymous() && ! $panel->dashboardPanelInfo( 'guest_allowed' ) )
				) {
					continue;
				}

				$panel_settings  = isset( $this->_config['panel']['panel_settings'][ $panel_name ] ) ? $this->_config['panel']['panel_settings'][ $panel_name ] : array();
				$panel_settings += (array) $panel->dashboardPanelInfo( 'default_settings' );
				if ( ! $links = $panel->panelHtmlLinks( $panel_settings, false, true, $identity ) ) {
					continue;
				}

				$panel->dashboardPanelOnLoad( $identity !== null );

				if ( ! empty( $panels_available[ $panel_name ]['labellable'] )
					&& isset( $this->_config['panel']['panels']['options'][ $panel_name ] )
				) {
					$panel_label = $this->_config['panel']['panels']['options'][ $panel_name ];
					if ( isset( $language ) ) {
						$panel_label = $this->_application->System_TranslateString( $panel_label, $panel_name . '_panel_label', 'dashboard_panel', $language );
					}
				} else {
					$panel_label = $panel->dashboardPanelLabel();
				}
				$panels[ $panel_name ] = array(
					'title'      => $panel_label,
					'links'      => $links,
					'wp_um_icon' => $panel->dashboardPanelInfo( 'wp_um_icon' ), // WP Ultimate Member profile tab icon
				);
			}
		}
		return $panels;
	}

	public function onCorePlatformWordPressInit() {
		if ( is_plugin_active( 'wp-2fa/wp-2fa.php' ) ) {
			add_filter(
				'drts_dashboard_account_settings_sections',
				function ( $sections ) {
					$sections['wp_2fa'] = array(
						'title'            => __( 'Two-Factor Authentication', 'directories-pro' ),
						'description'      => __( 'Configure two-factor authentication for your account.', 'directories-pro' ),
						'weight'           => 100,
						'section_callback' => function ( $context ) {
							return do_shortcode( '[wp-2fa-setup-form show_preamble="false" do_not_show_enabled="true"]' );
						},
					);
					return $sections;
				}
			);
			add_filter(
				'drts_dashboard_panels_loaded',
				function () {
					do_shortcode( '[wp-2fa-setup-form]' ); // load scripts/styles for the 2FA setup form
				}
			);
			add_filter(
				'wp_2fa_custom_setup_page_link',
				function ( $url, $user ) {
					if ( ! isset( $user ) ) {
						$user = \WP2FA\Admin\Helpers\User_Helper::get_user_object();
					}
					return (string) $this->_application->Dashboard_Panels_panelUrl( 'account', 'settings', '', array(), false, $user->user_login );
				},
				10,
				2
			);
		}

		// 3rd party plugin profile page integration
		if ( ! $this->_application->getPlatform()->isAdmin() ) {
			foreach ( array( 'BuddyPress', 'PeepSo' ) as $plugin_class ) {
				$this->_initProfileClass( $plugin_class );
			}
		}
		// Ultimate Member profile needs to be initialized in both backend/frontend
		$this->_initProfileClass( 'UM' );
	}

	protected function _initProfileClass( $pluginClass ) {
		if ( class_exists( $pluginClass, false )
			&& ! empty( $this->_config['wordpress'][ $pluginClass ]['account_show'] )
		) {
			$profile_class = __NAMESPACE__ . '\\WordPress\\' . $pluginClass . 'Profile';
			$settings      = array(
				'_own_profile_only' => empty( $this->_config['panel']['enable_public'] ) || ! $this->_isPublicDashboardAllowed(),
			) + $this->_config['wordpress'][ $pluginClass ];
			$profile_class::init( $this->_application, $settings );
		}
	}

	public function onCoreAccessRouteFilter( &$result, Context $context, $route, $paths ) {
		if ( ! $result
			|| $context->isEmbed()
			|| $this->_application->getPlatform()->getName() !== 'WordPress'
			|| $this->_application->getPlatform()->isAdmin()
			|| Request::isXhr()
			|| Request::isPostMethod()
			|| $paths[0] !== $this->getSlug( 'dashboard' )
		) {
			return;
		}

		// Redirect dashboard access to 3rd party plugin profile page?
		foreach ( array( 'BuddyPress', 'UM', 'PeepSo' ) as $plugin_class ) {
			if ( class_exists( $plugin_class, false )
				&& ! empty( $this->_config['wordpress'][ $plugin_class ]['account_redirect'] )
				&& ! empty( $this->_config['wordpress'][ $plugin_class ]['account_show'] )
			) {
				$profile_class = __NAMESPACE__ . '\\WordPress\\' . $plugin_class . 'Profile';
				$settings      = array(
					'_own_profile_only' => empty( $this->_config['panel']['enable_public'] ) || ! $this->_isPublicDashboardAllowed(),
				) + $this->_config['wordpress'][ $plugin_class ];
				$profile_class::init( $this->_application, $settings )->redirectDashboardAccess( $context, $paths );
				if ( $context->isRedirect() ) {
					$result = false;
					return;
				}
			}
		}
	}

	protected function _isPublicDashboardAllowed() {
		return ! ( $this->_application->isComponentLoaded( 'User' ) && $this->_application->getComponent( 'User' )->userDirectoryExists() );
	}

	public function onCoreUserLinkAttrFilter( &$attr, $identity ) {
		if ( false !== $attr['href'] // link is disabled if false
			&& ! empty( $this->_config['panel']['enable_public'] )
			&& ! $identity->isAnonymous()
			&& $this->_isPublicDashboardAllowed()
		) {
			$attr['href']   = $this->_application->Url( '/' . $this->getSlug( 'dashboard' ) . '/' . urlencode( $identity->username ) );
			$attr['target'] = $attr['rel'] = '';
		}
	}

	public function onEntityCreatePostEntitySuccess( $bundle, $entity, $values, $extraArgs ) {
		$this->_maybeClearPostsPanelPostCounts( $bundle, $entity );
	}

	public function onEntityUpdatePostEntitySuccess( $bundle, $entity, $oldEntity, $values, $extraArgs ) {
		$this->_maybeClearPostsPanelPostCounts( $bundle, $entity, $oldEntity );
	}

	protected function _maybeClearPostsPanelPostCounts( $bundle, $entity, $oldEntity = null ) {
		if ( ! $entity->getAuthorId()
			|| ( isset( $oldEntity ) && $entity->getStatus() === $oldEntity->getStatus() )
		) {
			return;
		}

		// Clear dashboard posts panel post counts
		$this->_application->getPlatform()->deleteCache( 'dashboard_post_counts_' . $bundle->group . '_' . $entity->getAuthorId() . '_', 'content' );
	}

	public function onEntityUpdateEntity( $bundle, $entity, &$values, $extraArgs ) {
		if ( ! empty( $extraArgs['dashboard_edit_post'] )
			&& ! empty( $values )
			&& $entity->isPublished()
			&& ! $this->_application->HasPermission( 'entity_publish_' . $bundle->name )
			&& $this->_application->Filter( 'dashboard_edit_post_pending', false, array( $bundle, $entity, $values, $extraArgs ) )
		) {
			$values['status'] = $this->_application->Entity_Status( $entity->getType(), 'pending' );
		}
	}

	public function notificationGetEventNames() {
		$ret = array( 'dashboard_account_deleted', 'dashboard_account_password_changed', 'dashboard_account_profile_edited' );
		foreach ( $this->_application->Entity_BundleTypes_byFeatures( array( 'public' ), 'post' ) as $bundle_type ) {
			$ret[] = 'dashboard_post_edited_' . $bundle_type;
			$ret[] = 'dashboard_post_deleted_' . $bundle_type;
		}
		return $ret;
	}

	public function notificationGetEvent( $name ) {
		switch ( $name ) {
			case 'dashboard_account_deleted':
				return new NotificationEvent\AccountDeletedNotificationEvent( $this->_application, $name );
			case 'dashboard_account_password_changed':
				return new NotificationEvent\AccountPasswordChangedNotificationEvent( $this->_application, $name );
			case 'dashboard_account_profile_edited':
				return new NotificationEvent\AccountProfileEditedNotificationEvent( $this->_application, $name );
			default:
				if ( strpos( $name, 'dashboard_post_edited_' ) === 0 ) {
					return new NotificationEvent\PostEditedNotificationEvent( $this->_application, $name, substr( $name, strlen( 'dashboard_post_edited_' ) ) );
				} elseif ( strpos( $name, 'dashboard_post_deleted_' ) === 0 ) {
					return new NotificationEvent\PostDeletedNotificationEvent( $this->_application, $name, substr( $name, strlen( 'dashboard_post_deleted_' ) ) );
				}
		}
	}

	public function onEntityIsAuthorFilter( &$ret, Entity\Type\IEntity $entity, AbstractIdentity $identity ) {
		if ( $identity->isAnonymous() ) {
			$ret = $this->_application->Dashboard_GuestAccess_isAuthor( $entity );
		}
	}

	public function onDisplayAdminElementsFormFilter( &$form, $bundle, $display, $parents ) {
		if ( $display['type'] === 'entity'
			&& $display['name'] === 'dashboard_row'
		) {
			$form['vertical_align'] = array(
				'#type'          => 'select',
				'#title'         => __( 'Vertical alignment', 'directories-pro' ),
				'#options'       => array(
					''       => __( 'Default', 'directories-pro' ),
					'top'    => __( 'Top', 'directories-pro' ),
					'middle' => __( 'Middle', 'directories-pro' ),
					'bottom' => __( 'Bottom', 'directories-pro' ),
				),
				'#default_value' => isset( $display['data']['vertical_align'] ) ? $display['data']['vertical_align'] : '',
				'#horizontal'    => true,
				'#weight'        => 20,
			);
		}
	}

	public function onDisplayAdminDisplayDataKeysFilter( &$keys, $bundle, $display ) {
		if ( $display->type === 'entity'
			&& $display->name === 'dashboard_row'
		) {
			$keys[] = 'vertical_align';
		}
	}

	public function onDisplayElementDataFilter( &$data, $bundle, $type, $display, $element ) {
		if ( $display->type === 'entity'
			&& $display->name === 'dashboard_row'
			&& ! empty( $display->data['vertical_align'] )
		) {
			$data['dashboard_row_vertical_align'] = $display->data['vertical_align'];
		}
	}

	public function onDisplayElementRenderedFilter( &$rendered, $bundle, array $element, $var ) {
		if ( $element['display_type'] === 'entity'
			&& $element['display'] === 'dashboard_row'
			&& ! empty( $element['dashboard_row_vertical_align'] )
		) {
			$rendered['attr']['class'] .= ' ' . DRTS_BS_PREFIX . 'align-' . $element['dashboard_row_vertical_align'];
		}
	}
}
