'header', 'headfoot_include_rules' => [ [ 'rule' => 'global', 'target' => '', ], ], 'headfoot_exclude_rules' => [], 'headfoot_user_roles' => [], 'headfoot_show_on_canvas' => '', ]; } /** * Registers the controls on the template document. * * @param \Elementor\Core\Base\Document $document Document. * @return void */ public function register_controls( $document ) { if ( ! $document instanceof Template_Document ) { return; } $defaults = self::defaults(); $document->start_controls_section( 'headfoot_display_section', [ 'label' => esc_html__( 'Display Conditions', 'headfoot' ), 'tab' => Controls_Manager::TAB_SETTINGS, ] ); $document->add_control( 'headfoot_type', [ 'label' => esc_html__( 'Type', 'headfoot' ), 'type' => Controls_Manager::SELECT, 'default' => $defaults['headfoot_type'], 'options' => Post_Type::get_types(), ] ); $rule_fields = $this->get_rule_fields(); $document->add_control( 'headfoot_include_rules', [ 'label' => esc_html__( 'Display On', 'headfoot' ), 'type' => Controls_Manager::REPEATER, 'fields' => $rule_fields, 'default' => $defaults['headfoot_include_rules'], 'title_field' => '{{{ rule === "specific" ? target : rule }}}', 'prevent_empty' => false, 'description' => esc_html__( 'A template with no rule never shows.', 'headfoot' ), ] ); $document->add_control( 'headfoot_exclude_rules', [ 'label' => esc_html__( 'Do Not Display On', 'headfoot' ), 'type' => Controls_Manager::REPEATER, 'fields' => $rule_fields, 'default' => $defaults['headfoot_exclude_rules'], 'title_field' => '{{{ rule === "specific" ? target : rule }}}', 'prevent_empty' => false, 'description' => esc_html__( 'An exclusion always wins over a display rule.', 'headfoot' ), ] ); $document->add_control( 'headfoot_user_roles', [ 'label' => esc_html__( 'User Roles', 'headfoot' ), 'type' => Controls_Manager::SELECT2, 'multiple' => true, 'options' => Rules::get_role_options(), 'default' => $defaults['headfoot_user_roles'], 'description' => esc_html__( 'An empty selection shows the template to everyone.', 'headfoot' ), ] ); $document->add_control( 'headfoot_show_on_canvas', [ 'label' => esc_html__( 'Enable on Elementor Canvas', 'headfoot' ), 'type' => Controls_Manager::SWITCHER, 'return_value' => '1', 'default' => $defaults['headfoot_show_on_canvas'], ] ); $document->end_controls_section(); } /** * Builds the repeater fields: a grouped rule select and a target select. * * @return array */ private function get_rule_fields() { $repeater = new Repeater(); $groups = []; foreach ( Rules::get_groups() as $group ) { if ( empty( $group['rules'] ) || ! is_array( $group['rules'] ) ) { continue; } $groups[] = [ 'label' => $group['label'], 'options' => $group['rules'], ]; } // The editor drops a row value that equals the field default before it // saves. save_default keeps the value, so a row with the rule global arrives. $repeater->add_control( 'rule', [ 'label' => esc_html__( 'Rule', 'headfoot' ), 'type' => Controls_Manager::SELECT, 'default' => 'global', 'groups' => $groups, 'save_default' => true, ] ); $repeater->add_control( 'target', [ 'label' => esc_html__( 'Target', 'headfoot' ), 'type' => Controls_Manager::SELECT2, 'options' => $this->get_target_options(), 'label_block' => true, 'condition' => [ 'rule' => Rules::SPECIFIC ], 'save_default' => true, ] ); return $repeater->get_controls(); } /** * Lists posts, pages, and terms as targets. * * The list holds the newest 300 posts of the public post types and up to * 300 terms. The meta box search covers the rest. * * @return array value => label */ private function get_target_options() { // Only the editor needs the list. The front end registers the control without it. if ( ! is_admin() && ! wp_doing_ajax() ) { return []; } $options = []; $post_types = array_diff( get_post_types( [ 'public' => true ] ), [ Post_Type::POST_TYPE, 'attachment', 'elementor_library', 'e-landing-page', 'e-floating-buttons' ] ); $posts = get_posts( [ 'post_type' => array_values( $post_types ), 'post_status' => [ 'publish', 'private', 'draft', 'pending', 'future' ], 'posts_per_page' => self::TARGET_LIMIT, 'orderby' => 'date', 'order' => 'DESC', 'no_found_rows' => true, 'suppress_filters' => false, 'lang' => '', ] ); foreach ( $posts as $post ) { if ( ! current_user_can( 'read_post', $post->ID ) ) { continue; } $options[ 'post:' . $post->ID ] = esc_html( Rules::get_label( 'post:' . $post->ID ) ); } $taxonomies = array_diff( get_taxonomies( [ 'public' => true ] ), [ 'post_format' ] ); $terms = get_terms( [ 'taxonomy' => array_values( $taxonomies ), 'number' => self::TARGET_LIMIT, 'hide_empty' => false, 'lang' => '', ] ); if ( ! is_wp_error( $terms ) ) { foreach ( $terms as $term ) { $options[ 'term:' . $term->term_id ] = esc_html( Rules::get_label( 'term:' . $term->term_id ) ); } } return $options; } /** * Copies the panel values into the plugin meta keys after an editor save. * * Elementor sends only the values that differ from the control defaults, * so a missing key means the default. * * @param \Elementor\Core\Base\Document $document Saved document. * @param array $data Saved data. * @return void */ public function after_save( $document, $data ) { if ( ! $document instanceof Template_Document || $document->is_autosave() ) { return; } if ( ! isset( $data['settings'] ) || ! is_array( $data['settings'] ) ) { return; } $settings = wp_parse_args( $data['settings'], self::defaults() ); $post_id = $document->get_main_id(); update_post_meta( $post_id, Post_Type::META_TYPE, Post_Type::sanitize_type( $settings['headfoot_type'] ) ); update_post_meta( $post_id, Post_Type::META_INCLUDE, Rules::sanitize_list( self::rows_to_rules( $settings['headfoot_include_rules'] ) ) ); update_post_meta( $post_id, Post_Type::META_EXCLUDE, Rules::sanitize_list( self::rows_to_rules( $settings['headfoot_exclude_rules'] ) ) ); update_post_meta( $post_id, Post_Type::META_ROLES, Rules::sanitize_roles( is_array( $settings['headfoot_user_roles'] ) ? $settings['headfoot_user_roles'] : [] ) ); update_post_meta( $post_id, Post_Type::META_CANVAS, empty( $settings['headfoot_show_on_canvas'] ) ? '0' : '1' ); } /** * Copies the plugin meta keys into the Elementor page settings. * * The meta box calls this after a save, so the panel shows the same values. * * @param int $post_id Template ID. * @return void */ public static function sync_page_settings( $post_id ) { $settings = get_post_meta( $post_id, self::SETTINGS_META, true ); if ( ! is_array( $settings ) ) { $settings = []; } $include = get_post_meta( $post_id, Post_Type::META_INCLUDE, true ); $exclude = get_post_meta( $post_id, Post_Type::META_EXCLUDE, true ); $roles = get_post_meta( $post_id, Post_Type::META_ROLES, true ); $settings['headfoot_type'] = Post_Type::sanitize_type( get_post_meta( $post_id, Post_Type::META_TYPE, true ) ); $settings['headfoot_include_rules'] = self::rules_to_rows( is_array( $include ) ? $include : [] ); $settings['headfoot_exclude_rules'] = self::rules_to_rows( is_array( $exclude ) ? $exclude : [] ); $settings['headfoot_user_roles'] = is_array( $roles ) ? array_values( $roles ) : []; $settings['headfoot_show_on_canvas'] = '1' === get_post_meta( $post_id, Post_Type::META_CANVAS, true ) ? '1' : ''; update_post_meta( $post_id, self::SETTINGS_META, wp_slash( $settings ) ); } /** * Turns repeater rows into rule identifiers. * * A row without a rule counts as global. Documents saved before the * save_default flag hold such rows, because the editor dropped the default. * * @param mixed $rows Repeater rows. * @return array */ public static function rows_to_rules( $rows ) { $rules = []; foreach ( is_array( $rows ) ? $rows : [] as $row ) { if ( ! is_array( $row ) ) { continue; } if ( empty( $row['rule'] ) ) { $row['rule'] = 'global'; } if ( Rules::SPECIFIC === $row['rule'] ) { if ( ! empty( $row['target'] ) ) { $rules[] = (string) $row['target']; } continue; } $rules[] = (string) $row['rule']; } return $rules; } /** * Turns rule identifiers into repeater rows. * * @param array $rules Rule identifiers. * @return array */ public static function rules_to_rows( $rules ) { $rows = []; foreach ( $rules as $rule ) { $specific = Rules::is_specific( $rule ); $rows[] = [ '_id' => substr( md5( $rule ), 0, 7 ), 'rule' => $specific ? Rules::SPECIFIC : $rule, 'target' => $specific ? $rule : '', ]; } return $rows; } }