+
- * Created on: 17/08/2018 - * - * @package Neve\Addons\Abstracts - */ - -namespace Neve\Addons\Abstracts; - -/** - * Class Addon_Loader - * - * @package Neve\Addons\Abstracts - */ -abstract class Addon_Loader { - /** - * Modules that will be dropped from loading. - * - * @var array - */ - private $removables = array(); - - /** - * Modules that will be loaded. - * - * @var array - */ - private $addon_modules = array(); - - /** - * Addon_Loader constructor. - */ - public function __construct() { - $this->removables = $this->set_removables(); - $this->addon_modules = $this->set_addons(); - } - - /** - * Set the modules that will be added. - * - * @return array - */ - abstract protected function set_addons(); - - /** - * Set the features that will be removed. - * - * @return array - */ - abstract protected function set_removables(); - - /** - * Filter the modules. Remove the removables and add the addons. - * - * @param array $modules the modules array that we filter. - * - * @return array - */ - final public function filter_modules( $modules ) { - $removables = $this->removables; - foreach ( $removables as $removable ) { - $key = array_search( $removable, $modules ); - if ( $key === false ) { - continue; - } - unset( $modules[ $key ] ); - } - - $addons = $this->addon_modules; - foreach ( $addons as $addon ) { - array_push( $modules, $addon ); - } - - return $modules; - } -} diff --git a/inc/addons/customizer/main.php b/inc/addons/customizer/main.php deleted file mode 100644 index a0a95e166d..0000000000 --- a/inc/addons/customizer/main.php +++ /dev/null @@ -1,36 +0,0 @@ - - * Created on: 17/08/2018 - * - * @package neve\addons\customizer - */ - -namespace Neve\Addons\Customizer; - -use Neve\Addons\Abstracts\Addon_Loader; - -/** - * Addon Loader - * - * @package Neve\Addons\Customizer - */ -class Main extends Addon_Loader { - /** - * Set the modules that will be added. - * - * @return array - */ - protected function set_addons() { - return array(); - } - - /** - * Set the features that will be removed. - * - * @return array - */ - protected function set_removables() { - return array(); - } -} diff --git a/inc/addons/main.php b/inc/addons/main.php deleted file mode 100644 index 203ef91d5d..0000000000 --- a/inc/addons/main.php +++ /dev/null @@ -1,40 +0,0 @@ - - * Created on: 17/08/2018 - * - * @package Neve\Addons - */ - -namespace Neve\Addons; - -use Neve\Addons\Abstracts\Addon_Loader; - -/** - * Class Main - * - * @package Neve\Addons - */ -class Main extends Addon_Loader { - /** - * Set the modules that will be added. - * - * @return array - */ - protected function set_addons() { - return array(); - } - - /** - * Set the features that will be removed. - * - * @return array - */ - protected function set_removables() { - return array( - '', - ); - } - - -} diff --git a/inc/admin/dashboard/changelog_handler.php b/inc/admin/dashboard/changelog_handler.php new file mode 100644 index 0000000000..d03303d832 --- /dev/null +++ b/inc/admin/dashboard/changelog_handler.php @@ -0,0 +1,152 @@ +parse_changelog( $changelog_path ); + } + + /** + * Parse the changelog file. + * + * @param string $changelog_path the changelog path. + * + * @return array + */ + private function parse_changelog( $changelog_path ) { + WP_Filesystem(); + global $wp_filesystem; + $changelog = $wp_filesystem->get_contents( $changelog_path ); + if ( is_wp_error( $changelog ) ) { + $changelog = ''; + } + $changelog = explode( PHP_EOL, $changelog ); + $releases = []; + $release_count = 0; + $current_section = ''; // Holds the current section ('Improvements', 'Bug Fixes', etc.) + + foreach ( $changelog as $changelog_line ) { + if ( strpos( $changelog_line, '**Changes:**' ) !== false || empty( $changelog_line ) ) { + continue; + } + if ( substr( ltrim( $changelog_line ), 0, 3 ) === '###' && ! preg_match( '/###\s?(New Features|Bug Fixes)/', $changelog_line ) ) { + // Extract version and date + preg_match( '/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $changelog_line, $found_v ); + preg_match( '/[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}/', $changelog_line, $found_d ); + if ( isset( $found_v[0] ) && isset( $found_d[0] ) ) { + $release_count++; + $releases[ $release_count ] = array( + 'version' => $found_v[0], + 'date' => $found_d[0], + ); + } + + $current_section = ''; + continue; + } + + // Check for the new headers 'New Features', 'Bug Fixes', etc. + if ( preg_match( '/###\s?(New Features|Bug Fixes)/', $changelog_line, $section_matches ) ) { + $current_section = strtolower( $section_matches[1] ); + continue; + } + + // Extracting items based on the new changelog structure + if ( $current_section === 'bug fixes' ) { + $changelog_line = preg_replace( '/-\s?\*\*(.*?):\*\*/', '', $changelog_line ); + $changelog_line = $this->parse_md_and_clean( $changelog_line ); + $releases[ $release_count ]['fixes'][] = $changelog_line; + continue; + } + + if ( $current_section === 'new features' ) { + $changelog_line = preg_replace( '/-\s?\*\*(.*?):\*\*/', '', $changelog_line ); + $changelog_line = $this->parse_md_and_clean( $changelog_line ); + $releases[ $release_count ]['features'][] = $changelog_line; + continue; + } + + // Legacy structure handling for feats and fixes + if ( preg_match( '/[*|-]?\s?(\[fix]|\[Fix]|fix|Fix)[:]?\s?(\b|(?=\[))/', $changelog_line ) ) { + $changelog_line = preg_replace( '/[*|-]?\s?(\[fix]|\[Fix]|fix|Fix)[:]?\s?(\b|(?=\[))/', '', $changelog_line ); + $releases[ $release_count ]['fixes'][] = $this->parse_md_and_clean( $changelog_line ); + continue; + } + + if ( preg_match( '/[*|-]?\s?(\[feat]|\[Feat]|feat|Feat)[:]?\s?(\b|(?=\[))/', $changelog_line ) ) { + $changelog_line = preg_replace( '/[*|-]?\s?(\[feat]|\[Feat]|feat|Feat)[:]?\s?(\b|(?=\[))/', '', $changelog_line ); + $releases[ $release_count ]['features'][] = $this->parse_md_and_clean( $changelog_line ); + continue; + } + + $changelog_line = $this->parse_md_and_clean( $changelog_line ); + if ( empty( $changelog_line ) ) { + continue; + } + if ( ! isset( $releases[ $release_count ]['tweaks'] ) ) { + $releases[ $release_count ]['tweaks'] = []; + } + $releases[ $release_count ]['tweaks'][] = $changelog_line; + } + + return array_values( $releases ); + } + + /** + * Parse markdown links, convert bold markers (**) to tags, and cleanup string. + * + * @param string $string changelog line. + * + * @return string + */ + private function parse_md_and_clean( $string ) { + // Drop spaces, starting lines | asterisks. + $string = trim( $string ); + $string = ltrim( $string, '*' ); + $string = ltrim( $string, '-' ); + + // Replace markdown links with tags. + $string = preg_replace_callback( + '/\[(.*?)]\((.*?)\)/', + function ( $matches ) { + return '' . $matches[1] . ''; + }, + htmlspecialchars( $string ) + ); + + // Convert bold markdown (**text**) to text. + $string = preg_replace( '/\*\*(.*?)\*\*/', '$1', $string ); + + return $string; + } + +} diff --git a/inc/admin/dashboard/main.php b/inc/admin/dashboard/main.php new file mode 100755 index 0000000000..ccd65b8f8f --- /dev/null +++ b/inc/admin/dashboard/main.php @@ -0,0 +1,744 @@ +plugin_helper = new Plugin_Helper(); + $this->cl_handler = new Changelog_Handler(); + } + + /** + * Run WordPress attached to actions. + */ + public function init() { + + add_action( 'init', [ $this, 'setup_config' ] ); + add_action( 'admin_menu', [ $this, 'register' ] ); + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] ); + add_action( 'init', array( $this, 'register_settings' ) ); + add_action( 'init', array( $this, 'register_about_page' ), 1 ); + } + + /** + * Add the about page with respect to the white label settings. + * + * @return void + */ + public function register_about_page() { + $theme = wp_get_theme(); + $filtered_name = apply_filters( 'ti_wl_theme_name', $theme->__get( 'Name' ) ); + $slug = $theme->__get( 'stylesheet' ); + + if ( empty( $slug ) || empty( $filtered_name ) ) { + return; + } + + // We check if the name is different from the filtered name, + // if it is, the whitelabel is in use and we should not add the about page. + // this check allows for child themes to use the about page. + if ( $filtered_name !== $theme->__get( 'Name' ) ) { + return; + } + + add_filter( + 'neve_about_us_metadata', + function () use ( $filtered_name ) { + + return [ + // Top-level page in the dashboard sidebar + 'location' => 'neve-welcome', + // Logo to display on the page + 'logo' => get_template_directory_uri() . '/assets/img/dashboard/logo.svg', + // Condition to show or hide the upgrade menu in the sidebar + 'has_upgrade_menu' => ! defined( 'NEVE_PRO_VERSION' ), + // Add predefined product pages to the about page. + 'product_pages' => [ 'otter-page' ], + // Upgrade menu item link & text + 'upgrade_link' => tsdk_translate_link( tsdk_utmify( esc_url( 'https://themeisle.com/themes/neve/upgrade/' ), 'aboutfilter', 'nevedashboard' ), 'query' ), + 'upgrade_text' => __( 'Upgrade', 'neve' ) . ' ' . $filtered_name, + ]; + } + ); + } + + /** + * Register Logger Setting + */ + public function register_settings() { + register_setting( + 'neve_settings', + 'neve_logger_flag', + [ + 'type' => 'string', + 'show_in_rest' => true, + 'default' => '', + ] + ); + } + + /** + * Setup the class props based on current theme. + */ + public function setup_config() { + $theme = wp_get_theme(); + + $this->theme_args['name'] = apply_filters( 'ti_wl_theme_name', $theme->__get( 'Name' ) ); + $this->theme_args['template'] = $theme->get( 'Template' ); + $this->theme_args['version'] = $theme->__get( 'Version' ); + $this->theme_args['description'] = apply_filters( 'ti_wl_theme_description', $theme->__get( 'Description' ) ); + $this->theme_args['slug'] = $theme->__get( 'stylesheet' ); + } + + /** + * Register theme options page. + * + * @return void + */ + public function register() { + $theme = $this->theme_args; + + if ( empty( $theme['name'] ) || empty( $theme['slug'] ) ) { + return; + } + + $theme_page = ! empty( $theme['template'] ) ? $theme['template'] . '-welcome' : $theme['slug'] . '-welcome'; + + $icon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjQuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzMiAzMiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzIgMzI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTAsMS42QzAsMC43LDAuNywwLDEuNiwwaDI4LjhDMzEuMywwLDMyLDAuNywzMiwxLjZWMzBjMCwwLjktMC43LDEuNi0xLjYsMS42SDEuNkMwLjcsMzEuNSwwLDMwLjgsMCwzMFYxLjZ6CgkgTTEzLDE1Ljh2Ny41SDkuMVY4YzAtMC4xLDAtMC4xLDAuMS0wLjJjMCwwLDAuMSwwLDAuMiwwLjFsOS42LDcuOFY4LjJoMy44djE1LjJjMCwwLjEsMCwwLjEtMC4xLDAuMmMwLDAtMC4xLDAtMC4yLTAuMUwxMywxNS44egoJIE0yMi44LDI1LjdIOS4xVjI3aDEzLjdWMjUuN3oiLz4KPC9zdmc+Cg=='; + if ( $theme['name'] !== 'Neve' ) { + $icon = 'dashicons-admin-appearance'; + } + $neve_icon = apply_filters( 'neve_menu_icon', $icon ); + $priority = apply_filters( 'neve_menu_priority', 59 ); // The position of the menu item, 60 is the position of the Appearance menu. + $capability = 'manage_options'; + + // Place a theme page in the Appearance menu, for older versions of Neve Pro or TPC. to maintain backwards compatibility. + if ( + ( defined( 'NEVE_PRO_VERSION' ) && version_compare( NEVE_PRO_VERSION, '2.6.1', '<=' ) ) || + ( defined( 'TIOB_VERSION' ) && version_compare( TIOB_VERSION, '1.1.38', '<=' ) ) + ) { + add_theme_page( + /* translators: %s - Theme name */ + sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ), + /* translators: %s - Theme name */ + sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ), + $capability, + 'admin.php?page=neve-welcome' + ); + } + + add_menu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_menu_page + wp_kses_post( $theme['name'] ), + wp_kses_post( $theme['name'] ), + $capability, + $theme_page, + [ $this, 'render' ], + $neve_icon, // The URL to the icon to be used for this menu + $priority + ); + + // Add Dashboard submenu. Same slug as parent to allow renaming the automatic submenu that is added. + add_submenu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_submenu_page + $theme_page, + /* translators: %s - Theme name */ + sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ), + /* translators: %s - Theme name */ + sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ), + $capability, + $theme_page, + [ $this, 'render' ] + ); + + $this->copy_customizer_page( $theme_page ); + + if ( ! defined( 'NEVE_PRO_VERSION' ) || 'valid' !== apply_filters( 'product_neve_license_status', false ) ) { + // Add Custom Layout submenu for upsell. + add_submenu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_submenu_page + $theme_page, + __( 'Custom Layouts', 'neve' ), + __( 'Custom Layouts', 'neve' ), + $capability, + 'admin.php?page=neve-welcome#custom-layouts' + ); + } + } + + /** + * Copy the customizer page to the dashboard. + * + * @param string $theme_page The theme page slug. + * + * @return void + */ + private function copy_customizer_page( $theme_page ) { + global $submenu; + if ( ! isset( $submenu['themes.php'] ) ) { + return; + } + $themes_menu = $submenu['themes.php']; + if ( empty( $themes_menu ) ) { + return; + } + $customize_pos = array_search( 'customize', array_column( $themes_menu, 1 ) ); + if ( false === $customize_pos ) { + return; + } + $themes_page_keys = array_keys( $themes_menu ); + if ( ! isset( $themes_page_keys[ $customize_pos ] ) ) { + return; + } + + $customizer_menu_item = array_splice( $themes_menu, $customize_pos, 1 ); + $customizer_menu_item = reset( $customizer_menu_item ); + if ( empty( $customizer_menu_item ) ) { + return; + } + + add_submenu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_submenu_page + $theme_page, + $customizer_menu_item[0], + $customizer_menu_item[0], + 'manage_options', + 'customize.php' + ); + } + + /** + * Render the application stub. + * + * @return void + */ + public function render() { + echo '
'; + } + + /** + * Load css and scripts for the about page + */ + public function enqueue() { + $screen = get_current_screen(); + if ( ! isset( $screen->id ) ) { + return; + } + + $theme = $this->theme_args; + $theme_page = ! empty( $theme['template'] ) ? $theme['template'] . '-welcome' : $theme['slug'] . '-welcome'; + + if ( $screen->id !== 'toplevel_page_' . $theme_page ) { + return; + } + + $build_path = get_template_directory_uri() . '/assets/apps/dashboard/build/'; + $dependencies = ( include get_template_directory() . '/assets/apps/dashboard/build/dashboard.asset.php' ); + + wp_register_style( 'neve-dash-style', $build_path . 'style-dashboard.css', [ 'wp-components', 'neve-components' ], $dependencies['version'] ); + wp_style_add_data( 'neve-dash-style', 'rtl', 'replace' ); + wp_enqueue_style( 'neve-dash-style' ); + wp_register_script( 'neve-dash-script', $build_path . 'dashboard.js', array_merge( $dependencies['dependencies'], [ 'updates' ] ), $dependencies['version'], true ); + wp_localize_script( 'neve-dash-script', 'neveDash', apply_filters( 'neve_dashboard_page_data', $this->get_localization() ) ); + wp_enqueue_script( 'neve-dash-script' ); + + if ( function_exists( 'wp_set_script_translations' ) ) { + wp_set_script_translations( 'neve-dash-script', 'neve' ); + } + + do_action( 'themeisle_sdk_dependency_enqueue_script', 'survey' ); + } + + /** + * Get localization data for the dashboard script. + * + * @return array + */ + private function get_localization() { + + $offer = new Limited_Offers(); + + $old_about_config = apply_filters( 'ti_about_config_filter', [ 'useful_plugins' => true ] ); + $theme_name = apply_filters( 'ti_wl_theme_name', $this->theme_args['name'] ); + $plugin_name = apply_filters( 'ti_wl_plugin_name', 'Neve Pro' ); + $plugin_name_addon = apply_filters( 'ti_wl_plugin_name', 'Neve Pro Addon' ); + $data = [ + 'nonce' => wp_create_nonce( 'wp_rest' ), + 'version' => 'v' . NEVE_VERSION, + 'assets' => get_template_directory_uri() . '/assets/img/dashboard/', + 'hasOldPro' => (bool) ( defined( 'NEVE_PRO_VERSION' ) && version_compare( NEVE_PRO_VERSION, '1.1.11', '<' ) ), + 'isRTL' => is_rtl(), + 'isValidLicense' => $this->has_valid_addons(), + 'notifications' => $this->get_notifications(), + 'customizerShortcuts' => $this->get_customizer_shortcuts(), + 'plugins' => $this->get_useful_plugins(), + 'featureData' => $this->get_free_pro_features(), + 'showFeedbackNotice' => $this->should_show_feedback_notice(), + 'allfeaturesNeveProURL' => tsdk_translate_link( tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'seeallfeatures', 'freevspropage' ), 'query' ), + 'startSitesgetNeveProURL' => tsdk_translate_link( tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'welcomestartersitescard', 'nevedashboard' ), 'query' ), + 'customLayoutsNeveProURL' => tsdk_translate_link( tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'customlayoutscard', 'nevedashboard' ), 'query' ), + 'upgradeURL' => apply_filters( 'neve_upgrade_link_from_child_theme_filter', tsdk_translate_link( tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'getpronow', 'freevspropage' ), 'query' ) ), + 'supportURL' => esc_url( 'https://wordpress.org/support/theme/neve/' ), + 'docsURL' => esc_url( 'https://docs.themeisle.com/article/946-neve-doc' ), + 'codexURL' => esc_url( 'https://codex.nevewp.com/' ), + 'strings' => [ + 'proTabTitle' => wp_kses_post( $plugin_name ), + /* translators: %s - Theme name */ + 'header' => sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme_name ) ), + /* translators: %s - Theme name */ + 'starterSitesCardDescription' => sprintf( __( '%s now comes with a sites library with various designs to pick from. Visit our collection of demos that are constantly being added.', 'neve' ), wp_kses_post( $theme_name ) ), + 'starterSitesCardUpsellMessage' => esc_html__( 'Upgrade to the Pro version and get instant access to all Premium Starter Sites — including Expert Sites — and much more.', 'neve' ), + /* translators: %s - Theme name */ + 'starterSitesTabDescription' => sprintf( __( 'With %s, you can choose from multiple unique demos, specially designed for you, that can be installed with a single click. You just need to choose your favorite, and we will take care of everything else.', 'neve' ), wp_kses_post( $theme_name ) ), + /* translators: 1 - Theme name, 2 - Cloud Templates & Patterns Collection */ + 'starterSitesUnavailableActive' => sprintf( __( 'In order to be able to import any starter sites for %1$s you would need to have the %2$s plugin active.', 'neve' ), wp_kses_post( $theme_name ), 'Cloud Templates & Patterns Collection' ), + /* translators: %s - Theme name */ + 'starterSitesUnavailableUpdate' => sprintf( __( 'In order to be able to import any starter sites for %1$s you would need to have the %2$s plugin updated to the latest version.', 'neve' ), wp_kses_post( $theme_name ), 'Cloud Templates & Patterns Collection' ), + /* translators: %s - Theme name */ + 'supportCardDescription' => sprintf( __( 'We want to make sure you have the best experience using %1$s, and that is why we have gathered all the necessary information here for you. We hope you will enjoy using %1$s as much as we enjoy creating great products.', 'neve' ), wp_kses_post( $theme_name ) ), + /* translators: %s - Theme name */ + 'docsCardDescription' => sprintf( __( 'Need more details? Please check our full documentation for detailed information on how to use %s.', 'neve' ), wp_kses_post( $theme_name ) ), + /* translators: %s - "Neve Pro Addon" */ + 'licenseCardHeading' => sprintf( __( '%s license', 'neve' ), wp_kses_post( $plugin_name_addon ) ), + /* translators: %s - "Neve Pro Addon" */ + 'updateOldPro' => sprintf( __( 'Please update %s to the latest version and then refresh this page to have access to the options.', 'neve' ), wp_kses_post( $plugin_name_addon ) ), + /* translators: %1$s - Author link - Themeisle */ + 'licenseCardDescription' => sprintf( + // translators: store name (Themeisle) + __( 'Enter your license from %1$s purchase history in order to get plugin updates', 'neve' ), + 'ThemeIsle' . esc_html__( '(opens in a new tab)', 'neve' ) . '' + ), + ], + 'changelog' => $this->cl_handler->get_changelog( get_template_directory() . '/CHANGELOG.md' ), + 'onboarding' => [], + 'hasFileSystem' => WP_Filesystem(), + 'hidePluginsTab' => apply_filters( 'neve_hide_useful_plugins', ! array_key_exists( 'useful_plugins', $old_about_config ) ), + 'tpcPath' => defined( 'TIOB_PATH' ) ? TIOB_PATH . 'template-patterns-collection.php' : 'template-patterns-collection/template-patterns-collection.php', + 'tpcAdminURL' => admin_url( 'admin.php?page=tiob-starter-sites' ), + 'pluginsURL' => esc_url( admin_url( 'plugins.php' ) ), + 'getPluginStateBaseURL' => esc_url( rest_url( '/nv/v1/dashboard/plugin-state/' ) ), + 'canInstallPlugins' => current_user_can( 'install_plugins' ), + 'canActivatePlugins' => current_user_can( 'activate_plugins' ), + 'deal' => $offer->get_localized_data(), + 'rootUrl' => get_site_url(), + 'daysSinceInstall' => round( ( time() - get_option( 'neve_install', 0 ) ) / DAY_IN_SECONDS ), + 'proPluginVersion' => defined( 'NEVE_PRO_VERSION' ) ? NEVE_PRO_VERSION : '', + ]; + + if ( defined( 'NEVE_PRO_PATH' ) ) { + $installed_plugins = get_plugins(); + $is_otter_installed = array_key_exists( 'otter-pro/otter-pro.php', $installed_plugins ); + $is_sparks_installed = array_key_exists( 'sparks-for-woocommerce/sparks-for-woocommerce.php', $installed_plugins ); + $data['changelogPro'] = $this->cl_handler->get_changelog( NEVE_PRO_PATH . '/CHANGELOG.md' ); + $data['isOtterProInstalled'] = $is_otter_installed; + $data['otterProInstall'] = $is_otter_installed ? esc_url( wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=otter-pro%2Fotter-pro.php&plugin_status=all&paged=1&s' ), 'activate-plugin_otter-pro/otter-pro.php' ) ) : esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=install_otter_pro' ), 'install_otter_pro' ) ); + $data['sparksInstallActivateEndpoint'] = $is_sparks_installed ? esc_url( wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=sparks-for-woocommerce%2Fsparks-for-woocommerce.php&plugin_status=all&paged=1&s' ), 'activate-plugin_sparks-for-woocommerce/sparks-for-woocommerce.php' ) ) : esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=install_sparks' ), 'install_sparks' ) ); + $data['moduleObserver'] = array( + 'customLayouts' => array( + 'labelSubMenu' => __( 'Custom Layouts', 'neve' ), + 'linkSubMenu' => 'edit.php?post_type=neve_custom_layouts', + ), + ); + + } + + if ( isset( $_GET['onboarding'] ) && $_GET['onboarding'] === 'yes' ) { + $data['isOnboarding'] = true; + } + $language = get_user_locale(); + $available_languages = [ + 'de_DE' => 'de', + 'de_DE_formal' => 'de', + ]; + $lang_code = isset( $available_languages[ $language ] ) ? 'de' : 'en'; + $data['lang'] = $lang_code; + + return $data; + } + + /** + * Get the notifications for plugin and theme updates. + * + * @return array + */ + public function get_notifications() { + $notifications = []; + $slug = 'neve'; + $themes_update = get_site_transient( 'update_themes' ); + + $plugin_folder = defined( 'NEVE_PRO_BASEFILE' ) ? basename( dirname( NEVE_PRO_BASEFILE ) ) : null; + $plugin_path = $plugin_folder ? $plugin_folder . '/neve-pro-addon.php' : null; + + if ( isset( $themes_update->response[ $slug ] ) ) { + $update = $themes_update->response[ $slug ]; + $notifications['neve'] = [ + // translators: s - theme name (Neve). + 'text' => sprintf( __( 'New theme update for %1$s! Please update to %2$s.', 'neve' ), wp_kses_post( $this->theme_args['name'] ), wp_kses_post( $update['new_version'] ) ), + 'update' => [ + 'type' => 'theme', + 'slug' => $slug, + ], + 'cta' => __( 'Update Now', 'neve' ), + 'type' => ( $plugin_path && is_plugin_active( $plugin_path ) ) ? 'warning' : null, + ]; + } + + if ( $plugin_path ) { + $plugins_update = get_site_transient( 'update_plugins' ); + if ( is_plugin_active( $plugin_path ) && isset( $plugins_update->response[ $plugin_path ] ) ) { + $update = $plugins_update->response[ $plugin_path ]; + $notifications['neve-pro-addon'] = [ + 'text' => sprintf( + // translators: s - Pro plugin name (Neve Pro) + __( 'New plugin update for %1$s! Please update to %2$s.', 'neve' ), + wp_kses_post( apply_filters( 'ti_wl_plugin_name', 'Neve Pro' ) ), + wp_kses_post( $update->new_version ) + ), + 'update' => [ + 'type' => 'plugin', + 'slug' => 'neve-pro-addon', + 'path' => $plugin_path, + ], + 'cta' => __( 'Update Now', 'neve' ), + 'type' => 'warning', + ]; + } + } + + if ( $this->show_branding_notice() ) { + $notifications['branding-discount'] = [ + 'text' => sprintf( + // translators: s - Discount Code + __( 'From 3.3.0 we decided to remove the copyright component from the free version. You can continue using it if you rollback to 3.2.x or you can upgrade to pro, using a one time 50%% discount: %s', 'neve' ), + wp_kses_post( 'NEVEBRANDING50' ) + ), + 'url' => tsdk_translate_link( tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'copyrightnotice', 'nevedashboard' ), 'query' ), + 'targetBlank' => true, + 'cta' => __( 'Upgrade', 'neve' ), + ]; + } + + if ( count( $notifications ) === 1 && is_plugin_active( $plugin_path ) ) { + foreach ( $notifications as $key => $notification ) { + /* translators: 1 - Theme Name (Neve), 2 - Plugin Name (Neve Pro) */ + $notifications[ $key ]['text'] = sprintf( __( 'We recommend that both %1$s and %2$s are updated to the latest version to ensure optimal intercompatibility.', 'neve' ), wp_kses_post( $this->theme_args['name'] ), apply_filters( 'ti_wl_plugin_name', 'Neve Pro' ) ); + } + } + + $notifications = apply_filters( 'neve_dashboard_notifications', $notifications ); + + return $notifications; + } + + /** + * Should branding notice be shown. + * + * @return bool + */ + private function show_branding_notice() { + if ( $this->has_valid_addons() ) { + return false; + } + + return time() < strtotime( '2022-07-06' ); + } + + /** + * Get the Customizer Shortcut Links. + * + * @return array + */ + private function get_customizer_shortcuts() { + return [ + [ + 'text' => __( 'Upload Logo', 'neve' ), + 'link' => add_query_arg( [ 'autofocus[control]' => 'custom_logo' ], admin_url( 'customize.php' ) ), + ], + [ + 'text' => __( 'Set Colors', 'neve' ), + 'link' => add_query_arg( [ 'autofocus[section]' => 'neve_colors_background_section' ], admin_url( 'customize.php' ) ), + ], + [ + 'text' => __( 'Customize Fonts', 'neve' ), + 'link' => add_query_arg( [ 'autofocus[control]' => 'neve_headings_font_family' ], admin_url( 'customize.php' ) ), + ], + [ + 'text' => __( 'Layout Options', 'neve' ), + 'link' => add_query_arg( [ 'autofocus[panel]' => 'neve_layout' ], admin_url( 'customize.php' ) ), + ], + [ + 'text' => __( 'Header Options', 'neve' ), + 'link' => add_query_arg( [ 'autofocus[panel]' => 'hfg_header' ], admin_url( 'customize.php' ) ), + ], + [ + 'text' => __( 'Blog Layouts', 'neve' ), + 'link' => add_query_arg( [ 'autofocus[section]' => 'neve_blog_archive_layout' ], admin_url( 'customize.php' ) ), + ], + [ + 'text' => __( 'Footer Options', 'neve' ), + 'link' => add_query_arg( [ 'autofocus[panel]' => 'hfg_footer' ], admin_url( 'customize.php' ) ), + ], + [ + 'text' => __( 'Content / Sidebar', 'neve' ), + 'link' => add_query_arg( [ 'autofocus[section]' => 'neve_sidebar' ], admin_url( 'customize.php' ) ), + ], + ]; + } + + /** + * Get doc link. + * + * @param string $utm_term utm term to use for doc link. + * @param string $url url to doc. + * @return string + */ + private function get_doc_link( $utm_term, $url ) { + + return tsdk_utmify( $url, $utm_term, 'freevspropage' ); + } + + /** + * Get the pro features for the free v pro table. + * + * @return array + */ + private function get_free_pro_features() { + return [ + [ + 'title' => __( 'Header/Footer builder', 'neve' ), + 'description' => __( 'Easily build your header and footer by dragging and dropping all the important elements in the real-time WordPress Customizer. More advanced options are available in PRO.', 'neve' ), + 'inLite' => true, + 'docsLink' => $this->get_doc_link( 'Header/Footer builder', 'https://docs.themeisle.com/category/1251-neve-header-builder' ), + ], + [ + 'title' => __( 'Page Builder Compatibility', 'neve' ), + 'description' => __( 'Neve is fully compatible with Gutenberg, the new WordPress editor and for all of you page builder fans, Neve has full compatibility with Elementor, Beaver Builder, and all the other popular page builders.', 'neve' ), + 'inLite' => true, + 'docsLink' => $this->get_doc_link( 'Page Builder Compatibility', 'https://docs.themeisle.com/article/946-neve-doc#pagebuilders' ), + ], + [ + 'title' => __( 'Header Booster', 'neve' ), + 'description' => __( 'Take the header builder to a new level with new awesome components: socials, contact, breadcrumbs, language switcher, multiple HTML, sticky and transparent menu, page header builder and many more.', 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'Header Booster', 'https://docs.themeisle.com/article/1057-header-booster-documentation' ), + ], + [ + 'title' => __( 'Page Header Builder', 'neve' ), + 'description' => __( 'The Page Header is the horizontal area that sits directly below the header and contains the page/post title. Easily design an attractive Page Header area using our dedicated builder.', 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'Page Header Builder', 'https://docs.themeisle.com/article/1262-neve-page-header' ), + ], + [ + 'title' => __( 'Custom Layouts', 'neve' ), + 'description' => __( 'Powerful Custom Layouts builder which allows you to easily create your own header, footer or custom content on any of the hook locations available in the theme.', 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'Custom Layouts', 'https://docs.themeisle.com/article/1062-custom-layouts-module' ), + ], + [ + 'title' => __( 'Blog Booster', 'neve' ), + 'description' => __( 'Give a huge boost to your entire blogging experience with features specially designed for increased user experience.', 'neve' ) . ' ' . __( 'Sharing, custom article sorting, comments integrations, number of minutes needed to read an article and many more.', 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'Blog Booster', 'https://docs.themeisle.com/article/1059-blog-booster-documentation' ), + ], + [ + 'title' => __( 'Elementor Booster', 'neve' ), + 'description' => __( 'Leverage the true flexibility of Elementor with powerful addons and templates that you can import with just one click.', 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'Elementor Booster', 'https://docs.themeisle.com/article/1063-elementor-booster-module-documentation' ), + ], + [ + 'title' => __( 'WooCommerce Booster', 'neve' ), + 'description' => __( 'Empower your online store with awesome new features, specially designed for a smooth WooCommerce integration.', 'neve' ) . ' ' . __( 'Wishlist, quick view, video products, advanced reviews, multiple dedicated layouts and many more.', 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'WooCommerce Booster', 'https://docs.themeisle.com/article/1058-woocommerce-booster-documentation' ), + ], + [ + 'title' => __( 'LifterLMS Booster', 'neve' ), + 'description' => __( 'Make your LifterLMS pages look stunning with our PRO design options. Specially created to help you set up your online courses with minimum customizations.', 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'LifterLMS Booster', 'https://docs.themeisle.com/article/1084-lifterlms-booster-documentation' ), + ], + [ + 'title' => __( 'Typekit(Adobe) Fonts', 'neve' ), + 'description' => __( "The module allows for an easy way of enabling new awesome Adobe (previous Typekit) Fonts in Neve's Typography options.", 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'Typekit(Adobe) Fonts', 'https://docs.themeisle.com/article/1085-typekit-fonts-documentation' ), + ], + [ + 'title' => __( 'White Label', 'neve' ), + 'description' => __( "For any developer or agency out there building websites for their own clients, we've made it easy to present the theme as your own.", 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'White Label', 'https://docs.themeisle.com/article/1061-white-label-module-documentation' ), + ], + [ + 'title' => __( 'Scroll To Top', 'neve' ), + 'description' => __( 'Simple but effective module to help you navigate back to the top of the really long pages.', 'neve' ), + 'inLite' => false, + 'docsLink' => $this->get_doc_link( 'Scroll To Top', 'https://docs.themeisle.com/article/1060-scroll-to-top-module-documentation' ), + ], + [ + 'title' => __( 'See all PRO features', 'neve' ), + 'presentational' => true, + ], + ]; + } + + /** + * Get the useful plugin data. + * + * @return array + */ + private function get_useful_plugins() { + $available = get_transient( $this->plugins_cache_key ); + $hash = get_transient( $this->plugins_cache_hash_key ); + $current_hash = substr( md5( wp_json_encode( $this->useful_plugins ) ), 0, 5 ); + + if ( $available !== false && $hash === $current_hash ) { + $available = json_decode( $available, true ); + foreach ( $available as $slug => $args ) { + $available[ $slug ]['cta'] = ( $args['cta'] === 'external' ) ? 'external' : $this->plugin_helper->get_plugin_state( $slug ); + $available[ $slug ]['path'] = $this->plugin_helper->get_plugin_path( $slug ); + $available[ $slug ]['activate'] = $this->plugin_helper->get_plugin_action_link( $slug ); + $available[ $slug ]['deactivate'] = $this->plugin_helper->get_plugin_action_link( $slug, 'deactivate' ); + $available[ $slug ]['network'] = $this->plugin_helper->get_is_network_wide( $slug ); + $available[ $slug ]['version'] = ! empty( $available[ $slug ]['version'] ) ? $this->plugin_helper->get_plugin_version( $slug, $available[ $slug ]['version'] ) : ''; + } + + return $available; + } + + $data = []; + foreach ( $this->useful_plugins as $slug ) { + + if ( array_key_exists( $slug, $this->get_external_plugins_data() ) ) { + $data[ $slug ] = $this->get_external_plugins_data()[ $slug ]; + continue; + } + + $current_plugin = $this->plugin_helper->get_plugin_details( $slug ); + if ( $current_plugin instanceof \WP_Error ) { + continue; + } + $data[ $slug ] = [ + 'banner' => $current_plugin->banners['low'], + 'name' => html_entity_decode( $current_plugin->name ), + 'description' => html_entity_decode( $current_plugin->short_description ), + 'version' => $current_plugin->version, + 'author' => html_entity_decode( wp_strip_all_tags( $current_plugin->author ) ), + 'cta' => $this->plugin_helper->get_plugin_state( $slug ), + 'path' => $this->plugin_helper->get_plugin_path( $slug ), + 'activate' => $this->plugin_helper->get_plugin_action_link( $slug ), + 'deactivate' => $this->plugin_helper->get_plugin_action_link( $slug, 'deactivate' ), + 'network' => $this->plugin_helper->get_is_network_wide( $slug ), + ]; + } + + set_transient( $this->plugins_cache_hash_key, $current_hash ); + set_transient( $this->plugins_cache_key, wp_json_encode( $data ) ); + + return $data; + } + + /** + * Check if feedback notice should be shown after 14 days since activation. + * + * @return bool + */ + private function should_show_feedback_notice() { + $activated_time = get_option( 'neve_install' ); + if ( ! empty( $activated_time ) ) { + if ( time() - intval( $activated_time ) > 14 * DAY_IN_SECONDS ) { + return true; + } + } + return false; + } + + /** + * Get data of external plugins that are not hosted on wp.org. + * + * @return array + */ + private function get_external_plugins_data() { + + $plugins = array( + 'wp-landing-kit' => array( + 'banner' => NEVE_ASSETS_URL . 'img/dashboard/wp-landing.jpg', + 'name' => 'WP Landing Kit', + 'description' => 'Turn WordPress into a landing page powerhouse with Landing Kit. Map domains to pages or any other published resource.', + 'author' => 'Themeisle', + 'cta' => 'external', + 'url' => tsdk_utmify( 'https://wplandingkit.com/', 'recommendedplugins', 'nevedashboard' ), + 'premium' => true, + ), + + ); + + return $plugins; + } + +} diff --git a/inc/admin/dashboard/plugin_helper.php b/inc/admin/dashboard/plugin_helper.php new file mode 100644 index 0000000000..6a142415e8 --- /dev/null +++ b/inc/admin/dashboard/plugin_helper.php @@ -0,0 +1,154 @@ +get_plugin_path( $slug ); + if ( file_exists( WP_PLUGIN_DIR . '/' . $plugin_link_suffix ) ) { + return is_plugin_active( $plugin_link_suffix ) ? 'deactivate' : 'activate'; + } + + return 'install'; + } + + /** + * Get plugin path based on plugin slug. + * + * @param string $slug - plugin slug. + * + * @return string + */ + public function get_plugin_path( $slug ) { + switch ( $slug ) { + case 'mailin': + return $slug . '/sendinblue.php'; + case 'wpforms-lite': + return $slug . '/wpforms.php'; + case 'intergeo-maps': + case 'visualizer': + case 'translatepress-multilingual': + return $slug . '/index.php'; + case 'beaver-builder-lite-version': + return $slug . '/fl-builder.php'; + case 'adblock-notify-by-bweb': + return $slug . '/adblock-notify.php'; + case 'feedzy-rss-feeds': + return $slug . '/feedzy-rss-feed.php'; + case 'wp-cloudflare-page-cache': + return $slug . '/wp-cloudflare-super-page-cache.php'; + default: + return $slug . '/' . $slug . '.php'; + } + } + + /** + * Call plugin api + * + * @param string $slug plugin slug. + * + * @return object + */ + public function get_plugin_details( $slug ) { + include_once ABSPATH . 'wp-admin/includes/plugin-install.php'; + + return plugins_api( + 'plugin_information', + array( + 'slug' => $slug, + 'fields' => array( + 'downloaded' => false, + 'rating' => false, + 'description' => false, + 'short_description' => true, + 'donate_link' => false, + 'tags' => false, + 'sections' => false, + 'homepage' => false, + 'added' => false, + 'last_updated' => false, + 'compatibility' => false, + 'tested' => false, + 'requires' => false, + 'downloadlink' => false, + 'icons' => false, + 'banners' => true, + ), + ) + ); + } + + /** + * Get Plugin Action link. + * + * @param string $slug plugin slug. + * @param string $action action [activate, deactivate]. + * @return string + */ + public function get_plugin_action_link( $slug, $action = 'activate' ) { + if ( ! in_array( $action, [ 'activate', 'deactivate' ] ) ) { + return ''; + } + + return add_query_arg( + array( + 'action' => $action, + 'plugin' => rawurlencode( $this->get_plugin_path( $slug ) ), + 'plugin_status' => 'all', + 'paged' => '1', + '_wpnonce' => wp_create_nonce( $action . '-plugin_' . $this->get_plugin_path( $slug ) ), + ), + esc_url( 'plugins.php' ) + ); + } + + /** + * Get plugin version. + * + * @param string $slug plugin slug. + * @return string | bool + */ + public function get_plugin_version( $slug, $default = false ) { + $plugin_file = $this->get_plugin_path( $slug ); + if ( ! is_plugin_active( $plugin_file ) ) { + return $default; + } + + $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_file ); + if ( ! array_key_exists( 'Version', $plugin_data ) ) { + return $default; + } + return $plugin_data['Version']; + } + + /** + * Check if current plugin is activated network-wide + * + * @param string $slug plugin slug. + * @return bool + */ + public function get_is_network_wide( $slug ) { + if ( ! is_multisite() ) { + return false; + } + $plugin_file = $this->get_plugin_path( $slug ); + return is_plugin_active_for_network( $plugin_file ); + } +} diff --git a/inc/admin/hooks_upsells.php b/inc/admin/hooks_upsells.php new file mode 100644 index 0000000000..6139e291e4 --- /dev/null +++ b/inc/admin/hooks_upsells.php @@ -0,0 +1,197 @@ +should_load() ) { + return; + } + + add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu' ), 99 ); + add_action( 'wp', array( $this, 'render_hook_placeholder' ) ); + } + + /** + * Check user role before allowing the class to run + * + * @return bool + */ + public function should_load() { + $should_load = current_user_can( 'administrator' ) && ! defined( 'NEVE_PRO_VERSION' ); + return apply_filters( 'neve_hooks_upsell_should_load', $should_load ); + } + + /** + * Check if the hooks should be shown. + */ + private function show_hooks() { + return isset( $_GET['neve_preview_hook'] ) && 'show' === $_GET['neve_preview_hook']; + } + + /** + * Render the admin item to show/hide hooks. + * + * @param \WP_Admin_Bar $wp_admin_bar Admin bar menus. + * + * @return void + */ + public function add_admin_bar_menu( $wp_admin_bar ) { + if ( is_admin() ) { + return; + } + + $title = __( 'Show Hooks', 'neve' ); + $href = add_query_arg( 'neve_preview_hook', 'show' ); + if ( $this->show_hooks() ) { + $title = __( 'Hide Hooks', 'neve' ); + $href = remove_query_arg( 'neve_preview_hook' ); + } + + $wp_admin_bar->add_menu( + array( + 'title' => sprintf( '%s ', $title ), + 'id' => 'neve_preview_hook', + 'parent' => false, + 'href' => $href, + ) + ); + } + + /** + * Beautify the hook name. + * + * @param string $hook_label The hook label. + * + * @return string + */ + public static function beautify_hook( $hook_label ) { + $hook_label = str_replace( 'neve_', '', $hook_label ); + $hook_label = str_replace( '_', ' ', $hook_label ); + $hook_label = str_replace( 'nv', ' ', $hook_label ); + $hook_label = str_replace( 'woocommerce', ' ', $hook_label ); + return ucwords( $hook_label ); + } + + /** + * Get the CSS for the hooks. + * + * @return string + */ + private function get_css() { + return ' + .nv-hook-wrapper { + text-align: center; width: 100%; + } + .nv-hook-placeholder { + display: flex; + width: 98%; + justify-content: center; + align-items: center; + margin: 10px auto; + border: 2px dashed #0065A6; + font-size: 14px; + padding: 6px 10px; + text-align: left; + word-break: break-word; + } + .nv-hook-placeholder a { + display: flex; + align-items: center; + justify-content: center; + color: #0065A6; + min-width: 250px; + width: 100%; + font-size: 16px; + min-height: 32px; + text-decoration: none; + } + .nv-hook-placeholder a:hover, .nv-hook-placeholder a:focus { + text-decoration: none; + } + .nv-hook-placeholder a:hover .nv-hook-upsell, .nv-hook-placeholder a:focus .nv-hook-upsell { + color: #0065A6; + opacity: 1; + } + .nv-hook-placeholder a .nv-hook-upsell { + font-size: 14px; + line-height: 16px; + font-weight: 600; + padding: 3px 2px; + margin-left: -2px; + display: flex; + align-items: center; + opacity: 0; + transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1); + position: absolute; + } + .nv-hook-placeholder a .nv-hook-name { + transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1); + font-size: 14px; + opacity: 1; + } + .nv-hook-placeholder a:hover .nv-hook-name, .nv-hook-placeholder a:focus .nv-hook-name { + opacity: 0; + '; + } + + /** + * Render the hooks placeholders with upsell. + */ + public function render_hook_placeholder() { + if ( ! $this->show_hooks() ) { + return; + } + $hooks = neve_hooks(); + echo ''; + $upsell_label = __( 'Neve PRO Features', 'neve' ) . ' / ' . __( 'Learn more', 'neve' ); + + // These hooks have to be removed as to not have nested links that will break the layout. + // We don't need them when the hooks are displayed, as the action will be replaced by the displayed hook action. + remove_action( 'woocommerce_before_shop_loop_item', 'woocommerce_template_loop_product_link_open', 10 ); + remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_product_link_close', 5 ); + + foreach ( $hooks as $hooks_in_category ) { + foreach ( $hooks_in_category as $hook_value ) { + $hook_label = self::beautify_hook( $hook_value ); + add_action( + $hook_value, + function () use ( $hook_value, $hook_label, $upsell_label ) { + $style = ''; + if ( 'woocommerce_before_shop_loop_item' === $hook_value ) { + $style = 'max-width: 200px;'; + } + $upsell_url = tsdk_translate_link( tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'viewhooks' ), 'query' ); + echo ''; + } + ); + } + } + } + +} diff --git a/inc/admin/metabox/controls/checkbox.php b/inc/admin/metabox/controls/checkbox.php index d2e8a003a8..924760a4e9 100644 --- a/inc/admin/metabox/controls/checkbox.php +++ b/inc/admin/metabox/controls/checkbox.php @@ -32,7 +32,7 @@ public function render_content( $post_id ) { $markup .= '

'; $markup .= '

'; $markup .= '
'; $markup .= '

'; - echo $markup; + echo $markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } diff --git a/inc/admin/metabox/controls/control_base.php b/inc/admin/metabox/controls/control_base.php index ee60144fd2..73f53eb72e 100644 --- a/inc/admin/metabox/controls/control_base.php +++ b/inc/admin/metabox/controls/control_base.php @@ -35,6 +35,13 @@ abstract class Control_Base { */ public $type = ''; + /** + * Control Priority + * + * @var int + */ + public $priority = 10; + /** * Control_Base constructor. * @@ -47,6 +54,9 @@ public function __construct( $id, $settings ) { } $this->id = $id; $this->settings = $settings; + if ( isset( $settings['priority'] ) ) { + $this->priority = $settings['priority']; + } } /** @@ -65,34 +75,6 @@ public function render( $post_id ) { wp_nonce_field( 'neve_meta_box_nonce', 'neve_meta_box_process' ); } - /** - * Render control label. - * - * @return string - */ - protected function render_label() { - $label = array_key_exists( 'label', $this->settings ) ? $this->settings['label'] : ''; - - if ( empty( $label ) ) { - return; - } - - $control_label = ''; - - $control_label .= '

'; - $control_label .= '' . esc_html( $label ) . ''; - $control_label .= '

'; - - echo wp_kses_post( $control_label ); - } - - /** - * Render control. - * - * @return void - */ - abstract public function render_content( $post_id ); - /** * Determine if a control should be visible or not. * @@ -117,32 +99,47 @@ private function should_render() { } /** - * Get the value. + * Render control label. * - * @return mixed + * @return void */ - protected final function get_value( $post_id ) { - $values = get_post_meta( $post_id ); + protected function render_label() { + $label = array_key_exists( 'label', $this->settings ) ? $this->settings['label'] : ''; - return isset( $values[ $this->id ] ) ? esc_attr( $values[ $this->id ][0] ) : $this->settings['default']; + if ( empty( $label ) ) { + return; + } + + $control_label = ''; + + $control_label .= '

'; + $control_label .= '' . esc_html( $label ) . ''; + $control_label .= '

'; + + echo wp_kses_post( $control_label ); } + /** + * Render control. + * + * @return void + */ + abstract public function render_content( $post_id ); + /** * Save control data. * - * @param string $post_id Post id. + * @param int $post_id Post id. * * @return void */ - public final function save( $post_id ) { + final public function save( $post_id ) { if ( ! isset( $_POST['neve_meta_box_process'] ) ) { return; } - - if ( ! wp_verify_nonce( $_POST['neve_meta_box_process'], 'neve_meta_box_nonce' ) ) { + if ( ! wp_verify_nonce( sanitize_key( $_POST['neve_meta_box_process'] ), 'neve_meta_box_nonce' ) ) { return; } - if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } @@ -150,19 +147,25 @@ public final function save( $post_id ) { return; } if ( isset( $_POST[ $this->id ] ) ) { - $value = wp_unslash( $_POST[ $this->id ] ); - if ( $value === $this->settings['default'] ) { + $value = $this->sanitize_value( wp_unslash( $_POST[ $this->id ] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + // Remove post meta on default. + if ( $value === $this->settings['default'] && $this->id !== 'neve_meta_content_width' ) { delete_post_meta( $post_id, $this->id ); return; } - - update_post_meta( $post_id, $this->id, $this->sanitize_value( $value ) ); + // Update post meta on other values. + update_post_meta( $post_id, $this->id, $value ); + return; + } else { + if ( $this->id === 'neve_meta_enable_content_width' ) { + update_post_meta( $post_id, 'neve_meta_enable_content_width', 'off' ); + } else { + delete_post_meta( $post_id, $this->id ); + } return; } - delete_post_meta( $post_id, $this->id ); - } /** @@ -181,21 +184,35 @@ protected function sanitize_value( $value ) { } return sanitize_text_field( $value ); - break; case 'checkbox': $allowed_values = array( 'on', 'off' ); - if ( ! in_array( $value, $allowed_values ) ) { + if ( ! in_array( $value, $allowed_values, true ) ) { return esc_html( $this->settings['default'] ); } return sanitize_text_field( $value ); - break; case 'range': return absint( $value ); - break; + case 'input': + return sanitize_text_field( $value ); case 'separator': default: break; } + + return sanitize_text_field( $value ); + } + + /** + * Get the value. + * + * @param int $post_id the post id. + * + * @return mixed + */ + final protected function get_value( $post_id ) { + $value = get_post_meta( $post_id, $this->id, true ); + + return ! empty( $value ) ? $value : $this->settings['default']; } } diff --git a/inc/admin/metabox/controls/radio.php b/inc/admin/metabox/controls/radio.php index 94b6f12e4b..3af4072e2e 100644 --- a/inc/admin/metabox/controls/radio.php +++ b/inc/admin/metabox/controls/radio.php @@ -39,6 +39,6 @@ public function render_content( $post_id ) { } $markup .= '

'; - echo $markup; + echo $markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } diff --git a/inc/admin/metabox/controls/range.php b/inc/admin/metabox/controls/range.php index 3aad132810..6ec062ebaf 100644 --- a/inc/admin/metabox/controls/range.php +++ b/inc/admin/metabox/controls/range.php @@ -37,26 +37,32 @@ public function render_content( $post_id ) { $class .= ' neve-dependent'; } - $markup = ''; + $markup = ' +'; $markup .= '

'; - $markup .= ''; - $markup .= ''; + $markup .= ''; + $markup .= ''; $markup .= '

'; - echo $markup; + echo $markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } diff --git a/inc/admin/metabox/controls_base.php b/inc/admin/metabox/controls_base.php new file mode 100644 index 0000000000..2d0a238a48 --- /dev/null +++ b/inc/admin/metabox/controls_base.php @@ -0,0 +1,55 @@ +add_controls(); + } + + /** + * Add controls. + */ + abstract protected function add_controls(); + + /** + * Add the control. + * + * @param Controls\Control_Base|Object $control the control object. + */ + public function add_control( $control ) { + array_push( $this->controls, $control ); + } + + /** + * Get the controls. + * + * @return array + */ + public function get_controls() { + return $this->controls; + } +} diff --git a/inc/admin/metabox/main.php b/inc/admin/metabox/main.php index 162c9130b8..16e6e3c543 100644 --- a/inc/admin/metabox/main.php +++ b/inc/admin/metabox/main.php @@ -12,41 +12,54 @@ * * @package Neve\Admin\Metabox */ -class Main extends Metabox_Base { +class Main extends Controls_Base { + + /** * Add controls. */ public function add_controls() { $this->add_layout_controls(); - $this->add_control( new Controls\Separator( 'neve_meta_separator', array() ) ); + $this->add_control( new Controls\Separator( 'neve_meta_separator', array( 'priority' => 20 ) ) ); $this->add_content_toggles(); - $this->add_control( new Controls\Separator( 'neve_meta_separator', array() ) ); + $this->add_control( new Controls\Separator( 'neve_meta_separator', array( 'priority' => 45 ) ) ); $this->add_content_width(); } /** - * Add content width control. + * Add layout controls. */ - private function add_content_width() { + private function add_layout_controls() { $this->add_control( - new Controls\Checkbox( - 'neve_meta_enable_content_width', + new Controls\Radio( + 'neve_meta_container', array( - 'default' => 'off', - 'label' => __( 'Content Width', 'neve' ) . ' (%)', - 'input_label' => __( 'Enable Individual Content Width', 'neve' ), + 'default' => 'default', + 'choices' => array( + 'default' => __( 'Customizer Setting', 'neve' ), + 'contained' => __( 'Contained', 'neve' ), + 'full-width' => __( 'Full Width', 'neve' ), + ), + 'label' => __( 'Container', 'neve' ), ) ) ); + + $position_default = 'default'; + $this->add_control( - new Controls\Range( - 'neve_meta_content_width', + new Controls\Radio( + 'neve_meta_sidebar', array( - 'default' => 70, - 'min' => 50, - 'max' => 100, - 'hidden' => $this->hide_content_width(), - 'depends_on' => 'neve_meta_enable_content_width', + 'default' => $position_default, + 'choices' => array( + 'default' => __( 'Customizer Setting', 'neve' ), + 'left' => __( 'Left Sidebar', 'neve' ), + 'right' => __( 'Right Sidebar', 'neve' ), + 'full-width' => __( 'No Sidebar', 'neve' ), + ), + 'label' => __( 'Sidebar', 'neve' ), + 'priority' => 15, ) ) ); @@ -61,20 +74,24 @@ private function add_content_toggles() { 'default' => 'off', 'label' => __( 'Components', 'neve' ), 'input_label' => __( 'Disable Header', 'neve' ), + 'priority' => 25, ), 'neve_meta_disable_title' => array( 'default' => 'off', 'input_label' => __( 'Disable Title', 'neve' ), 'active_callback' => array( $this, 'hide_on_single_product' ), + 'priority' => 30, ), 'neve_meta_disable_featured_image' => array( 'default' => 'off', 'input_label' => __( 'Disable Featured Image', 'neve' ), 'active_callback' => array( $this, 'hide_on_single_page_and_product' ), + 'priority' => 35, ), 'neve_meta_disable_footer' => array( 'default' => 'off', 'input_label' => __( 'Disable Footer', 'neve' ), + 'priority' => 40, ), ); @@ -83,6 +100,7 @@ private function add_content_toggles() { 'label' => '', 'input_label' => '', 'active_callback' => '__return_true', + 'priority' => 10, ); foreach ( $content_controls as $control_id => $args ) { @@ -96,6 +114,7 @@ private function add_content_toggles() { 'label' => $args['label'], 'input_label' => $args['input_label'], 'active_callback' => $args['active_callback'], + 'priority' => $args['priority'], ) ) ); @@ -103,35 +122,33 @@ private function add_content_toggles() { } /** - * Add layout controls. + * Add content width control. */ - private function add_layout_controls() { + private function add_content_width() { + $enabled_default = 'off'; + $width_default = self::is_post() ? 70 : 100; + $this->add_control( - new Controls\Radio( - 'neve_meta_container', + new Controls\Checkbox( + 'neve_meta_enable_content_width', array( - 'default' => 'default', - 'choices' => array( - 'default' => __( 'Customizer Setting', 'neve' ), - 'contained' => __( 'Contained', 'neve' ), - 'full-width' => __( 'Full Width', 'neve' ), - ), - 'label' => __( 'Container', 'neve' ), + 'default' => $enabled_default, + 'label' => __( 'Content Width', 'neve' ) . ' (%)', + 'input_label' => __( 'Enable Individual Content Width', 'neve' ), + 'priority' => 50, ) ) ); $this->add_control( - new Controls\Radio( - 'neve_meta_sidebar', + new Controls\Range( + 'neve_meta_content_width', array( - 'default' => 'default', - 'choices' => array( - 'default' => __( 'Customizer Setting', 'neve' ), - 'left' => __( 'Left Sidebar', 'neve' ), - 'right' => __( 'Right Sidebar', 'neve' ), - 'full-width' => __( 'No Sidebar', 'neve' ), - ), - 'label' => __( 'Sidebar', 'neve' ), + 'default' => $width_default, + 'min' => 50, + 'max' => 100, + 'hidden' => self::hide_content_width(), + 'depends_on' => 'neve_meta_enable_content_width', + 'priority' => 55, ) ) ); @@ -142,14 +159,22 @@ private function add_layout_controls() { * * @return bool */ - public function hide_content_width() { + public static function hide_content_width() { + if ( self::is_new_page() ) { + return false; + } + if ( ! isset( $_GET['post'] ) ) { return true; } - $meta = get_post_meta( $_GET['post'], 'neve_meta_enable_content_width', true ); + $meta = get_post_meta( (int) $_GET['post'], 'neve_meta_enable_content_width', true ); - if ( $meta !== 'on' ) { + if ( empty( $meta ) && self::is_checkout() ) { + return false; + } + + if ( empty( $meta ) || $meta === 'off' ) { return true; } @@ -170,7 +195,7 @@ public function hide_on_single_product() { return true; } - $post_type = get_post_type( $_GET['post'] ); + $post_type = get_post_type( (int) $_GET['post'] ); if ( $post_type !== 'product' ) { return true; @@ -193,7 +218,7 @@ public function hide_on_single_page_and_product() { return true; } - $post_type = get_post_type( $_GET['post'] ); + $post_type = get_post_type( (int) $_GET['post'] ); if ( $post_type !== 'page' && $post_type !== 'product' ) { return true; @@ -201,4 +226,65 @@ public function hide_on_single_page_and_product() { return false; } + + /** + * Check if we're adding a new post of type `page`. + * + * @return bool + */ + public static function is_new_page() { + global $pagenow; + + if ( $pagenow !== 'post-new.php' ) { + return false; + } + + if ( ! isset( $_GET['post_type'] ) ) { + return false; + } + if ( ( $_GET['post_type'] !== 'page' ) ) { + return false; + } + + return true; + } + + /** + * Check if is checkout. + */ + public static function is_checkout() { + if ( ! class_exists( 'WooCommerce', false ) ) { + return false; + } + if ( ! isset( $_GET['post'] ) ) { + return false; + } + if ( $_GET['post'] === get_option( 'woocommerce_checkout_page_id' ) ) { + return true; + } + + return false; + } + + /** + * Check if is post. + */ + public static function is_post() { + global $pagenow; + + // New post. + if ( $pagenow === 'post-new.php' && ! isset( $_GET['post_type'] ) ) { + return true; + } + + if ( ! isset( $_GET['post'] ) ) { + return false; + } + + if ( get_post_type( absint( $_GET['post'] ) ) === 'post' ) { + return true; + } + + return false; + } } diff --git a/inc/admin/metabox/manager.php b/inc/admin/metabox/manager.php new file mode 100755 index 0000000000..726483e379 --- /dev/null +++ b/inc/admin/metabox/manager.php @@ -0,0 +1,468 @@ +control_classes = array( + 'Neve\\Admin\\Metabox\\Main', + ); + + $this->control_classes = apply_filters( 'neve_filter_metabox_controls', $this->control_classes ); + } + + /** + * Instantiate the controls and actually load them into the control manager. + */ + public function load_controls() { + if ( empty( $this->control_classes ) ) { + return; + } + foreach ( $this->control_classes as $control_manager ) { + $control_instance = new $control_manager(); + if ( ! $control_instance instanceof Controls_Base ) { + continue; + } + + $control_instance->init(); + + $this->controls = array_merge( $this->controls, $control_instance->get_controls() ); + } + $this->order_by_priority(); + } + + /** + * The metabox content. + */ + public function render_controls() { + global $post; + + foreach ( $this->controls as $control ) { + if ( method_exists( $control, 'render' ) ) { + $control->render( $post->ID ); + } + } + } + + /** + * Save metabox content. + * + * @param int $post_id the post id. + */ + public function save( $post_id ) { + foreach ( $this->controls as $control ) { + if ( method_exists( $control, 'save' ) ) { + $control->save( $post_id ); + } + } + } + + /** + * Register meta box to control layout on pages and posts. + */ + public function add() { + $post_type = 'Neve'; + $post_type_from_db = get_post_type(); + if ( $post_type_from_db ) { + $post_type = ucfirst( $post_type_from_db ); + } + + add_meta_box( + 'neve-page-settings', + sprintf( + /* translators: %s - post type */ + __( '%s Settings', 'neve' ), + $post_type + ), + array( $this, 'render_metabox' ), + array( 'post', 'page', 'product' ), + 'side', + 'default', + array( + '__back_compat_meta_box' => true, + ) + ); + + if ( $this->is_gutenberg_active() ) { + add_meta_box( + 'neve-page-settings-notice', + sprintf( + /* translators: %s - post type */ + __( '%s Settings', 'neve' ), + $post_type + ), + array( $this, 'render_metabox_notice' ), + Supported_Post_Types::get( 'block_editor' ), + 'side', + 'default', + array( + '__back_compat_meta_box' => false, + ) + ); + } + } + + /** + * Detect if is gutenberg editor. + * + * @return bool + */ + private function is_gutenberg_active() { + global $current_screen; + if ( method_exists( $current_screen, 'is_block_editor' ) ) { + return $current_screen->is_block_editor(); + } + return false; + } + + /** + * The metabox content. + */ + public function render_metabox() { + $this->render_controls(); + } + + /** + * Render the metabox notice. + */ + public function render_metabox_notice() { + echo '
'; + echo '

' . esc_html__( 'Page Settings are now accessible from the top bar', 'neve' ) . '

'; + printf( + /* translators: %1$s - Keyboard shortcut. %2&s - svg icon */ + esc_html__( 'Click the %1$s icon in the top bar or use the keyboard shortcut ( %2$s ) to customise the layout settings for this page', 'neve' ), + apply_filters( 'ti_wl_theme_is_localized', false ) ? + '' : + ' + + + ', + 'SHIFT + ALT + S ' . esc_html__( 'or', 'neve' ) . ' control + option + S' + ); + echo '
'; + } + + /** + * Enqueue scripts and styles. + */ + public function enqueue() { + + if ( $this->is_gutenberg_active() ) { + return; + } + + $screen = get_current_screen(); + + if ( ! is_object( $screen ) ) { + return; + } + + if ( $screen->base !== 'post' ) { + return; + } + + wp_register_script( 'neve-metabox', NEVE_ASSETS_URL . 'js/build/all/metabox.js', array( 'jquery' ), NEVE_VERSION, true ); + + wp_localize_script( 'neve-metabox', 'neveMetabox', $this->get_localization() ); + + wp_enqueue_script( 'neve-metabox' ); + } + + /** + * Localize the Metabox script. + * + * @return array + */ + private function get_localization() { + return array(); + } + + /** + * Order the controls by given priority. + */ + private function order_by_priority() { + $order = array(); + + foreach ( $this->controls as $key => $control_object ) { + $order[ $key ] = $control_object->priority; + } + array_multisort( $order, SORT_ASC, $this->controls ); + } + + /** + * Register meta + */ + public function neve_register_meta() { + $meta_sidebar_controls = apply_filters( + 'neve_sidebar_meta_controls', + [ + [ + 'id' => 'neve_meta_sidebar', + 'type' => 'radio', + ], + [ + 'id' => 'neve_meta_container', + 'type' => 'button-group', + ], + [ + 'id' => 'neve_meta_enable_content_width', + 'type' => 'checkbox', + ], + [ + 'id' => 'neve_meta_content_width', + 'type' => 'range', + ], + [ + 'id' => 'neve_meta_title_alignment', + 'type' => 'button-group', + ], + [ + 'id' => 'neve_meta_author_avatar', + 'type' => 'checkbox', + ], + [ + 'id' => 'neve_post_elements_order', + 'type' => 'sortable-list', + ], + [ + 'id' => 'neve_meta_disable_header', + 'type' => 'checkbox', + ], + [ + 'id' => 'neve_meta_disable_footer', + 'type' => 'checkbox', + ], + [ + 'id' => 'neve_meta_disable_title', + 'type' => 'checkbox', + ], + ] + ); + foreach ( $meta_sidebar_controls as $control ) { + $type = 'string'; + if ( $control['type'] === 'range' ) { + $type = 'integer'; + } + + $post_type = ''; + if ( array_key_exists( 'post_type', $control ) ) { + $post_type = $control['post_type']; + } + + $meta_settings = array( + 'show_in_rest' => true, + 'type' => $type, + 'single' => true, + 'sanitize_callback' => 'sanitize_text_field', + 'auth_callback' => function () { + return current_user_can( 'edit_posts' ); + }, + ); + + register_post_meta( + $post_type, + $control['id'], + $meta_settings + ); + } + } + + /** + * Register the metabox sidebar. + */ + public function meta_sidebar_script_enqueue() { + global $post_type, $pagenow; + + $do_not_load_on = [ 'widgets.php', 'customize.php' ]; + + // $post_type returns "page" on widgets.php and on customize.php so we need to check this separately. + if ( in_array( $pagenow, $do_not_load_on, true ) || ! in_array( $post_type, Supported_Post_Types::get( 'block_editor' ) ) ) { + return false; + } + + $dependencies = ( include get_template_directory() . '/assets/apps/metabox/build/index.asset.php' ); + + wp_enqueue_script( + 'neve-meta-sidebar', + trailingslashit( get_template_directory_uri() ) . 'assets/apps/metabox/build/index.js', + $dependencies['dependencies'], + $dependencies['version'], + true + ); + + if ( function_exists( 'wp_set_script_translations' ) ) { + wp_set_script_translations( 'neve-meta-sidebar', 'neve' ); + } + + $container = $post_type === 'post' ? Mods::get( Config::MODS_SINGLE_POST_CONTAINER_STYLE, 'contained' ) : Mods::get( Config::MODS_DEFAULT_CONTAINER_STYLE, 'contained' ); + $editor_width = Mods::get( Config::MODS_CONTAINER_WIDTH ); + + $advanced_layout = Mods::get( Config::MODS_ADVANCED_LAYOUT_OPTIONS, true ); + + $single_width = $post_type === 'post' ? + Mods::get( Config::MODS_SINGLE_CONTENT_WIDTH, $this->sidebar_layout_width_default( Config::MODS_SINGLE_CONTENT_WIDTH ) ) : + Mods::get( Config::MODS_OTHERS_CONTENT_WIDTH, $this->sidebar_layout_width_default( Config::MODS_OTHERS_CONTENT_WIDTH ) ); + $content_width = $advanced_layout ? + $single_width : + Mods::get( Config::MODS_SITEWIDE_CONTENT_WIDTH, $this->sidebar_layout_width_default( Config::MODS_SITEWIDE_CONTENT_WIDTH ) ); + + $editor_width = isset( $editor_width['desktop'] ) ? (int) $editor_width['desktop'] : 1170; + + $post_elements_default_order = $this->get_post_elements_default_order(); + $show_avatar = $this->get_author_avatar_state(); + $reading_time = $this->get_reading_time_state(); + + $post_type_details = get_post_type_object( $post_type ); + $post_type_label = esc_html( $post_type_details->labels->singular_name ); + + $localized_data = apply_filters( + 'neve_meta_sidebar_localize_filter', + array( + 'actions' => array( + 'neve_meta_content_width' => array( + 'container' => $container, + 'editor' => $editor_width, + 'content' => $content_width, + ), + ), + 'elementsDefaultOrder' => $post_elements_default_order, + 'avatarDefaultState' => $show_avatar, + 'readingTimeDefaultState' => $reading_time, + 'postTypeLabel' => $post_type_label, + 'isCoverLayout' => Layout_Single_Post::is_cover_layout(), + ) + ); + wp_localize_script( + 'neve-meta-sidebar', + 'metaSidebar', + $localized_data + ); + + wp_enqueue_style( + 'neve-meta-sidebar-css', // Handle. + trailingslashit( get_template_directory_uri() ) . 'assets/apps/metabox/build/index.css', + array( 'wp-edit-blocks' ), + NEVE_VERSION + ); + } + + /** + * Get the value of elements order from customizer. + * + * @return string + */ + private function get_post_elements_default_order() { + $default_order = $this->post_ordering(); + + $content_order = get_theme_mod( 'neve_layout_single_post_elements_order', wp_json_encode( $default_order ) ); + if ( ! is_string( $content_order ) ) { + $content_order = wp_json_encode( $default_order ); + } + $content_order = json_decode( $content_order, true ); + if ( empty( $content_order ) ) { + return wp_json_encode( $content_order ); + } + + $is_cover_layout = Layout_Single_Post::is_cover_layout(); + $title_meta_index = array_search( 'title-meta', $content_order ); + if ( $title_meta_index !== false && ! $is_cover_layout ) { + $content_order[ $title_meta_index ] = 'title'; + $next_index = $title_meta_index + 1; + $content_order = array_merge( array_slice( $content_order, 0, $next_index, true ), array( 'meta' ), array_slice( $content_order, $next_index, null, true ) ); + } + + return wp_json_encode( $content_order ); + } + + /** + * Get the value of author avatar display from customizer. + * + * @return bool + */ + private function get_author_avatar_state() { + $show_avatar = get_theme_mod( 'neve_author_avatar', false ); + return get_theme_mod( 'neve_single_post_author_avatar', $show_avatar ); + } + + /** + * Get the value of Reading Time visibility from customizer. + * + * @return bool + */ + private function get_reading_time_state() { + $meta_fields = get_theme_mod( 'neve_single_post_meta_fields', self::get_default_single_post_meta_fields() ); + + if ( is_string( $meta_fields ) ) { + $meta_fields = json_decode( $meta_fields, true ); + } + + if ( ! is_array( $meta_fields ) ) { + return false; + } + + foreach ( $meta_fields as $args ) { + if ( ! array_key_exists( 'slug', $args ) || ! array_key_exists( 'visibility', $args ) || $args['slug'] !== 'reading' ) { + continue; + } + + return $args['visibility'] === 'yes'; + } + + return false; + } +} diff --git a/inc/admin/metabox/metabox_base.php b/inc/admin/metabox/metabox_base.php deleted file mode 100644 index 118d7bc8d5..0000000000 --- a/inc/admin/metabox/metabox_base.php +++ /dev/null @@ -1,143 +0,0 @@ -add_controls(); - } - - /** - * Add controls. - */ - abstract protected function add_controls(); - - /** - * Add the control. - * - * @param Controls\Control_Base $control the control object. - */ - public function add_control( $control ) { - array_push( $this->controls, $control ); - } - - /** - * Register meta box to control layout on pages and posts. - */ - public function add() { - - if ( $this->should_add_meta() === false ) { - return; - } - - add_meta_box( - 'neve-page-settings', - __( 'Neve Settings', 'neve' ), - array( $this, 'render_metabox' ), - array( 'post', 'page', 'product' ), - 'side' - ); - } - - /** - * The metabox content. - */ - public function render_metabox() { - global $post; - - foreach ( $this->controls as $control ) { - $control->render( $post->ID ); - } - } - - /** - * Save metabox content. - */ - public function save( $post_id ) { - foreach ( $this->controls as $control ) { - $control->save( $post_id ); - } - } - - /** - * Decide if the metabox should be visible. - * - * @return bool - */ - public function should_add_meta() { - global $post; - - if ( empty( $post ) ) { - return false; - } - - $restricted_pages_id = array(); - if ( in_array( $post->ID, $restricted_pages_id ) ) { - return false; - } - - return true; - } - - /** - * Enqueue scripts and styles. - */ - public function enqueue() { - - $screen = get_current_screen(); - - if ( ! is_object( $screen ) ) { - return; - } - if ( $screen->base !== 'post' ) { - return; - } - - wp_register_script( 'neve-metabox', NEVE_ASSETS_URL . 'js/metabox' . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.js', array( 'jquery' ), NEVE_VERSION, true ); - - wp_localize_script( 'neve-metabox', 'neveMetabox', $this->get_localization() ); - - wp_enqueue_script( 'neve-metabox' ); - } - - /** - * Localize the Metabox script. - * - * @return array - */ - private function get_localization() { - return array(); - } -} diff --git a/inc/admin/plugin_install/main.php b/inc/admin/plugin_install/main.php deleted file mode 100644 index 5772226582..0000000000 --- a/inc/admin/plugin_install/main.php +++ /dev/null @@ -1,182 +0,0 @@ -check_plugin_state( $slug ); - if ( empty( $slug ) ) { - return ''; - } - - $additional = ''; - - if ( $state === 'deactivate' ) { - $additional = ' action_button active'; - } - - $button .= '
'; - - $plugin_link_suffix = self::get_plugin_path( $slug ); - - $nonce = add_query_arg( - array( - 'action' => 'activate', - 'plugin' => rawurlencode( $plugin_link_suffix ), - 'plugin_status' => 'all', - 'paged' => '1', - '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $plugin_link_suffix ), - ), - esc_url( network_admin_url( 'plugins.php' ) ) - ); - switch ( $state ) { - case 'install': - $button .= '' . __( 'Install and activate', 'neve' ) . ''; - break; - - case 'activate': - $button .= '' . esc_html__( 'Activate', 'neve' ) . ''; - break; - - case 'deactivate': - $nonce = add_query_arg( - array( - 'action' => 'deactivate', - 'plugin' => rawurlencode( $plugin_link_suffix ), - 'plugin_status' => 'all', - 'paged' => '1', - '_wpnonce' => wp_create_nonce( 'deactivate-plugin_' . $plugin_link_suffix ), - ), - esc_url( network_admin_url( 'plugins.php' ) ) - ); - - $button .= '' . esc_html__( 'Deactivate', 'neve' ) . ''; - break; - - case 'enable_cpt': - $url = esc_url( admin_url( 'admin.php?page=jetpack#/settings' ) ); - $button .= '' . esc_html__( 'Activate', 'neve' ) . ' ' . esc_html__( 'Jetpack Portfolio', 'neve' ) . ''; - break; - }// End switch(). - $button .= '
'; - - return $button; - } - - /** - * Check plugin state. - * - * @param string $slug plugin slug. - * - * @return bool - */ - public function check_plugin_state( $slug ) { - - $plugin_link_suffix = self::get_plugin_path( $slug ); - - if ( file_exists( ABSPATH . 'wp-content/plugins/' . $plugin_link_suffix ) ) { - $needs = is_plugin_active( $plugin_link_suffix ) ? 'deactivate' : 'activate'; - if ( $needs === 'deactivate' && ! post_type_exists( 'portfolio' ) && $slug === 'jetpack' ) { - return 'enable_cpt'; - } - - return $needs; - } else { - return 'install'; - } - } - - /** - * Enqueue Function. - */ - public function enqueue_scripts() { - wp_register_script( 'neve-plugin-install', get_template_directory_uri() . '/inc/admin/plugin_install/plugin-install.js', array( 'jquery' ), NEVE_VERSION, true ); - - wp_localize_script( - 'neve-plugin-install', - 'nevePluginInstall', - array( - 'activating' => esc_html__( 'Activating ', 'neve' ), - ) - ); - - wp_enqueue_script( 'plugin-install' ); - wp_enqueue_script( 'updates' ); - wp_enqueue_script( 'neve-plugin-install' ); - } -} diff --git a/inc/admin/plugin_install/plugin-install.js b/inc/admin/plugin_install/plugin-install.js deleted file mode 100644 index 978de316cc..0000000000 --- a/inc/admin/plugin_install/plugin-install.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Remove activate button and replace with activation in progress button. - */ - -/* global nevePluginInstall */ -/* global console */ - -jQuery( document ).ready( - function ( $ ) { - $.pluginInstall = { - 'init': function () { - this.handleInstall(); - this.handleActivate(); - }, - - 'handleInstall': function () { - var self = this; - $( 'body' ).on( 'click', '.neve-install-plugin', function ( e ) { - e.preventDefault(); - var button = $( this ); - var slug = button.attr( 'data-slug' ); - var url = button.attr( 'href' ); - var redirect = $( button ).attr( 'data-redirect' ); - button.text( wp.updates.l10n.installing ); - button.addClass( 'updating-message' ); - wp.updates.installPlugin( - { - slug: slug, - success: function () { - button.text( nevePluginInstall.activating + '...' ); - self.activatePlugin( url, redirect ); - } - } - ); - } ); - }, - - 'activatePlugin': function ( url, redirect ) { - if ( typeof url === 'undefined' || !url ) { - return; - } - jQuery.ajax( - { - async: true, - type: 'GET', - url: url, - success: function () { - // Reload the page. - if ( typeof(redirect) !== 'undefined' && redirect !== '' ) { - window.location.replace( redirect ); - } else { - location.reload(); - } - }, - error: function ( jqXHR, exception ) { - var msg = ''; - if ( jqXHR.status === 0 ) { - msg = 'Not connect.\n Verify Network.'; - } else if ( jqXHR.status === 404 ) { - msg = 'Requested page not found. [404]'; - } else if ( jqXHR.status === 500 ) { - msg = 'Internal Server Error [500].'; - } else if ( exception === 'parsererror' ) { - msg = 'Requested JSON parse failed.'; - } else if ( exception === 'timeout' ) { - msg = 'Time out error.'; - } else if ( exception === 'abort' ) { - msg = 'Ajax request aborted.'; - } else { - msg = 'Uncaught Error.\n' + jqXHR.responseText; - } - console.log( msg ); - }, - } - ); - }, - - 'handleActivate': function () { - var self = this; - $( 'body' ).on( 'click', '.activate-now', function ( e ) { - e.preventDefault(); - var button = $( this ); - var url = button.attr( 'href' ); - var redirect = button.attr( 'data-redirect' ); - button.addClass( 'updating-message' ); - button.text( nevePluginInstall.activating + '...' ); - self.activatePlugin( url, redirect ); - } ); - }, - }; - $.pluginInstall.init(); - } -); \ No newline at end of file diff --git a/inc/admin/troubleshoot/main.php b/inc/admin/troubleshoot/main.php new file mode 100644 index 0000000000..9f030d615a --- /dev/null +++ b/inc/admin/troubleshoot/main.php @@ -0,0 +1,145 @@ + __( 'Neve', 'neve' ), + 'fields' => array( + 'api' => array( + 'label' => __( 'API connectivity', 'neve' ), + 'value' => $this->test_api_connectivity() ? __( 'Yes', 'neve' ) : __( 'No', 'neve' ) . ' ' . get_transient( 'neve_troubleshoot_api_reason' ), + 'private' => false, + ), + 'child' => array( + 'label' => __( 'Child theme files', 'neve' ), + 'value' => is_child_theme() ? $this->list_files() : __( 'No', 'neve' ), + 'private' => false, + ), + 'customizer_css' => array( + 'label' => __( 'Customizer Custom CSS', 'neve' ), + 'value' => empty( $custom_customizer_css ) ? __( 'No', 'neve' ) : $custom_customizer_css, + 'private' => false, + ), + ), + ); + + return $debug_info; + } + + /** + * List active theme files + * + * @return string + */ + public function list_files() { + return implode( ",\n\r", list_files( get_stylesheet_directory(), 2 ) ); + } + + /** + * Register tests for the Status Page + * + * @param array $tests List of tests. + * + * @return array + */ + public function neve_add_tests( $tests ) { + $tests['direct']['neve_api_test'] = array( + 'label' => __( 'Neve', 'neve' ) . ' ' . __( 'API connectivity', 'neve' ), + 'test' => [ $this, 'neve_api_test' ], + ); + + return $tests; + } + + /** + * Neve API test pretty response + * + * @return array + */ + public function neve_api_test() { + $result = array( + 'label' => __( 'Neve', 'neve' ) . ' ' . __( 'API connectivity', 'neve' ), + 'status' => 'good', + 'badge' => array( + 'label' => __( 'Neve', 'neve' ), + 'color' => 'blue', + ), + 'description' => sprintf( + '

%s

', + /* translators: Theme Name */ + sprintf( __( 'API for %s is reachable.', 'neve' ), __( 'Neve', 'neve' ) ) + ), + 'actions' => '', + 'test' => 'neve_api_test', + ); + + if ( ! $this->test_api_connectivity() ) { + $result['status'] = 'critical'; + $result['label'] = __( 'Can not connect to API', 'neve' ); + $result['badge']['color'] = 'red'; + $result['description'] = sprintf( + '

%s

', + /* translators: Theme Name */ + sprintf( __( 'API for %s is reachable on your site.', 'neve' ), __( 'Neve', 'neve' ) ) + ); + } + + return $result; + } + + /** + * Test API connectivity to Themeisle + * + * @return bool + */ + public function test_api_connectivity() { + $transient = get_transient( 'neve_troubleshoot_api_response' ); + if ( $transient !== false ) { + return ( $transient === 'yes' ); + } + $response = neve_safe_get( 'https://api.themeisle.com/health' ); + if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) { + $reason = is_wp_error( $response ) ? $response->get_error_message() : $response['response']['message']; + set_transient( 'neve_troubleshoot_api_reason', $reason, 10 * MINUTE_IN_SECONDS ); + set_transient( 'neve_troubleshoot_api_response', 'no', 10 * MINUTE_IN_SECONDS ); + + return false; + } + set_transient( 'neve_troubleshoot_api_reason', '', 10 * MINUTE_IN_SECONDS ); + set_transient( 'neve_troubleshoot_api_response', 'yes', 10 * MINUTE_IN_SECONDS ); + + return true; + } +} diff --git a/inc/compatibility/amp.php b/inc/compatibility/amp.php index ac3544f850..b8cb26eca1 100644 --- a/inc/compatibility/amp.php +++ b/inc/compatibility/amp.php @@ -20,11 +20,11 @@ class Amp { /** * Run the hooks and filters. */ - public function init() { - add_filter( 'neve_nav_data_attrs', array( $this, 'add_nav_attrs' ) ); - add_filter( 'neve_nav_toggle_data_attrs', array( $this, 'add_nav_toggle_attrs' ) ); + public function register_hooks() { + if ( ! neve_is_amp() ) { + return; + } add_filter( 'neve_caret_wrap_filter', array( $this, 'amp_dropdowns' ), 10, 2 ); - add_filter( 'neve_woocommerce_sidebar_filter_btn_data_attrs', array( @@ -41,57 +41,153 @@ public function init() { 10, 2 ); + add_filter( 'walker_nav_menu_start_el', array( $this, 'wrap_content' ), 10, 4 ); add_filter( 'neve_sidebar_data_attrs', array( $this, 'add_woo_sidebar_attrs' ), 10, 2 ); - add_action( 'wp_head', array( $this, 'render_amp_states' ) ); + add_filter( 'neve_search_menu_item_filter', array( $this, 'add_search_menu_item_attrs' ), 10, 2 ); + add_action( 'neve_after_header_hook', array( $this, 'render_amp_states' ) ); + add_filter( 'neve_nav_toggle_data_attrs', array( $this, 'add_nav_toggle_attrs' ) ); + add_action( 'wp_head', array( $this, 'inline_styles' ) ); + + + /** + * Add infinite scroll for amp. + */ + $this->maybe_add_amp_infinite_scroll(); } /** - * Add amp states to the dom. + * Add inline styles for AMP. + * + * @return void */ - public function render_amp_states() { + public function inline_styles() { + echo ' + '; + } + + /** + * Register amp bootstrap hook. + */ + public function init() { + add_action( 'wp', array( $this, 'register_hooks' ) ); + } + + /** + * Wrap the content of the menu items in case of AMP. + * + * @param string $item_output item markup. + * @param object $item item information. + * @param int $depth item depth. + * @param object $args menu args. + * + * @return string + */ + public function wrap_content( $item_output, $item, $depth, $args ) { if ( ! neve_is_amp() ) { - return; + return $item_output; } - echo ''; - echo ''; - echo ''; + if ( strpos( $args->menu_id, 'nv-primary-navigation' ) === false ) { + return $item_output; + } - echo ''; - echo ''; - echo ''; + if ( ! in_array( 'menu-item-has-children', $item->classes, true ) ) { + return $item_output; + } + + if ( strpos( $item_output, 'has-caret' ) > - 1 ) { + return $item_output; + } + + $caret = '
'; + $caret .= ''; + $caret .= '
'; + + $item_output = '
' . $item_output . $caret . '
'; + // Filter that is used for AMP proper event integration. + $item_output = apply_filters( 'neve_caret_wrap_filter', $item_output, $item->menu_order ); + + return $item_output; } /** - * Add navigation data attributes. + * Add amp parameters for menu child search icon. * - * @param string $input the data attrs already existing in the nav. + * @param string $input input string. + * @param int $instance instance number of nav menu. * * @return string */ - public function add_nav_attrs( $input ) { - if ( ! neve_is_amp() ) { - return $input; - } - $input .= ' [class]="( nvAmpMenuExpanded ? \'nv-navbar responsive-opened\' : \'nv-navbar\' )" '; - $input .= ' aria-expanded="false" [aria-expanded]="nvAmpMenuExpanded ? \'true\' : \'false\'" '; + public function add_search_menu_item_attrs( $input, $instance ) { + return $input . ' on="tap:nv-menu-item-search-' . $instance . '.toggleClass(class=\'active\')" '; + } - return $input; + /** + * Add amp states to the dom. + */ + public function render_amp_states() { + echo ''; + echo ''; + echo ''; + + echo ''; + echo ''; + echo ''; } /** * Add the nav toggle data attributes. * - * @param string $input the data attrs already existing in nav toggle. - * * @return string */ - public function add_nav_toggle_attrs( $input ) { - if ( ! neve_is_amp() ) { - return $input; - } - $input .= ' on="tap:AMP.setState( { nvAmpMenuExpanded: ! nvAmpMenuExpanded } )" '; - $input .= ' [class]="\'navbar-toggle\' + ( nvAmpMenuExpanded ? \' active\' : \'\' )" '; + public function add_nav_toggle_attrs( $input = '' ) { + $input = ' on="tap:neve_body.toggleClass(class=\'is-menu-sidebar\'),AMP.setState( { nvAmpMenuExpanded: ! nvAmpMenuExpanded } )" '; + $input .= ' role="button" '; $input .= ' aria-expanded="false" '; $input .= ' [aria-expanded]="nvAmpMenuExpanded ? \'true\' : \'false\'" '; @@ -106,11 +202,10 @@ public function add_nav_toggle_attrs( $input ) { * @return string */ public function add_woo_sidebar_filter_btn_attrs( $input ) { - if ( ! neve_is_amp() ) { - return $input; - } $input .= ' on="tap:AMP.setState( { nvAmpWooSidebarExpanded: true } )" '; + $input .= ' role="button" '; + $input .= ' '; return $input; } @@ -119,14 +214,11 @@ public function add_woo_sidebar_filter_btn_attrs( $input ) { * Add woo sidebar amp attrs. * * @param string $input input. - * @param string $slug sidebar slug. + * @param string $slug sidebar slug. * * @return string */ public function add_woo_sidebar_attrs( $input, $slug ) { - if ( ! neve_is_amp() ) { - return $input; - } if ( $slug !== 'shop-sidebar' ) { return $input; } @@ -141,18 +233,17 @@ public function add_woo_sidebar_attrs( $input, $slug ) { * Add amp attributes to sidebar close button. * * @param string $input empty string. - * @param string $slug sidebar slug. + * @param string $slug sidebar slug. * * @return string */ public function sidebar_close_button_attrs( $input, $slug ) { - if ( ! neve_is_amp() ) { - return $input; - } if ( $slug !== 'shop-sidebar' ) { return $input; } $input .= ' on="tap:AMP.setState( { nvAmpWooSidebarExpanded: false } )" '; + $input .= ' role="button" '; + $input .= ' '; return $input; } @@ -161,30 +252,190 @@ public function sidebar_close_button_attrs( $input, $slug ) { * Implement AMP integration on drop-downs. * * @param string $output the output. - * @param string $id menu item order. + * @param string $id menu item order. * * @return mixed */ public function amp_dropdowns( $output, $id ) { - // Bail if not AMP. - if ( ! neve_is_amp() ) { - return $output; - } // Generate a unique id for drop-down items. $state = 'neveMenuItemExpanded' . $id; - $attrs = ''; + $attrs = ''; + $amp_caret = ''; + $caret = ''; + + $attrs .= '
'; + $attrs .= ''; - $attrs .= ' class="caret-wrap"'; - $attrs .= ' [class]="\'caret-wrap\' + ( ' . $state . ' ? \' caret-dropdown-open\' : \'\')" '; - $attrs .= ' on="tap:AMP.setState( { ' . $state . ': ! ' . $state . ' } )"'; - $attrs .= ' aria-expanded="false" '; - $attrs .= ' [aria-expanded]="' . $state . ' ? \'true\' : \'false\'" '; + $amp_caret .= '
' . $caret . '
'; + $amp_caret .= '
' . $caret . '
'; - $output = str_replace( 'class="caret-wrap ' . $id . '"', $attrs, $output ); - $output = str_replace( '', '', $output ); + $output = str_replace( '
', $attrs, $output ); + $output = str_replace( $caret, $amp_caret, $output ); return $output; } + + /** + * Try to add amp infinite scroll. + * + * @return bool + */ + private function maybe_add_amp_infinite_scroll() { + + if ( ! $this->should_display_infinite_scroll() ) { + return false; + } + + add_action( 'wp_head', [ $this, 'add_amp_experiments' ], 1 ); + + remove_all_actions( 'neve_do_pagination' ); + add_action( 'neve_before_footer_hook', [ $this, 'wrap_footer_before' ] ); + add_action( 'neve_after_footer_hook', [ $this, 'wrap_footer_after' ] ); + + return true; + } + + /** + * Check if it's blog post index. + * + * @return bool + */ + private function is_blog_page() { + global $post; + + $post_type = get_post_type( $post ); + + return ( $post_type === 'post' ) && ( is_home() || is_archive() ); + } + + /** + * Decide if amp infinite scroll should work. + * + * @return bool + */ + public function should_display_infinite_scroll() { + + if ( ! $this->is_blog_page() ) { + return false; + } + if ( $this->blog_has_sidebar() ) { + return false; + } + + $pagination_type = get_theme_mod( 'neve_pagination_type', 'number' ); + if ( $pagination_type !== 'infinite' ) { + return false; + } + + $has_pagination = ! empty( get_the_posts_pagination() ); + if ( ! $has_pagination ) { + return false; + } + + return true; + } + + /** + * Amp experiments for infinite scroll feature. + */ + public function add_amp_experiments() { + echo ''; + } + + /** + * Check if blog has sidebar. + * + * @return bool + */ + public function blog_has_sidebar() { + $option = 'neve_default_sidebar_layout'; + $advanced_options = get_theme_mod( 'neve_advanced_layout_options', false ); + if ( $advanced_options !== false ) { + $option = 'neve_blog_archive_sidebar_layout'; + } + + return apply_filters( 'neve_sidebar_position', get_theme_mod( $option, 'right' ) ) !== 'full-width'; + } + + /** + * Before footer pagination tags. + */ + public function wrap_footer_before() { + $amp_pagination_data = $this->get_amp_pagination_data(); + + if ( ! is_paged() ) { + echo ''; + echo ''; + echo '
'; + } else { + $links = paginate_links( array( 'type' => 'list' ) ); + $links = str_replace( + array( '
#s', $page, $matches ); + + if ( empty( $matches ) ) { + continue; + } + + $url = html_entity_decode( $matches[1] ); + $page = html_entity_decode( $matches[2] ); + $image = get_site_icon_url(); + + $amp_pagination[] = [ + 'url' => $url, + 'title' => get_bloginfo( 'name' ) . ' - ' . __( 'Page', 'neve' ) . ' ' . $page . ' - ' . get_bloginfo( 'description' ), + 'image' => $image, + ]; + + } + + return $amp_pagination; + } } diff --git a/inc/compatibility/beaver.php b/inc/compatibility/beaver.php index d885df7bf5..2ac5360d19 100644 --- a/inc/compatibility/beaver.php +++ b/inc/compatibility/beaver.php @@ -9,46 +9,61 @@ namespace Neve\Compatibility; +use Neve\Core\Settings\Config; +use Neve\Core\Settings\Mods; + /** * Class Bever * * @package Neve\Compatibility */ -class Beaver extends Page_Builder_Base { +class Beaver extends Page_Builder_Base { /** * Init function. */ public function init() { - if ( ! defined( 'FL_THEME_BUILDER_VERSION' ) ) { - return; + if ( defined( 'FL_BUILDER_VERSION' ) ) { + add_filter( 'fl_builder_color_presets', array( $this, 'global_color_presets' ) ); } - add_action( 'wp', array( $this, 'add_theme_builder_hooks' ) ); + if ( defined( 'FL_THEME_BUILDER_VERSION' ) ) { + add_action( 'wp', array( $this, 'add_theme_builder_hooks' ) ); + add_filter( 'fl_theme_builder_part_hooks', array( $this, 'register_part_hooks' ) ); + } } /** * Check if it page was edited with page builder. * - * @param string $pid post id. + * @param int $pid post id. * * @return bool */ protected function is_edited_with_builder( $pid ) { - if ( class_exists( '\FLBuilderModel' ) ) { + if ( class_exists( '\FLBuilderModel', false ) ) { return \FLBuilderModel::is_builder_enabled( $pid ); } + return false; } + /** + * Load Beaver compatibility style. + */ + public function load_style() { + wp_add_inline_style( 'neve-style', '.fl-builder.bbhf-transparent-header:not(.bhf-sticky-header) #nv-beaver-header .fl-row-content-wrap{background-color:transparent;border:none;transition:background-color .3s ease-in-out}.fl-builder.bbhf-transparent-header .bhf-fixed-header:not(.bhf-fixed) .fl-row-content-wrap{background-color:transparent;border:none;transition:background-color .3s ease-in-out}.fl-builder.bbhf-transparent-header #nv-beaver-header{position:absolute;z-index:10;width:100%}' ); + } + /** * Add support for elementor theme locations. */ public function add_theme_builder_hooks() { - if ( ! class_exists( '\FLThemeBuilderLayoutData' ) ) { + if ( ! class_exists( '\FLThemeBuilderLayoutData', false ) ) { return; } + add_action( 'wp_enqueue_scripts', [ $this, 'load_style' ] ); // Get the header ID. $header_ids = \FLThemeBuilderLayoutData::get_current_page_header_ids(); @@ -69,4 +84,95 @@ public function add_theme_builder_hooks() { } } + + /** + * Beautify hook names. + * + * @param string $hook Hook name. + * + * @return string + */ + private function beautify_hook( $hook ) { + $hook_label = str_replace( '_', ' ', $hook ); + $hook_label = str_replace( 'neve', ' ', $hook_label ); + $hook_label = str_replace( 'woocommerce', ' ', $hook_label ); + $hook_label = ucwords( $hook_label ); + return $hook_label; + } + + /** + * Mapping function to move from neve_hooks format to the format required by Beaver Builder. + * + * @param string $location Current location, the key of neve_hooks array. + * @param array $hooks Hooks from that location. + * + * @return array + */ + private function hook_to_part( $location, $hooks ) { + $part = array( + 'label' => ucfirst( $location ), + ); + foreach ( $hooks as $hook ) { + $part['hooks'][ $hook ] = $this->beautify_hook( $hook ); + } + return $part; + } + + /** + * Register part hooks for Beaver Themer. + * + * @return array + */ + public function register_part_hooks() { + $hooks = neve_hooks(); + return array_map( array( $this, 'hook_to_part' ), array_keys( $hooks ), $hooks ); + } + + /** + * Adds global colors from neve to Beaver Builder color presets. + * + * @param array $colors Color presets. + * + * @return array + */ + public function global_color_presets( $colors ) { + + $global_colors = get_theme_mod( 'neve_global_colors', neve_get_global_colors_default( true ) ); + + if ( empty( $global_colors ) ) { + return $colors; + } + + if ( ! isset( $global_colors['activePalette'] ) ) { + return $colors; + } + + $active = $global_colors['activePalette']; + + if ( ! isset( $global_colors['palettes'][ $active ] ) ) { + return $colors; + } + + $palette = $global_colors['palettes'][ $active ]; + + if ( ! isset( $palette['colors'] ) ) { + return $colors; + } + + $palette_colors = array_values( $palette['colors'] ); + + $global_custom_colors = Mods::get( Config::MODS_GLOBAL_CUSTOM_COLORS, [] ); + + foreach ( $global_custom_colors as $args ) { + $palette_colors[] = $args['val']; + } + + foreach ( $palette_colors as $color ) { + if ( ! array_search( $color, $colors, true ) ) { + $colors[] = str_replace( '#', '', $color ); + } + } + + return array_values( array_unique( $colors ) ); + } } diff --git a/inc/compatibility/block-patterns/dark-header-centered-content.php b/inc/compatibility/block-patterns/dark-header-centered-content.php new file mode 100644 index 0000000000..bdb582bb7a --- /dev/null +++ b/inc/compatibility/block-patterns/dark-header-centered-content.php @@ -0,0 +1,32 @@ + __( 'Dark header with centered content', 'neve' ), + 'content' => ' +
+

Welcome to Neve.This is a hero section.

+ + + +

This is a description text of the cover

+ + + + + + + + +
+', + 'categories' => array( 'header' ), +); diff --git a/inc/compatibility/block-patterns/four-columns-team-members.php b/inc/compatibility/block-patterns/four-columns-team-members.php new file mode 100644 index 0000000000..1b9be57f65 --- /dev/null +++ b/inc/compatibility/block-patterns/four-columns-team-members.php @@ -0,0 +1,116 @@ + __( 'Four columns with team members', 'neve' ), + 'content' => ' +
+
+
+ + + +

Tim Jerris

+ + + +

Founder

+ + + + + + + + +
+ + + +
+
+ + + +

Erica Browser

+ + + +

Client Service

+ + + + + + + + +
+ + + +
+
+ + + +

Jack Nolston

+ + + +

CFO

+ + + + + + + + +
+ + + +
+
+ + + +

Jane Austin

+ + + +

Marketing

+ + + + + + + + +
+
+', + 'categories' => array( 'columns' ), +); diff --git a/inc/compatibility/block-patterns/gallery-grid-buttons.php b/inc/compatibility/block-patterns/gallery-grid-buttons.php new file mode 100644 index 0000000000..d48b7877d1 --- /dev/null +++ b/inc/compatibility/block-patterns/gallery-grid-buttons.php @@ -0,0 +1,26 @@ + __( 'Gallery grid with buttons', 'neve' ), + 'content' => ' + +', + 'categories' => array( 'gallery' ), +); diff --git a/inc/compatibility/block-patterns/gallery-title-buttons.php b/inc/compatibility/block-patterns/gallery-title-buttons.php new file mode 100644 index 0000000000..057a39d1af --- /dev/null +++ b/inc/compatibility/block-patterns/gallery-title-buttons.php @@ -0,0 +1,34 @@ + __( 'Gallery with title and button', 'neve' ), + 'content' => ' +
+
+
+

Gallery with title and button

+ +
+ + + + +
+ + + + +
+', + 'categories' => array( 'gallery' ), +); diff --git a/inc/compatibility/block-patterns/light-header-left-aligned-content.php b/inc/compatibility/block-patterns/light-header-left-aligned-content.php new file mode 100644 index 0000000000..3b1d9e3ee3 --- /dev/null +++ b/inc/compatibility/block-patterns/light-header-left-aligned-content.php @@ -0,0 +1,34 @@ + __( 'Light header with left-aligned content', 'neve' ), + 'content' => ' +
+
+
+

A Hero section over a light background

+ + + +

The quick brown fox jumps over the lazy dog" is an English-language pangram

+ + + + +
+ + + +
+
+
+', + 'categories' => array( 'header' ), +); diff --git a/inc/compatibility/block-patterns/testimonials-columns.php b/inc/compatibility/block-patterns/testimonials-columns.php new file mode 100644 index 0000000000..0ee67a5fdb --- /dev/null +++ b/inc/compatibility/block-patterns/testimonials-columns.php @@ -0,0 +1,64 @@ + __( 'Testimonial columns', 'neve' ), + 'content' => ' +
+

Testimonials

+ + + +
+ + + +
+
+
+ + + +

Jason Stobbard

+ + + +

"...Absolutely fantastic work, many thanks for the perfect collaboration so far, very much appreciated!..."

+
+ + + +
+
+ + + +

Jane Austin

+ + + +

"... I love this team! They did fantastic work, many thanks for the perfect collaboration so far, very much appreciated!..."

+
+ + + +
+
+ + + +

Jason Stobbard

+ + + +

"...Absolutely fantastic work, many thanks for the perfect collaboration so far, very much appreciated!..."

+
+
+
+', + 'categories' => array( 'columns' ), +); diff --git a/inc/compatibility/block-patterns/three-columns-images-text.php b/inc/compatibility/block-patterns/three-columns-images-text.php new file mode 100644 index 0000000000..bcb0b1a078 --- /dev/null +++ b/inc/compatibility/block-patterns/three-columns-images-text.php @@ -0,0 +1,34 @@ + __( 'Three columns with images and text', 'neve' ), + 'content' => ' +
+
+
+
+ + + +
+
+
+ + + +
+

The quick brown fox jumps over the lazy dog"

+ + + +

Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter.

+
+
+', + 'categories' => array( 'columns' ), +); diff --git a/inc/compatibility/block-patterns/three-columns-images-texts-content.php b/inc/compatibility/block-patterns/three-columns-images-texts-content.php new file mode 100644 index 0000000000..9367a85f8a --- /dev/null +++ b/inc/compatibility/block-patterns/three-columns-images-texts-content.php @@ -0,0 +1,84 @@ + __( 'Three columns with images, content and buttons', 'neve' ), + 'content' => ' +
+
+
+ + + +

Heading three

+ + + +

Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs.

+ + + + + + + + +
+ + + +
+
+ + + +

Heading three

+ + + +

The passage is attributed to an unknown typesetter in the 15th century who is thought to have scrambled parts.

+ + + + + + + + +
+ + + +
+
+ + + +

Heading three

+ + + +

Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs.

+ + + + + + + + +
+
+', + 'categories' => array( 'columns' ), +); diff --git a/inc/compatibility/block-patterns/two-columns-centered-content.php b/inc/compatibility/block-patterns/two-columns-centered-content.php new file mode 100644 index 0000000000..974a493d1e --- /dev/null +++ b/inc/compatibility/block-patterns/two-columns-centered-content.php @@ -0,0 +1,48 @@ + __( 'Two columns with centered content', 'neve' ), + 'content' => ' +
+
+
+ + + +

Heading three

+ + + +
+ + + +

Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs.

+
+ + + +
+
+ + + +

Heading three

+ + + +
+ + + +

The passage is attributed to an unknown typesetter in the 15th century who is thought to have scrambled parts.

+
+
+', + 'categories' => array( 'columns' ), +); diff --git a/inc/compatibility/block-patterns/two-columns-image-text.php b/inc/compatibility/block-patterns/two-columns-image-text.php new file mode 100644 index 0000000000..33aee4f1eb --- /dev/null +++ b/inc/compatibility/block-patterns/two-columns-image-text.php @@ -0,0 +1,34 @@ + __( 'Two columns with image and text', 'neve' ), + 'content' => ' +
+
+
+
+ + + +
+

The quick brown fox jumps over the lazy dog"

+ + + +

Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter in the 15th century who is thought to have scrambled parts of Cicero\'s De Finibus Bonorum et Malorum for use in a type specimen book.

+ + + + +
+
+', + 'categories' => array( 'columns' ), +); diff --git a/inc/compatibility/block-patterns/two-columns-with-text.php b/inc/compatibility/block-patterns/two-columns-with-text.php new file mode 100644 index 0000000000..93077ae299 --- /dev/null +++ b/inc/compatibility/block-patterns/two-columns-with-text.php @@ -0,0 +1,44 @@ + __( 'Two columns with text', 'neve' ), + 'content' => ' +
+
+

This is a heading on the left, more text on the right!

+ + + +

Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter.

+ + + +

Learn More

+
+ + + +
+

This is Heading 3

+ + + +

Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter.

+ + + +

This is Heading 3

+ + + +

Lorem ipsum, or lipsum as it is sometimes known, is dummy text used in laying out print, graphic or web designs. The passage is attributed to an unknown typesetter.

+
+
+', + 'categories' => array( 'text' ), +); diff --git a/inc/compatibility/easy_digital_downloads.php b/inc/compatibility/easy_digital_downloads.php new file mode 100644 index 0000000000..69491ce71d --- /dev/null +++ b/inc/compatibility/easy_digital_downloads.php @@ -0,0 +1,93 @@ +' ) ) { + $edd_settings_filter = 'edd_settings_styles'; + } + add_filter( $edd_settings_filter, array( $this, 'edd_settings_styles' ) ); + add_filter( 'body_class', array( $this, 'add_body_class' ) ); + } + + /** + * Add neve easy digital downloads body class. + * + * @param array $classes Current classes on body. + */ + public function add_body_class( $classes ) { + + if ( edd_is_checkout() || + edd_is_success_page() || + edd_is_failed_transaction_page() || + edd_is_purchase_history_page() || + is_post_type_archive( 'download' ) || + get_post_type() == 'download' || + is_tax( 'download_category' ) || + is_tax( 'download_tag' ) + ) { + $classes[] = 'nv-edd'; + } + + return $classes; + + } + + /** + * Dequeue the EDD default styles as we have our own. + * + * @return void + */ + public function dequeue_edd_styles() { + wp_dequeue_style( 'edd-styles' ); + } + + /** + * Filter the settings from EDD's "Styles" tab + * + * @param mixed $settings EDD style settings. + * @return array + */ + public function edd_settings_styles( $settings ) { + /* + * Settings with type 'descriptive_text' are automatically stripped by EDD + * So this field is not saved to the DB on save changes. + * + * see edd_settings_sanitize() + */ + $settings['main'] = array( + 'neve_controlled' => array( + 'id' => 'neve_controlled', + 'name' => esc_html__( 'Controlled by Neve', 'neve' ), + 'desc' => esc_html__( 'Neve Theme controls base style settings of Easy Digital Downloads. Additional settings from other extensions might appear below.', 'neve' ), + 'type' => 'descriptive_text', + ), + ); + + return $settings; + } + +} diff --git a/inc/compatibility/elementor.php b/inc/compatibility/elementor.php index 5c66feed73..0445b80960 100644 --- a/inc/compatibility/elementor.php +++ b/inc/compatibility/elementor.php @@ -8,12 +8,52 @@ namespace Neve\Compatibility; +use Neve\Core\Dynamic_Css; +use Neve\Core\Settings\Config; +use Neve\Core\Settings\Mods; + /** * Class Elementor * * @package Neve\Compatibility */ class Elementor extends Page_Builder_Base { + /** + * Stores Elementor templates meta + * + * @var array + */ + const ELEMENTOR_TEMPLATE_TYPES = [ + 'single_product' => [ + 'location' => 'single', + 'condition_indicator' => 'product', + ], + 'product_archive' => [ + 'location' => 'archive', + 'condition_indicator' => 'product_archive', + ], + ]; + + /** + * Stores if the current page is overriden by Elementor or not (checks by ::is_elementor_template method) according to the location. + * + * @var array + */ + private static $cache_cp_has_template = []; + + /** + * Stores Elementor Pro Conditions_Manager instance. + * + * @var false|\ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager + */ + private static $elementor_conditions_manager = false; + + /** + * Custom global colors theme mod value + * + * @var array|null + */ + private static $custom_global_colors = null; /** * Init function. @@ -22,32 +62,244 @@ public function init() { if ( ! defined( 'ELEMENTOR_VERSION' ) ) { return; } - $this->add_theme_builder_hooks(); + self::$custom_global_colors = self::$custom_global_colors ?? Mods::get( Config::MODS_GLOBAL_CUSTOM_COLORS, [] ); + + add_filter( 'neve_dynamic_style_output', array( $this, 'fix_links' ), 99, 2 ); + add_action( 'wp', array( $this, 'add_theme_builder_hooks' ) ); add_action( 'elementor/editor/before_enqueue_scripts', array( $this, 'maybe_set_page_template' ), 1 ); + add_filter( 'rest_request_after_callbacks', [ $this, 'alter_global_colors_in_picker' ], 999, 3 ); + add_filter( 'rest_request_after_callbacks', [ $this, 'alter_global_colors_front_end' ], 999, 3 ); + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue' ), 100 ); + /** + * Elementor - Neve Pro Compatibility + * add_filter call for "neve_pro_run_wc_view" hook. + * + * The callback, suspenses some WooCommerce modifications (especially customizer support) by Neve Pro if an Elementor template is applied on the current page. + * That gives full capability to Elementor and removes Neve Pro customizations. + */ + add_filter( 'neve_pro_run_wc_view', array( $this, 'suspend_woo_customizations' ), 10, 2 ); + } + + /** + * Enqueue Global Colors + */ + public function enqueue() { + $colors = $this->get_current_palette_colors(); + $css = ':root{'; + foreach ( $colors as $slug => $color ) { + $css .= '--e-global-color-' . str_replace( '-', '', $slug ) . ':' . $color . ';'; + } + $css .= '}'; + /** + * Filters the css with base vars for elementor colors. + * + * @param string $css Single post page components. + * + * @since 3.1.0 + */ + $css = apply_filters( 'neve_elementor_colors', $css ); + $css = Dynamic_Css::minify_css( $css ); + wp_add_inline_style( 'neve-style', $css ); + } + + /** + * Filter rest responses to add Neve Palette Colors to pages using Elementor. + * + * @param \WP_REST_Response $response request response. + * @param array $handler request handler. + * @param \WP_REST_Request $request rest request. + * @return \WP_REST_Response + */ + public function alter_global_colors_front_end( $response, $handler, \WP_REST_Request $request ) { + $route = $request->get_route(); + $rest_to_slugs = [ + 'nvprimaryaccent' => 'nv-primary-accent', + 'nvsecondaryaccent' => 'nv-secondary-accent', + 'nvsitebg' => 'nv-site-bg', + 'nvlightbg' => 'nv-light-bg', + 'nvdarkbg' => 'nv-dark-bg', + 'nvtextcolor' => 'nv-text-color', + 'nvtextdarkbg' => 'nv-text-dark-bg', + 'nvc1' => 'nv-c-1', + 'nvc2' => 'nv-c-2', + ]; + + // introduce custom global colors + foreach ( array_keys( self::$custom_global_colors ) as $slug ) { + $rest_to_slugs[ str_replace( '-', '', $slug ) ] = $slug; + } + + $rest_id = substr( $route, strrpos( $route, '/' ) + 1 ); + + if ( ! in_array( $rest_id, array_keys( $rest_to_slugs ), true ) ) { + return $response; + } + + $colors = $this->get_current_palette_colors(); + $response = new \WP_REST_Response( + [ + 'id' => esc_attr( $rest_id ), + 'title' => $this->get_global_color_prefix() . esc_html( $rest_to_slugs[ $rest_id ] ), + 'value' => neve_sanitize_colors( $colors[ $rest_to_slugs[ $rest_id ] ] ), + ] + ); + return $response; + } + + /** + * Filter rest responses to add Neve Palette Colors to Elementor. + * + * @param \WP_REST_Response $response request response. + * @param array $handler request handler. + * @param \WP_REST_Request $request rest request. + * @return \WP_REST_Response + */ + public function alter_global_colors_in_picker( $response, $handler, \WP_REST_Request $request ) { + $route = $request->get_route(); + + if ( $route !== '/elementor/v1/globals' ) { + return $response; + } + + $label_map = [ + 'nv-primary-accent' => __( 'Primary Accent', 'neve' ), + 'nv-secondary-accent' => __( 'Secondary Accent', 'neve' ), + 'nv-site-bg' => __( 'Site Background', 'neve' ), + 'nv-light-bg' => __( 'Light Background', 'neve' ), + 'nv-dark-bg' => __( 'Dark Background', 'neve' ), + 'nv-text-color' => __( 'Text Color', 'neve' ), + 'nv-text-dark-bg' => __( 'Text Dark Background', 'neve' ), + 'nv-c-1' => __( 'Extra Color 1', 'neve' ), + 'nv-c-2' => __( 'Extra Color 2', 'neve' ), + ]; + + foreach ( self::$custom_global_colors as $slug => $args ) { + $label_map[ $slug ] = $args['label']; + } + + $colors = $this->get_current_palette_colors(); + $data = $response->get_data(); + + foreach ( $colors as $slug => $color_value ) { + $no_hyphens = str_replace( '-', '', $slug ); + $data['colors'][ $no_hyphens ] = [ + 'id' => esc_attr( $no_hyphens ), + 'title' => $this->get_global_color_prefix() . esc_html( $label_map[ $slug ] ), + 'value' => neve_sanitize_colors( $color_value ), + ]; + } + + $response->set_data( $data ); + + return $response; } /** * Add support for elementor theme locations. */ - private function add_theme_builder_hooks() { - if ( ! class_exists( '\ElementorPro\Modules\ThemeBuilder\Module' ) ) { + public function add_theme_builder_hooks() { + if ( ! class_exists( '\ElementorPro\Modules\ThemeBuilder\Module', false ) ) { return; } - // Elementor locations compatibility. + // Elementor locations compatibility. (This action fires by Elementor Pro) add_action( 'elementor/theme/register_locations', array( $this, 'register_theme_locations' ) ); + if ( ! function_exists( 'elementor_theme_do_location' ) ) { + return; + } + // Override theme templates. + add_action( 'neve_do_top_bar', array( $this, 'do_header' ), 0 ); add_action( 'neve_do_header', array( $this, 'do_header' ), 0 ); add_action( 'neve_do_footer', array( $this, 'do_footer' ), 0 ); + add_action( 'neve_do_404', array( $this, 'do_404' ), 0 ); + add_action( 'neve_do_single_post', array( $this, 'do_single_post' ), 0 ); + add_action( 'neve_do_single_page', array( $this, 'do_single_page' ), 0 ); + add_action( 'neve_page_header', array( $this, 'remove_header_on_page' ), 0 ); + } + + /** + * Register Theme Location for Elementor + * see https://developers.elementor.com/theme-locations-api/ + * + * @param \ElementorPro\Modules\ThemeBuilder\Classes\Locations_Manager $manager Elementor object. + */ + public function register_theme_locations( $manager ) { + $manager->register_all_core_location(); + } + + /** + * Remove actions for elementor header to act properly. + */ + public function do_header() { + $did_location = elementor_theme_do_location( 'header' ); + if ( $did_location ) { + remove_all_actions( 'neve_do_top_bar' ); + remove_all_actions( 'neve_do_header' ); + } + } + /** + * Remove actions for elementor footer to act properly. + */ + public function do_footer() { + $did_location = elementor_theme_do_location( 'footer' ); + if ( $did_location ) { + remove_all_actions( 'neve_do_footer' ); + } + } + + /** + * Remove actions for elementor 404 to act properly. + */ + public function do_404() { + if ( ! is_404() ) { + return; + } + $did_location = elementor_theme_do_location( 'single' ); + if ( $did_location ) { + remove_all_actions( 'neve_do_404' ); + } + } + + /** + * Remove actions for elementor single post to act properly. + */ + public function do_single_post() { + $did_location = elementor_theme_do_location( 'single' ); + if ( $did_location ) { + remove_all_actions( 'neve_do_single_post' ); + } + } + + /** + * Remove actions for elementor single page to act properly. + */ + public function do_single_page() { + $did_location = elementor_theme_do_location( 'single' ); + if ( $did_location ) { + remove_all_actions( 'neve_do_single_page' ); + } + } + + /** + * Remove title on single page. + */ + public function remove_header_on_page() { + if ( ! is_singular( 'page' ) ) { + return; + } + if ( elementor_theme_do_location( 'single' ) ) { + remove_all_actions( 'neve_page_header' ); + } } /** * Check if it page was edited with page builder. * - * @param string $pid post id. + * @param int $pid post id. * * @return bool */ @@ -61,32 +313,231 @@ protected function is_edited_with_builder( $pid ) { } /** - * Register Theme Location for Elementor - * see https://developers.elementor.com/theme-locations-api/ + * Fix the underline of links added by neve. * - * @param \ElementorPro\Modules\ThemeBuilder\Classes\Locations_Manager $manager Elementor object. + * @param string $css Current css. + * @param string $context Context. + * + * @return string */ - public function register_theme_locations( $manager ) { - $manager->register_all_core_location(); + public function fix_links( $css, $context = 'frontend' ) { + if ( $context !== 'frontend' ) { + return $css; + } + + return $css . '.nv-content-wrap .elementor a:not(.button):not(.wp-block-file__button){ + text-decoration: none; + }'; } /** - * Remove actions for elementor header to act properly. + * Get current palette colors. + * + * @return array */ - public function do_header() { - $did_location = \ElementorPro\Modules\ThemeBuilder\Module::instance()->get_locations_manager()->do_location( 'header' ); - if ( $did_location ) { - remove_all_actions( 'neve_do_header' ); + private function get_current_palette_colors() { + $customizer = get_theme_mod( 'neve_global_colors', neve_get_global_colors_default( true ) ); + $active = $customizer['activePalette']; + $palettes = $customizer['palettes']; + $palette = $palettes[ $active ]; + $colors = $palette['colors']; + + foreach ( self::$custom_global_colors as $slug => $args ) { + $colors[ $slug ] = $args['val']; } + + return $colors; } /** - * Remove actions for elementor footer to act properly. + * Get the global colors prefix. + * + * @return string */ - public function do_footer() { - $did_location = \ElementorPro\Modules\ThemeBuilder\Module::instance()->get_locations_manager()->do_location( 'footer' ); - if ( $did_location ) { - remove_all_actions( 'neve_do_footer' ); + private function get_global_color_prefix() { + return ( apply_filters( 'ti_wl_theme_is_localized', false ) ? __( 'Theme', 'neve' ) : 'Neve' ) . ' - '; + } + + /** + * Returns Condition_Manager instance of the Elementor Pro. + * + * @return false|\ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager + */ + private static function get_condition_manager() { + if ( self::$elementor_conditions_manager !== false ) { + return self::$elementor_conditions_manager; + } + + if ( ! method_exists( '\ElementorPro\Modules\ThemeBuilder\Module', 'instance' ) ) { + return false; + } + + $theme_builder = \ElementorPro\Modules\ThemeBuilder\Module::instance(); + + if ( ! method_exists( $theme_builder, 'get_conditions_manager' ) ) { + return false; + } + + self::$elementor_conditions_manager = $theme_builder->get_conditions_manager(); + return self::$elementor_conditions_manager; + } + + /** + * Checks if the site has Elementor template as independent from current post ID. + * The method was designed to use in customizer. ! Do not use it outside of the customizer. + * + * @param string $elementor_template_type valid types: single_product|product_archive (keys of the self::ELEMENTOR_TEMPLATE_TYPES const array). + * @return bool + */ + public static function has_template( $elementor_template_type ) { + if ( ! class_exists( '\ElementorPro\Plugin', false ) ) { + return false; + } + + if ( ! array_key_exists( $elementor_template_type, self::ELEMENTOR_TEMPLATE_TYPES ) ) { + return false; + } + + $template_meta = self::ELEMENTOR_TEMPLATE_TYPES[ $elementor_template_type ]; + + $location = $template_meta['location']; + $has_indicator = $template_meta['condition_indicator']; // represents second path of the Elementor condition + + /** + * @var \ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager $conditions_manager + */ + $conditions_manager = self::get_condition_manager(); + + if ( ! is_object( $conditions_manager ) || ! method_exists( $conditions_manager, 'get_cache' ) ) { + return false; + } + + /** + * @var \ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Cache $instance_cond_cache + */ + $instance_cond_cache = $conditions_manager->get_cache(); + + if ( ! method_exists( $instance_cond_cache, 'get_by_location' ) ) { + return false; } + + $templates = $instance_cond_cache->get_by_location( $location ); + + foreach ( $templates as $template_conditions_arr ) { + /** @var string $condition_path specifies the condition such as include/product_archive OR exclude/product_archive/product_search OR include/product/in_product_cat/18 etc. */ + foreach ( $template_conditions_arr as $condition_path ) { + $condition_parts = explode( '/', $condition_path ); + + if ( $condition_parts[0] !== 'include' ) { + continue; + } + + if ( $condition_parts[1] === $has_indicator ) { + return true; + } + } + } + + return false; + } + + /** + * Is the current page has an elementor template. Looks if the an Elementor template is applied to the current page or not. + * + * @param string $elementor_template_type valid types: single_product|product_archive (keys of the self::ELEMENTOR_TEMPLATE_TYPES const array). To available params; see keys of the self::ELEMENTOR_TEMPLATE_TYPES array. + * @return bool + */ + public static function is_elementor_template( $elementor_template_type ) { + if ( ! class_exists( '\ElementorPro\Plugin', false ) ) { + return false; + } + + if ( ! array_key_exists( $elementor_template_type, self::ELEMENTOR_TEMPLATE_TYPES ) ) { + return false; + } + + $location = self::ELEMENTOR_TEMPLATE_TYPES[ $elementor_template_type ]['location']; + + if ( array_key_exists( $elementor_template_type, self::$cache_cp_has_template ) ) { + return self::$cache_cp_has_template[ $location ]; + } + + /** + * @var \ElementorPro\Modules\ThemeBuilder\Classes\Conditions_Manager $conditions_manager + */ + $conditions_manager = self::get_condition_manager(); + + if ( ! is_object( $conditions_manager ) || ! method_exists( $conditions_manager, 'get_location_templates' ) ) { + return false; + } + + $templates = $conditions_manager->get_location_templates( $location ); + + self::$cache_cp_has_template[ $location ] = ( count( $templates ) > 0 ); + + return self::$cache_cp_has_template[ $location ]; + } + + /** + * Detect if a page is using the checkout widget. + * + * @return bool + */ + public static function is_elementor_checkout() { + if ( ! class_exists( 'WooCommerce' ) ) { + return false; + } + if ( ! function_exists( 'is_checkout' ) && ! is_checkout() ) { + return false; + } + if ( ! class_exists( '\ElementorPro\Plugin', false ) ) { + return false; + } + if ( array_key_exists( 'checkout', self::$cache_cp_has_template ) ) { + return self::$cache_cp_has_template['checkout']; + } + + $is_elementor_checkout = false; + $page_id = get_the_ID(); + $elementor_data = get_post_meta( $page_id, '_elementor_data', true ); + if ( ! empty( $elementor_data ) && is_string( $elementor_data ) && ( strpos( $elementor_data, 'woocommerce-checkout-page' ) !== false ) ) { + $is_elementor_checkout = true; + } + + self::$cache_cp_has_template['checkout'] = $is_elementor_checkout; + + return self::$cache_cp_has_template['checkout']; + } + + /** + * Conditionally suspense Woocommerce moditifications by Neve Pro if Elementor template applies to current page. + * + * @param bool $should_load Current loading status. + * @param string $class_name Fully class name that applies the Woo Modification. + * @return bool + */ + public function suspend_woo_customizations( $should_load, $class_name ) { + switch ( $class_name ) { + case 'Neve_Pro\Modules\Woocommerce_Booster\Views\Shop_Page': + $elementor_template_type = 'product_archive'; + break; + + case 'Neve_Pro\Modules\Woocommerce_Booster\Views\Shop_Product': + $elementor_template_type = is_single() ? 'single_product' : 'product_archive'; // Sometimes shop_product is used inside of the single product as related products etc. + break; + + case 'Neve_Pro\Modules\Woocommerce_Booster\Views\Single_Product_Video': + case 'Neve_Pro\Modules\Woocommerce_Booster\Views\Single_Product': + $elementor_template_type = 'single_product'; + break; + + default: + return $should_load; + } + + // Does the current page is overridden by an Elementor template? + $elementor_overrides = self::is_elementor_template( $elementor_template_type ); + + return ! $elementor_overrides; } } diff --git a/inc/compatibility/fse.php b/inc/compatibility/fse.php new file mode 100644 index 0000000000..b046a541d1 --- /dev/null +++ b/inc/compatibility/fse.php @@ -0,0 +1,624 @@ +templates = [ + 'index' => __( 'Blog', 'neve' ), + 'front-page' => __( 'Front Page', 'neve' ), + 'archive' => __( 'Archive', 'neve' ), + '404' => '404', + 'search' => __( 'Search', 'neve' ), + 'page' => __( 'Page', 'neve' ), + 'single' => __( 'Single Post', 'neve' ), + ]; + } + + /** + * Init hooks. + */ + public function init() { + if ( ! class_exists( '\WP_Theme_JSON_Data', false ) ) { + return; + } + + // Customizer. + add_action( 'customize_register', [ $this, 'add_controls' ] ); + add_action( 'customize_controls_enqueue_scripts', [ $this, 'add_styles' ] ); + + // Remove site editor menu in admin bar and dashboard. + add_action( 'admin_bar_menu', [ $this, 'remove_admin_bar_menu' ], PHP_INT_MAX ); + add_action( 'admin_menu', [ $this, 'remove_dashboard_menu' ], PHP_INT_MAX ); + + // Filter out block templates (they load by default). + add_filter( 'get_block_templates', [ $this, 'filter_templates' ], 10, 3 ); + + + add_action( 'admin_init', [ $this, 'shortcircuit_redirect' ] ); + + // Theme header/footer + add_action( 'wp_body_open', [ $this, 'handle_header' ], PHP_INT_MAX ); + add_action( 'wp_footer', [ $this, 'handle_footer' ], PHP_INT_MIN ); + + } + + /** + * Remove admin bar menu item. + * + * @param WP_Admin_Bar $wp_admin_bar the WP_Admin_Bar instance. + * + * @return void + */ + public function remove_admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) { + if ( $this->is_enabled() ) { + return; + } + $wp_admin_bar->remove_node( 'site-editor' ); + } + + /** + * Remove dashboard menu item. + * + * @return void + */ + public function remove_dashboard_menu() { + if ( $this->is_enabled() ) { + return; + } + remove_submenu_page( 'themes.php', 'site-editor.php' ); + } + + /** + * Shortcircuits the redirect to the site editor. + * This is needed because the site editor sometimes breaks depending on what is enabled in the customizer Full Site Editing panel. + * + * @return void + */ + public function shortcircuit_redirect() { + if ( ! $this->should_load( true ) ) { + return; + } + + global $pagenow; + + if ( $pagenow !== 'site-editor.php' ) { + return; + } + + if ( get_option( 'show_on_front' ) === 'page' && get_option( 'page_on_front' ) ) { + if ( $this->is_template_enabled( 'front-page' ) ) { + return; + } + + if ( isset( $_GET['postType'] ) && $_GET['postType'] !== 'wp_template' && isset( $_GET['postId'] ) ) { + $this->do_redirect(); + } + } else { + if ( $this->is_template_enabled( 'index' ) ) { + return; + } + + $this->do_redirect(); + } + } + + /** + * Redirect to generic site editor URL. + * + * @return void + */ + private function do_redirect() { + wp_safe_redirect( add_query_arg( 'postType', 'wp_template', admin_url( 'site-editor.php' ) ) ); + + exit; + } + + /** + * Set up the conditions to check if we're on a specific template. + * + * @return array + */ + public function get_template_conditions() { + return [ + 'index' => $this->is_blog(), + 'front-page' => $this->is_front_page(), + 'archive' => is_post_type_archive( 'post' ) && ! $this->is_blog(), + '404' => is_404(), + 'search' => is_search(), + 'page' => $this->is_single_page(), + 'single' => is_singular( 'post' ), + ]; + } + + /** + * Handle header. + * + * @return void + */ + public function handle_header() { + $template = $this->get_template_slug(); + + if ( ! $this->is_template_enabled( $template ) ) { + return; + } + + $header_classes = apply_filters( 'nv_header_classes', 'header' ); + ?> + +
+ + +
> + + + + handle_theme_part( 'header', $template ); + } + + do_action( 'neve_after_header_hook' ); + ?> +
+ + +
+ get_template_slug(); + + if ( ! $this->is_template_enabled( $template ) ) { + return; + } + + do_action( 'neve_before_primary_end' ); + ?> +
+ handle_theme_part( 'footer', $template ); + do_action( 'neve_after_footer_hook' ); + } + ?> +
+ should_load() ) { + return; + } + + $option = $part === 'header' ? $this->get_option_slug_for_header( $template ) : $this->get_option_slug_for_footer( $template ); + + if ( get_theme_mod( $option, true ) !== true ) { + return; + } + + do_action( 'neve_do_' . $part ); + } + + /** + * Filters the array of queried block templates array after they've been fetched. + * + * @param \WP_Block_Template[] $query_result Array of found block templates. + * @param array $query Arguments to retrieve templates. + * @param string $template_type wp_template or wp_template_part. + */ + public function filter_templates( $query_result, $query, $template_type ) { + if ( $template_type !== 'wp_template' ) { + return $query_result; + } + + if ( ! $this->is_enabled() ) { + return []; + } + + foreach ( $query_result as $key => $template ) { + // Skip if this is not defined by the theme itself. Allow all other templates to fall through. + if ( ! in_array( $template->slug, array_keys( $this->templates ) ) ) { + continue; + } + + $enabled = $this->is_template_enabled( $template->slug ); + $conditions = $this->get_template_conditions(); + + + // Still need to load all templates in admin. + if ( ! is_admin() ) { + if ( ! isset( $conditions[ $template->slug ] ) || $conditions[ $template->slug ] !== true ) { + $enabled = false; + } + // Page should not affect the front page. + if ( $template->slug === 'page' && $this->is_front_page() ) { + $enabled = false; + } + + // Don't pass through the index template if we're on archive. + if ( $template->slug === 'index' && ! $this->is_blog() ) { + $enabled = false; + } + } + + if ( $enabled ) { + continue; + } + + unset( $query_result[ $key ] ); + } + + return $query_result; + } + + /** + * Add customizer controls. + * + * @param WP_Customize_Manager $wp_customize the customizer manager. + * + * @return void + */ + public function add_controls( WP_Customize_Manager $wp_customize ) { + $wp_customize->add_section( + $this->customize_section, + array( + 'title' => __( 'Full Site Editing', 'neve' ), + 'priority' => 1000, + ) + ); + + $wp_customize->add_setting( + self::FSE_ENABLED_SLUG, + array( + 'default' => false, + 'sanitize_callback' => 'neve_sanitize_checkbox', + ) + ); + + $wp_customize->add_control( + self::FSE_ENABLED_SLUG, + array( + 'label' => __( 'Full Site Editing', 'neve' ), + 'section' => $this->customize_section, + 'type' => 'neve_toggle_control', + ) + ); + + $priority = 10; + + foreach ( $this->templates as $slug => $label ) { + $wp_customize->add_setting( + 'neve_fse_heading_' . $slug, + array( + 'sanitize_callback' => 'sanitize_text_field', + ) + ); + $wp_customize->add_control( + new \Neve\Customizer\Controls\React\Heading( + $wp_customize, + 'neve_fse_heading_' . $slug, + array( + 'section' => $this->customize_section, + 'active_callback' => [ $this, 'is_enabled' ], + 'priority' => $priority, + 'label' => $label, + ) + ) + ); + + $priority ++; + + $wp_customize->add_setting( + $this->get_option_slug_for_template( $slug ), + array( + 'default' => false, + 'sanitize_callback' => 'neve_sanitize_checkbox', + ) + ); + + $wp_customize->add_control( + $this->get_option_slug_for_template( $slug ), + array( + 'active_callback' => [ $this, 'is_enabled' ], + 'section' => $this->customize_section, + 'priority' => $priority, + 'label' => __( 'Yes', 'neve' ), + 'type' => 'neve_toggle_control', + ) + ); + + $priority ++; + + $wp_customize->add_setting( + $this->get_option_slug_for_header( $slug ), + array( + 'default' => true, + 'sanitize_callback' => 'neve_sanitize_checkbox', + ) + ); + + $wp_customize->add_control( + $this->get_option_slug_for_header( $slug ), + array( + 'active_callback' => function () use ( $slug ) { + return $this->is_enabled() && $this->is_template_enabled( $slug ); + }, + 'section' => $this->customize_section, + 'priority' => $priority, + 'label' => __( 'Header', 'neve' ), + 'type' => 'neve_toggle_control', + ) + ); + + $priority ++; + + $wp_customize->add_setting( + $this->get_option_slug_for_footer( $slug ), + array( + 'default' => true, + 'sanitize_callback' => 'neve_sanitize_checkbox', + ) + ); + + $wp_customize->add_control( + $this->get_option_slug_for_footer( $slug ), + array( + 'active_callback' => function () use ( $slug ) { + return $this->is_enabled() && $this->is_template_enabled( $slug ); + }, + 'section' => $this->customize_section, + 'priority' => $priority, + 'label' => __( 'Footer', 'neve' ), + 'type' => 'neve_toggle_control', + ) + ); + + $priority += 10; + } + } + + /** + * Checks if template condition is met. + * + * @return string|null + */ + public function get_template_slug() { + foreach ( $this->get_template_conditions() as $slug => $condition ) { + if ( ! $condition ) { + continue; + } + + // Page shouldn't pass through to front page. + if ( $slug === 'page' && $this->is_front_page() ) { + continue; + } + + // We're on archive but not on index. + if ( $slug === 'archive' && $this->is_blog() ) { + continue; + } + + return $slug; + } + + return null; + } + + /** + * Check if the FSE templates are enabled. + * + * @return bool + */ + public function is_enabled() { + return get_theme_mod( self::FSE_ENABLED_SLUG, false ); + } + + /** + * Check if templates should be loaded. + * + * @return bool + */ + private function should_load( $admin = false ) { + if ( ! $this->is_enabled() ) { + return false; + } + + if ( $admin ) { + return true; + } + + $status = array_map( + function ( $template ) { + return $this->is_template_enabled( $template ) && $this->get_template_slug() === $template; + }, + array_keys( $this->templates ) + ); + + if ( ! in_array( true, $status, true ) ) { + return false; + } + + return true; + } + + /** + * Get the option ID for a template. + * + * @param string $template the template slug. + * + * @return string + */ + private function get_option_slug_for_template( $template ) { + return 'neve_fse_' . $template; + } + + /** + * Get the option ID for a template header. + * + * @param string $template the template slug. + * + * @return string + */ + private function get_option_slug_for_header( $template ) { + return 'neve_fse_header_' . $template; + } + + /** + * Get the option ID for a template footer. + * + * @param string $template the template slug. + * + * @return string + */ + private function get_option_slug_for_footer( $template ) { + return 'neve_fse_footer_' . $template; + } + + /** + * Is specific template enabled. + * + * @param string $template the template slug. + * + * @return bool + */ + private function is_template_enabled( $template ) { + return get_theme_mod( $this->get_option_slug_for_template( $template ), false ); + } + + /** + * Check if the current page is blog. + * + * @return bool + */ + private function is_blog() { + return is_post_type_archive( 'post' ) && is_home() && ! is_front_page(); + } + + /** + * Check if current page is front page. + * + * @return bool + */ + private function is_front_page() { + return 'page' == get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ) === get_the_ID(); + } + + /** + * Checks if single page. + * + * @return bool + */ + private function is_single_page() { + // Disable PHP page templates. + $page_template = get_page_template_slug( get_the_ID() ); + if ( strpos( $page_template, '.php' ) !== false ) { + return false; + } + + return is_singular( 'page' ) && ! $this->is_front_page(); + } + + /** + * Customizer inline styles. + * + * @return void + */ + public function add_styles() { + $css = ' + #sub-accordion-section-neve_fse .customize-control-neve_toggle_control, + #sub-accordion-section-neve_fse .customize-control-neve_customizer_heading { margin: 0; } + #sub-accordion-section-neve_fse .customize-control-neve_customizer_heading {margin-top: 10px;} + #sub-accordion-section-neve_fse [id*="neve_fse_header"] .neve-white-background-control, + #sub-accordion-section-neve_fse [id*="neve_fse_footer"] .neve-white-background-control {padding-left: 30px;} + + #accordion-section-neve_fse h3:before { + content: "BETA"; + background-color: #0065a6; + display: inline-flex; + margin-right: 5px; + border-radius: 3px; + color: #fff; + font-size: 11px; + font-weight: 500; + padding: 0 7px; + height: 100%; + line-height: 1.6; + } + '; + + wp_add_inline_style( Loader::CUSTOMIZER_STYLE_HANDLE, Dynamic_Css::minify_css( $css ) ); + + $js = ' + wp.customize.bind("ready", function() { + wp.customize.notifications.remove("site_editor_block_theme_notice"); + }); + '; + + wp_add_inline_script( 'react-controls', $js ); + } +} diff --git a/inc/compatibility/generic.php b/inc/compatibility/generic.php new file mode 100644 index 0000000000..b532b9de5e --- /dev/null +++ b/inc/compatibility/generic.php @@ -0,0 +1,63 @@ + + * Created on: 2019-06-05 + * + * @package generic.php + */ + +namespace Neve\Compatibility; + +/** + * Class Generic + * + * @package Neve\Compatibility + */ +class Generic { + /** + * Init function. + */ + public function init() { + if ( class_exists( 'Mega_Menu', false ) ) { + add_filter( 'megamenu_themes', array( $this, 'max_megamenu_theme' ) ); + } + } + + /** + * Max Mega Menu Hestia Compatibility + * + * @param array $themes the themes. + * + * @return array + **/ + public function max_megamenu_theme( $themes ) { + $themes['neve_max_mega_menu'] = array( + 'title' => 'Neve', + 'menu_item_link_height' => '50px', + 'menu_item_align' => 'right', + 'container_background_from' => 'rgba(255, 255, 255, 0)', + 'container_background_to' => 'rgba(255, 255, 255, 0)', + 'menu_item_background_hover_from' => 'rgba(255, 255, 255, 0.1)', + 'menu_item_background_hover_to' => 'rgba(255, 255, 255, 0.1)', + 'menu_item_link_color' => '#555', + 'menu_item_link_color_hover' => '#0366d6', + 'menu_item_highlight_current' => 'off', + 'panel_background_from' => 'rgb(255, 255, 255)', + 'panel_background_to' => 'rgb(255, 255, 255)', + 'responsive_breakpoint' => '959px', + 'resets' => 'on', + 'toggle_background_from' => 'rgba(255, 255, 255, 0.1)', + 'toggle_background_to' => 'rgba(255, 255, 255, 0.1)', + 'toggle_font_color' => 'rgb(102, 102, 102)', + 'mobile_background_from' => 'rgb(255, 255, 255)', + 'mobile_background_to' => 'rgb(255, 255, 255)', + 'mobile_menu_item_link_color' => 'rgb(102, 102, 102)', + 'responsive_text' => '', + ); + + return $themes; + } + +} diff --git a/inc/compatibility/gutenberg.php b/inc/compatibility/gutenberg.php deleted file mode 100644 index 11e4b70e9d..0000000000 --- a/inc/compatibility/gutenberg.php +++ /dev/null @@ -1,214 +0,0 @@ - - * Created on: 15/11/2018 - * - * @package gutenberg.php - */ - -namespace Neve\Compatibility; - -use Neve\Views\Layouts\Layout_Container; -use Neve\Views\Layouts\Layout_Sidebar; - -/** - * Class Gutenberg - * - * @package Neve\Compatibility - */ -class Gutenberg { - - /** - * Available post meta to be taken into consideration. - * - * @var array - */ - private $available_post_meta = array( - 'neve_meta_disable_header', - 'neve_meta_disable_title', - 'neve_meta_disable_featured_image', - 'neve_meta_disable_footer', - 'neve_meta_sidebar', - 'neve_meta_container', - 'neve_meta_enable_content_width', - 'neve_meta_content_width', - ); - - /** - * The post ID. - * - * @var null - */ - private $post_id = null; - - /** - * Initialize the compatibility module. - */ - public function init() { - $this->set_post_id(); - - add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_gutenberg_scripts' ) ); - } - - /** - * Set the post ID. - * - * @return int|null - */ - private function set_post_id() { - if ( ! isset( $_GET['post'] ) ) { - return null; - } - $this->post_id = $_GET['post']; - } - - /** - * Enqueue gutenberg scripts. - */ - public function enqueue_gutenberg_scripts() { - wp_enqueue_style( 'neve-gutenberg-style', NEVE_ASSETS_URL . 'css/gutenberg-editor-style' . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.css', array(), NEVE_VERSION ); - - wp_register_script( 'neve-gutenberg-script', NEVE_ASSETS_URL . 'js/gutenberg-preview-manager' . ( ( NEVE_DEBUG ) ? '' : '.min' ) . '.js', array( 'jquery' ), NEVE_VERSION, true ); - - wp_localize_script( 'neve-gutenberg-script', 'neveGutenbergHelper', apply_filters( 'neve_gutenberg_helper_filter_localization', $this->localize_gutenberg_helper_script() ) ); - - wp_enqueue_script( 'neve-gutenberg-script' ); - } - - /** - * Localize the gutenberg helper script. - * - * @return array - */ - public function localize_gutenberg_helper_script() { - $localization = array(); - $localization = array_merge( $localization, $this->get_post_metas() ); - $localization['strings'] = $this->get_strings(); - $localization['metaStatus'] = $this->get_meta_status(); - $localization['sidebarSetup'] = $this->get_sidebar_setup(); - $localization['containerSetup'] = $this->get_container_setup(); - - return $localization; - } - - /** - * Check if we're editing a page. - * - * @return bool - */ - private function is_page() { - if ( isset( $_GET['post'] ) && get_post_type( $_GET['post'] ) === 'page' ) { - return true; - } - - if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'page' ) { - return true; - } - - return false; - } - - - /** - * Get the sidebar setup. - * - * @return string - * TODO: Really pull the sidebar setup from customizer. - */ - private function get_sidebar_setup() { - $context = 'single-post'; - if ( $this->is_page() ) { - $context = 'single-page'; - } - - $sidebar_manager = new Layout_Sidebar(); - - $layout = $sidebar_manager->get_sidebar_setup( $context ); - - $setup = get_theme_mod( $layout['theme_mod'], 'right' ); - - return $setup; - } - - /** - * Get the container setup. - * - * @return string - */ - private function get_container_setup() { - $context = 'single-post'; - - if ( $this->is_page() ) { - $context = 'single-page'; - } - - $container_manager = new Layout_Container(); - - $layout = $container_manager->container_layout( 'contained', $context ); - - if ( $layout === 'container' ) { - return 'contained'; - } - - return 'full-width'; - } - - /** - * Get the post metas we're interested in. - * - * @return array - */ - private function get_post_metas() { - if ( $this->post_id === null ) { - return array(); - } - - $metas = array(); - foreach ( $this->available_post_meta as $meta ) { - $meta_value = get_post_meta( $this->post_id, $meta, true ); - if ( empty( $meta_value ) ) { - continue; - } - $metas[ $meta ] = $meta_value; - } - - return $metas; - } - - /** - * Check if post meta is shown on the front end. - * - * @return string - */ - private function get_meta_status() { - if ( $this->is_page() ) { - return 'disabled'; - } - $default_meta_order = json_encode( - array( - 'author', - 'date', - 'comments', - ) - ); - $meta = get_theme_mod( 'neve_post_meta_ordering', $default_meta_order ); - $meta = json_decode( $meta, true ); - if ( empty( $meta ) ) { - return 'disabled'; - } - - return 'enabled'; - } - - /** - * Get translatable strings. - * - * @return array - */ - private function get_strings() { - return array( - 'sidebar' => __( 'Sidebar', 'neve' ), - ); - } - -} diff --git a/inc/compatibility/header_footer_beaver.php b/inc/compatibility/header_footer_beaver.php new file mode 100644 index 0000000000..f9cbef24f6 --- /dev/null +++ b/inc/compatibility/header_footer_beaver.php @@ -0,0 +1,83 @@ +should_load() ) { + return false; + } + $this->add_theme_builder_hooks(); + } + + /** + * Check if plugin is installed. + */ + private function should_load() { + if ( ! defined( 'FL_BUILDER_VERSION' ) ) { + return false; + } + if ( ! class_exists( '\BB_Header_Footer', false ) ) { + return false; + } + + return true; + } + + /** + * Replace theme hooks with the one from the plugin. + */ + private function add_theme_builder_hooks() { + add_action( 'neve_do_header', array( $this, 'do_header' ), 0 ); + add_action( 'neve_do_footer', array( $this, 'do_footer' ), 0 ); + } + + /** + * Replace Header hooks. + */ + public function do_header() { + $header_id = \BB_Header_Footer::get_settings( 'bb_header_id', '' ); + if ( empty( $header_id ) ) { + return false; + } + remove_all_actions( 'neve_do_top_bar' ); + remove_all_actions( 'neve_do_header' ); + + echo '
'; + \BB_Header_Footer::get_header_content(); + echo '
'; + + return true; + } + + /** + * Replace Footer hooks. + */ + public function do_footer() { + $footer_id = \BB_Header_Footer::get_settings( 'bb_footer_id', '' ); + if ( empty( $footer_id ) ) { + return false; + } + remove_all_actions( 'neve_do_footer' ); + echo '
'; + \BB_Header_Footer::get_footer_content(); + echo '
'; + + return true; + } +} diff --git a/inc/compatibility/header_footer_elementor.php b/inc/compatibility/header_footer_elementor.php new file mode 100644 index 0000000000..6915665704 --- /dev/null +++ b/inc/compatibility/header_footer_elementor.php @@ -0,0 +1,72 @@ +should_load() ) { + return; + } + + $this->add_theme_builder_hooks(); + } + + /** + * Replace theme hooks with the one from the plugin. + */ + private function add_theme_builder_hooks() { + add_action( 'neve_do_header', array( $this, 'do_header' ), 0 ); + add_action( 'neve_do_footer', array( $this, 'do_footer' ), 0 ); + } + + /** + * Replace Header hooks. + */ + public function do_header() { + if ( ! hfe_header_enabled() ) { + return; + } + hfe_render_header(); + remove_all_actions( 'neve_do_top_bar' ); + remove_all_actions( 'neve_do_header' ); + } + + /** + * Replace Footer hooks. + */ + public function do_footer() { + if ( ! hfe_footer_enabled() ) { + return; + } + hfe_render_footer(); + remove_all_actions( 'neve_do_footer' ); + } +} diff --git a/inc/compatibility/lifter.php b/inc/compatibility/lifter.php new file mode 100644 index 0000000000..440e486f05 --- /dev/null +++ b/inc/compatibility/lifter.php @@ -0,0 +1,386 @@ + ' + ,a.llms-button-primary, + button.llms-button-primary, + a.llms-button-action, + button.llms-button-action', + 'hover' => ' + ,a.llms-button-primary:hover, + button.llms-button-primary:hover, + a.llms-button-primary:active, + button.llms-button-primary:active, + a.llms-button-primary:focus, + button.llms-button-primary:focus, + a.llms-button-action:hover, + a.llms-button-action:active, + a.llms-button-action:focus, + button.llms-button-action:hover, + button.llms-button-action:active, + button.llms-button-action:focus', + ); + + /** + * Secondary buttons selectors. + * + * @var array + */ + private $secondary_buttons_selectors = array( + 'default' => ' + ,a.llms-button-secondary', + 'hover' => ' + ,a.llms-button-secondary:hover', + ); + + /** + * Init function. + */ + public function init() { + if ( ! $this->should_load() ) { + return; + } + $this->load_hooks(); + $this->add_inline_selectors(); + } + + /** + * Check if LifterLMS plugin is activated. + */ + private function should_load() { + return class_exists( 'LifterLMS', false ); + } + + /** + * Load hooks and filters. + */ + private function load_hooks() { + add_action( 'wp_enqueue_scripts', array( $this, 'load_styles' ) ); + + remove_action( 'lifterlms_before_main_content', 'lifterlms_output_content_wrapper', 10 ); + remove_action( 'lifterlms_after_main_content', 'lifterlms_output_content_wrapper_end', 10 ); + remove_all_actions( 'lifterlms_sidebar' ); + + add_action( 'lifterlms_before_main_content', array( $this, 'content_wrapper_open' ), 0 ); + add_action( 'lifterlms_loop', array( $this, 'content_wrapper_close' ), 100 ); + add_filter( 'lifterlms_show_page_title', '__return_false' ); + + add_action( 'neve_llms_content', array( $this, 'content_open' ), 10 ); + add_action( + 'lifterlms_loop', + function() { + echo '
'; + echo '