Tags()

Description #

Tags addon for AnsPress

Source #

File: addons/tags/tags.php

class Tags extends \AnsPress\Singleton {
	/**
	 * Instance of this class.
	 *
	 * @var     object
	 * @since 4.1.8
	 */
	protected static $instance = null;

	/**
	 * Initialize the class
	 *
	 * @since 4.1.8 Added filter `ap_category_questions_args`.
	 * @since 4.2.0 Added hook `ap_settings_menu_features_groups`.
	 */
	protected function __construct() {
		ap_register_page( 'tag', __( 'Tag', 'anspress-question-answer' ), array( $this, 'tag_page' ), false );
		ap_register_page( 'tags', __( 'Tags', 'anspress-question-answer' ), array( $this, 'tags_page' ) );

		anspress()->add_action( 'ap_settings_menu_features_groups', $this, 'add_to_settings_page' );
		anspress()->add_action( 'ap_form_options_features_tag', $this, 'option_fields' );
		anspress()->add_action( 'widgets_init', $this, 'widget_positions' );
		anspress()->add_action( 'init', $this, 'register_question_tag', 1 );
		anspress()->add_action( 'ap_admin_menu', $this, 'admin_tags_menu' );
		anspress()->add_action( 'ap_display_question_metas', $this, 'ap_display_question_metas', 10, 2 );
		anspress()->add_action( 'ap_question_info', $this, 'ap_question_info' );
		anspress()->add_action( 'ap_enqueue', $this, 'ap_assets_js' );
		anspress()->add_action( 'ap_enqueue', $this, 'ap_localize_scripts' );
		anspress()->add_filter( 'term_link', $this, 'term_link_filter', 10, 3 );
		anspress()->add_action( 'ap_question_form_fields', $this, 'ap_question_form_fields' );
		anspress()->add_action( 'ap_processed_new_question', $this, 'after_new_question', 0, 2 );
		anspress()->add_action( 'ap_processed_update_question', $this, 'after_new_question', 0, 2 );
		anspress()->add_filter( 'ap_page_title', $this, 'page_title' );
		anspress()->add_filter( 'ap_breadcrumbs', $this, 'ap_breadcrumbs' );
		anspress()->add_action( 'wp_ajax_ap_tags_suggestion', $this, 'ap_tags_suggestion' );
		anspress()->add_action( 'wp_ajax_nopriv_ap_tags_suggestion', $this, 'ap_tags_suggestion' );
		anspress()->add_action( 'ap_rewrites', $this, 'rewrite_rules', 10, 3 );
		anspress()->add_filter( 'ap_main_questions_args', $this, 'ap_main_questions_args' );
		anspress()->add_filter( 'ap_category_questions_args', $this, 'ap_main_questions_args' );
		anspress()->add_filter( 'ap_current_page', $this, 'ap_current_page' );
		anspress()->add_action( 'posts_pre_query', $this, 'modify_query_archive', 9999, 2 );

		// List filtering.
		anspress()->add_filter( 'ap_list_filters', $this, 'ap_list_filters' );
		anspress()->add_action( 'ap_ajax_load_filter_qtag', $this, 'load_filter_tag' );
		anspress()->add_action( 'ap_ajax_load_filter_tags_order', $this, 'load_filter_tags_order' );
		anspress()->add_filter( 'ap_list_filter_active_qtag', $this, 'filter_active_tag', 10, 2 );
		anspress()->add_filter( 'ap_list_filter_active_tags_order', $this, 'filter_active_tags_order', 10, 2 );
	}

	/**
	 * Tag page layout.
	 *
	 * @since 4.1.0 Use `get_queried_object()` to get current term.
	 */
	public function tag_page() {
		global $question_tag;
		$question_tag = get_queried_object();

		$question_args = array(
			'paged'     => max( 1, get_query_var( 'ap_paged' ) ),
			'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
				array(
					'taxonomy' => 'question_tag',
					'field'    => 'id',
					'terms'    => array( $question_tag->term_id ),
				),
			),
		);

		$question_args = apply_filters( 'ap_tag_question_query_args', $question_args );

		if ( $question_tag ) {
			anspress()->questions = ap_get_questions( $question_args );
			include ap_get_theme_location( 'addons/tag/tag.php' );
		}
	}

	/**
	 * Tags page layout
	 */
	public function tags_page() {
		global $question_tags, $ap_max_num_pages, $ap_per_page, $tags_rows_found;
		$paged    = max( 1, get_query_var( 'paged' ) );
		$per_page = (int) ap_opt( 'tags_per_page' );
		$per_page = 0 === $per_page ? 1 : $per_page;
		$offset   = $per_page * ( $paged - 1 );

		$tag_args = array(
			'taxonomy'      => 'question_tag',
			'ap_tags_query' => true,
			'parent'        => 0,
			'number'        => $per_page,
			'offset'        => $offset,
			'hide_empty'    => false,
			'order'         => 'DESC',
		);

		$ap_sort = ap_isset_post_value( 'tags_order', 'count' );

		if ( 'new' === $ap_sort ) {
			$tag_args['orderby'] = 'id';
			$tag_args['order']   = 'DESC';
		} elseif ( 'name' === $ap_sort ) {
			$tag_args['orderby'] = 'name';
			$tag_args['order']   = 'ASC';
		} else {
			$tag_args['orderby'] = 'count';
		}

		if ( ap_isset_post_value( 'ap_s' ) ) {
			$tag_args['search'] = ap_sanitize_unslash( 'ap_s', 'r' );
		}

		/**
		 * Filter applied before getting tags.
		 *
		 * @var array
		 */
		$tag_args = apply_filters( 'ap_tags_shortcode_args', $tag_args );

		$query = new \WP_Term_Query( $tag_args );

		// Count terms.
		$tag_args['fields'] = 'count';
		$found_query        = new \WP_Term_Query( $tag_args );
		$tags_rows_found    = (int) $found_query->get_terms();
		$ap_max_num_pages   = ceil( $tags_rows_found / $per_page );
		$question_tags      = $query->get_terms();

		include ap_get_theme_location( 'addons/tag/tags.php' );
	}

	/**
	 * Register widget position.
	 */
	public function widget_positions() {
		register_sidebar(
			array(
				'name'          => __( '(AnsPress) Tags', 'anspress-question-answer' ),
				'id'            => 'ap-tags',
				'before_widget' => '<div id="%1$s" class="ap-widget-pos %2$s">',
				'after_widget'  => '</div>',
				'description'   => __( 'Widgets in this area will be shown in anspress tags page.', 'anspress-question-answer' ),
				'before_title'  => '<h3 class="ap-widget-title">',
				'after_title'   => '</h3>',
			)
		);
	}

	/**
	 * Register tag taxonomy for question cpt.
	 *
	 * @return void
	 * @since 2.0
	 */
	public function register_question_tag() {
		ap_add_default_options(
			array(
				'max_tags'      => 5,
				'min_tags'      => 1,
				'tags_per_page' => 20,
				'tag_page_slug' => 'tag',
			)
		);

		$tag_labels = array(
			'name'               => __( 'Question Tags', 'anspress-question-answer' ),
			'singular_name'      => __( 'Tag', 'anspress-question-answer' ),
			'all_items'          => __( 'All Tags', 'anspress-question-answer' ),
			'add_new_item'       => __( 'Add New Tag', 'anspress-question-answer' ),
			'edit_item'          => __( 'Edit Tag', 'anspress-question-answer' ),
			'new_item'           => __( 'New Tag', 'anspress-question-answer' ),
			'view_item'          => __( 'View Tag', 'anspress-question-answer' ),
			'search_items'       => __( 'Search Tag', 'anspress-question-answer' ),
			'not_found'          => __( 'Nothing Found', 'anspress-question-answer' ),
			'not_found_in_trash' => __( 'Nothing found in Trash', 'anspress-question-answer' ),
			'parent_item_colon'  => '',
		);

		/**
		 * FILTER: ap_question_tag_labels
		 * Filter ic called before registering question_tag taxonomy
		 */
		$tag_labels = apply_filters( 'ap_question_tag_labels', $tag_labels );
		$tag_args   = array(
			'hierarchical' => true,
			'labels'       => $tag_labels,
			'rewrite'      => false,
		);

		/**
		 * FILTER: ap_question_tag_args
		 * Filter ic called before registering question_tag taxonomy
		 */
		$tag_args = apply_filters( 'ap_question_tag_args', $tag_args );

		/**
		 * Now let WordPress know about our taxonomy
		 */
		register_taxonomy( 'question_tag', array( 'question' ), $tag_args );
	}

	/**
	 * Add tags menu in wp-admin.
	 */
	public function admin_tags_menu() {
		add_submenu_page( 'anspress', __( 'Question Tags', 'anspress-question-answer' ), __( 'Tags', 'anspress-question-answer' ), 'manage_options', 'edit-tags.php?taxonomy=question_tag' );
	}

	/**
	 * Add tags settings to features settings page.
	 *
	 * @param array $groups Features settings group.
	 * @return array
	 * @since 4.2.0
	 */
	public function add_to_settings_page( $groups ) {
		$groups['tag'] = array(
			'label' => __( 'Tag', 'anspress-question-answer' ),
		);

		return $groups;
	}

	/**
	 * Register option fields.
	 */
	public function option_fields() {
		$opt = ap_opt();

		$form = array(
			'fields' => array(
				'tags_per_page' => array(
					'label'       => __( 'Tags to show', 'anspress-question-answer' ),
					'description' => __( 'Numbers of tags to show in tags page.', 'anspress-question-answer' ),
					'subtype'     => 'number',
					'value'       => $opt['tags_per_page'],
				),
				'max_tags'      => array(
					'label'       => __( 'Maximum tags', 'anspress-question-answer' ),
					'description' => __( 'Maximum numbers of tags that user can add when asking.', 'anspress-question-answer' ),
					'subtype'     => 'number',
					'value'       => $opt['max_tags'],
				),
				'min_tags'      => array(
					'label'       => __( 'Minimum tags', 'anspress-question-answer' ),
					'description' => __( 'minimum numbers of tags that user must add when asking.', 'anspress-question-answer' ),
					'subtype'     => 'number',
					'value'       => $opt['min_tags'],
				),
				'tag_page_slug' => array(
					'label' => __( 'Tag page slug', 'anspress-question-answer' ),
					'desc'  => __( 'Slug for tag page', 'anspress-question-answer' ),
					'value' => $opt['tag_page_slug'],
				),
			),
		);

		return $form;
	}


	/**
	 * Append meta display.
	 *
	 * @param  array $metas Display metas.
	 * @param  array $question_id Post ID.
	 * @return array
	 * @since 2.0
	 */
	public function ap_display_question_metas( $metas, $question_id ) {
		if ( ap_post_have_terms( $question_id, 'question_tag' ) ) {
			$metas['tags'] = ap_question_tags_html(
				array(
					'label' => '<i class="apicon-tag"></i>',
					'show'  => 1,
				)
			); }

		return $metas;
	}

	/**
	 * Hook tags after post.
	 *
	 * @param object $post Post object.
	 * @since 1.0
	 */
	public function ap_question_info( $post ) {
		if ( ap_question_have_tags() ) {
			echo wp_kses_post( '<div class="widget"><span class="ap-widget-title">' . esc_attr__( 'Tags', 'anspress-question-answer' ) . '</span>' );

			echo wp_kses_post(
				'<div class="ap-post-tags clearfix">' .
				ap_question_tags_html(
					array(
						'list'  => true,
						'label' => '',
					)
				) .
				'</div></div>'
			);
		}
	}

	/**
	 * Enqueue scripts.
	 *
	 * @return void
	 */
	public function ap_assets_js() {
		wp_enqueue_script( 'anspress-tags', ANSPRESS_URL . 'assets/js/tags.js', array( 'anspress-list' ), AP_VERSION, true );
	}

	/**
	 * Add translated strings to the javascript files
	 */
	public function ap_localize_scripts() {
		$l10n_data = array(
			'deleteTag'            => __( 'Delete Tag', 'anspress-question-answer' ),
			'addTag'               => __( 'Add Tag', 'anspress-question-answer' ),
			'tagAdded'             => __( 'added to the tags list.', 'anspress-question-answer' ),
			'tagRemoved'           => __( 'removed from the tags list.', 'anspress-question-answer' ),
			'suggestionsAvailable' => __( 'Suggestions are available. Use the up and down arrow keys to read it.', 'anspress-question-answer' ),
		);

		wp_localize_script(
			'anspress-tags',
			'apTagsTranslation',
			$l10n_data
		);
	}

	/**
	 * Filter tag term link.
	 *
	 * @param  string $url      Default URL of taxonomy.
	 * @param  object $term     Term array.
	 * @param  string $taxonomy Taxonomy type.
	 * @return string           New URL for term.
	 */
	public function term_link_filter( $url, $term, $taxonomy ) {
		if ( 'question_tag' === $taxonomy ) {
			if ( get_option( 'permalink_structure' ) !== '' ) {
				$opt          = get_option( 'ap_tags_path', 'tags' );
				$default_lang = '';

				// Support polylang permalink.
				if ( function_exists( 'pll_default_language' ) ) {
					$default_lang = pll_get_term_language( $term->term_id ) ? pll_get_term_language( $term->term_id ) : pll_default_language();
				}

				return user_trailingslashit( home_url( $default_lang . '/' . $opt ) . '/' . $term->slug );
			} else {
				return add_query_arg(
					array(
						'ap_page'      => 'tag',
						'question_tag' => $term->slug,
					),
					home_url()
				);
			}
		}
		return $url;
	}

	/**
	 * Add tag field in question form.
	 *
	 * @param array $form AnsPress form arguments.
	 * @since 4.1.0
	 */
	public function ap_question_form_fields( $form ) {
		$editing_id = ap_sanitize_unslash( 'id', 'r' );

		$form['fields']['tags'] = array(
			'label'      => __( 'Tags', 'anspress-question-answer' ),
			'desc'       => sprintf(
				// Translators: %1$d contain minimum tags required and %2$d contain maximum tags allowed.
				__( 'Tagging will helps others to easily find your question. Minimum %1$d and maximum %2$d tags.', 'anspress-question-answer' ),
				ap_opt( 'min_tags' ),
				ap_opt( 'max_tags' )
			),
			'type'       => 'tags',
			'array_max'  => ap_opt( 'max_tags' ),
			'array_min'  => ap_opt( 'min_tags' ),
			'js_options' => array(
				'create' => true,
			),
		);

		// Add value when editing post.
		if ( ! empty( $editing_id ) ) {
			$tags = get_the_terms( $editing_id, 'question_tag' );
			if ( $tags ) {
				$tags                            = wp_list_pluck( $tags, 'term_id' );
				$form['fields']['tags']['value'] = $tags;
			}
		}

		return $form;
	}

	/**
	 * Things to do after creating a question.
	 *
	 * @param  integer $post_id Post ID.
	 * @param  object  $post Post object.
	 * @since 1.0
	 */
	public function after_new_question( $post_id, $post ) {
		$values = anspress()->get_form( 'question' )->get_values();

		if ( isset( $values['tags'], $values['tags']['value'] ) ) {
			wp_set_object_terms( $post_id, $values['tags']['value'], 'question_tag' );
		}
	}

	/**
	 * Tags page title.
	 *
	 * @param  string $title Title.
	 * @return string
	 */
	public function page_title( $title ) {
		if ( is_question_tags() ) {
			$title = ap_opt( 'tags_page_title' );
		} elseif ( is_question_tag() ) {
			$tag_id = sanitize_title( get_query_var( 'q_tag' ) );
			$tag    = $tag_id ? get_term_by( 'slug', $tag_id, 'question_tag' ) : get_queried_object();

			if ( $tag ) {
				$title = $tag->name;
			}
		}

		return $title;
	}

	/**
	 * Hook into AnsPress breadcrums to show tags page.
	 *
	 * @param  array $navs Breadcrumbs navs.
	 * @return array
	 */
	public function ap_breadcrumbs( $navs ) {
		if ( is_question_tag() ) {
			$tag_id       = sanitize_title( get_query_var( 'q_tag' ) );
			$tag          = $tag_id ? get_term_by( 'slug', $tag_id, 'question_tag' ) : get_queried_object();
			$navs['page'] = array(
				'title' => __( 'Tags', 'anspress-question-answer' ),
				'link'  => ap_get_link_to( 'tags' ),
				'order' => 8,
			);

			if ( $tag ) {
				$navs['tag'] = array(
					'title' => $tag->name,
					'link'  => get_term_link( $tag, 'question_tag' ), // @codingStandardsIgnoreLine.
					'order' => 8,
				);
			}
		} elseif ( is_question_tags() ) {
			$navs['page'] = array(
				'title' => __( 'Tags', 'anspress-question-answer' ),
				'link'  => ap_get_link_to( 'tags' ),
				'order' => 8,
			);
		}

		return $navs;
	}

	/**
	 * Handle tags suggestion on question form
	 */
	public function ap_tags_suggestion() {
		$keyword = ap_sanitize_unslash( 'q', 'r' );

		$tags = get_terms(
			array(
				'taxonomy'   => 'question_tag',
				'orderby'    => 'count',
				'order'      => 'DESC',
				'hide_empty' => false,
				'search'     => $keyword,
				'number'     => 8,
			)
		);

		if ( $tags ) {
			$items = array();
			foreach ( $tags as $k => $t ) {
				$items [ $k ] = $t->slug;
			}

			$result = array(
				'status' => true,
				'items'  => $items,
			);
			wp_send_json( $result );
		}

		wp_send_json( array( 'status' => false ) );
	}

	/**
	 * Add category pages rewrite rule.
	 *
	 * @param array  $rules AnsPress rules.
	 * @param string $slug Slug.
	 * @param int    $base_page_id AnsPress base page id.
	 * @return array
	 */
	public function rewrite_rules( $rules, $slug, $base_page_id ) {
		$base_slug = get_page_uri( ap_opt( 'tags_page' ) );
		update_option( 'ap_tags_path', $base_slug, true );
		$lang_rule    = str_replace( ap_base_page_slug() . '/', '', $slug );
		$lang_rewrite = str_replace( ap_opt( 'base_page' ), '', $base_page_id );

		$cat_rules = array(
			$lang_rule . $base_slug . '/([^/]+)/page/?([0-9]{1,})/?$' => $lang_rewrite . 'index.php?question_tag=$matches[#]&ap_paged=$matches[#]&ap_page=tag',
			$lang_rule . $base_slug . '/([^/]+)/?$' => $lang_rewrite . 'index.php?question_tag=$matches[#]&ap_page=tag',
		);

		return $cat_rules + $rules;
	}

	/**
	 * Filter main questions query args. Modify and add tags args.
	 *
	 * @param  array $args Questions args.
	 * @return array
	 */
	public function ap_main_questions_args( $args ) {
		global $wp;
		$query = $wp->query_vars;

		$current_filter = ap_get_current_list_filters( 'qtag' );
		$tags_operator  = ! empty( $wp->query_vars['ap_tags_operator'] ) ? $wp->query_vars['ap_tags_operator'] : 'IN';

		if ( isset( $query['ap_tags'] ) && is_array( $query['ap_tags'] ) ) {
			$args['tax_query'][] = array(
				'taxonomy' => 'question_tag',
				'field'    => 'slug',
				'terms'    => $query['ap_tags'],
				'operator' => $tags_operator,
			);
		} elseif ( ! empty( $current_filter ) ) {
			$args['tax_query'][] = array(
				'taxonomy' => 'question_tag',
				'field'    => 'term_id',
				'terms'    => $current_filter,
				'operator' => 'IN',
			);
		}

		return $args;
	}

	/**
	 * Add tags sorting in list filters
	 *
	 * @param array $filters Filters.
	 * @return array
	 */
	public function ap_list_filters( $filters ) {
		global $wp;

		if ( ! isset( $wp->query_vars['ap_tags'] ) && 'tag' !== ap_current_page() ) {
			$filters['qtag'] = array(
				'title'    => __( 'Tag', 'anspress-question-answer' ),
				'search'   => true,
				'multiple' => true,
			);
		}

		if ( 'tags' === ap_current_page() ) {
			return array(
				'tags_order' => array(
					'title' => __( 'Order', 'anspress-question-answer' ),
				),
			);
		}

		return $filters;
	}

	/**
	 * Ajax callback for loading order by filter.
	 *
	 * @since 4.0.0
	 */
	public function load_filter_tag() {
		$filter = ap_sanitize_unslash( 'filter', 'r' );
		check_ajax_referer( 'filter_' . $filter, '__nonce' );
		$search = ap_sanitize_unslash( 'search', 'r', false );

		ap_ajax_json(
			array(
				'success'  => true,
				'items'    => ap_get_tag_filter( $search ),
				'multiple' => true,
				'nonce'    => wp_create_nonce( 'filter_' . $filter ),
			)
		);
	}

	/**
	 * Ajax callback for loading order by filter for tags.
	 *
	 * @since 4.0.0
	 */
	public function load_filter_tags_order() {
		$filter = ap_sanitize_unslash( 'filter', 'r' );
		check_ajax_referer( 'filter_' . $filter, '__nonce' );

		ap_ajax_json(
			array(
				'success' => true,
				'items'   => array(
					array(
						'key'   => 'tags_order',
						'value' => 'popular',
						'label' => __( 'Popular', 'anspress-question-answer' ),
					),
					array(
						'key'   => 'tags_order',
						'value' => 'new',
						'label' => __( 'New', 'anspress-question-answer' ),
					),
					array(
						'key'   => 'tags_order',
						'value' => 'name',
						'label' => __( 'Name', 'anspress-question-answer' ),
					),
				),
				'nonce'   => wp_create_nonce( 'filter_' . $filter ),
			)
		);
	}

	/**
	 * Output active tag in filter
	 *
	 * @param bool  $active Is active.
	 * @param mixed $filter Current filter.
	 * @since 4.0.0
	 */
	public function filter_active_tag( $active, $filter ) {
		$current_filters = ap_get_current_list_filters( 'qtag' );

		if ( ! empty( $current_filters ) ) {
			$args = array(
				'taxonomy'      => 'question_tag',
				'hierarchical'  => true,
				'hide_if_empty' => true,
				'number'        => 2,
				'include'       => $current_filters,
			);

			$terms = get_terms( $args );

			if ( $terms ) {
				$active_terms = array();
				foreach ( (array) $terms as $t ) {
					$active_terms[] = $t->name;
				}

				$count = count( $current_filters );

				// translators: Placeholder contains count.
				$more_label = sprintf( __( ', %d+', 'anspress-question-answer' ), $count - 2 );

				return ': <span class="ap-filter-active">' . implode( ', ', $active_terms ) . ( $count > 2 ? $more_label : '' ) . '</span>';
			}
		}
	}

	/**
	 * Output active tags_order in filter
	 *
	 * @param string $active Active filter.
	 * @param array  $filter Filter.
	 * @since 4.1.0
	 */
	public function filter_active_tags_order( $active, $filter ) {
		$tags_order = ap_get_current_list_filters( 'tags_order' );
		$tags_order = ! empty( $tags_order ) ? $tags_order : 'popular';

		$orders = array(
			'popular' => __( 'Popular', 'anspress-question-answer' ),
			'new'     => __( 'New', 'anspress-question-answer' ),
			'name'    => __( 'Name', 'anspress-question-answer' ),
		);

		$active = isset( $orders[ $tags_order ] ) ? $orders[ $tags_order ] : '';

		return ': <span class="ap-filter-active">' . $active . '</span>';
	}

	/**
	 * Modify current page to show tag archive.
	 *
	 * @param string $query_var Current page.
	 * @return string
	 * @since 4.1.0
	 */
	public function ap_current_page( $query_var ) {
		if ( 'tags' === $query_var && 'tag' === get_query_var( 'ap_page' ) ) {
			return 'tag';
		}

		return $query_var;
	}

	/**
	 * Modify main query to show tag archive.
	 *
	 * @param array|null $posts Array of objects.
	 * @param object     $query Wp_Query object.
	 *
	 * @return array|null
	 * @since 4.1.0
	 */
	public function modify_query_archive( $posts, $query ) {
		if ( $query->is_main_query() &&
			$query->is_tax( 'question_tag' ) &&
			'tag' === get_query_var( 'ap_page' ) ) {
			$query->found_posts   = 1;
			$query->max_num_pages = 1;
			$page                 = get_page( ap_opt( 'tags_page' ) );
			$page->post_title     = get_queried_object()->name;
			$posts                = array( $page );
		}

		return $posts;
	}
}

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Add your comment