Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/Html.tar

Query.php000066600000014575151147003600006377 0ustar00<?php
namespace AIOSEO\Plugin\Common\Sitemap\Html;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles all queries for the HTML sitemap.
 *
 * @since 4.1.3
 */
class Query {
	/**
	 * Returns all eligible sitemap entries for a given post type.
	 *
	 * @since 4.1.3
	 *
	 * @param  string $postType   The post type.
	 * @param  array  $attributes The attributes.
	 * @return array              The post objects.
	 */
	public function posts( $postType, $attributes ) {
		$fields  = '`ID`, `post_title`,';
		$fields .= '`post_parent`, `post_date_gmt`, `post_modified_gmt`';

		$orderBy = '';
		switch ( $attributes['order_by'] ) {
			case 'last_updated':
				$orderBy = 'post_modified_gmt';
				break;
			case 'alphabetical':
				$orderBy = 'post_title';
				break;
			case 'id':
				$orderBy = 'ID';
				break;
			case 'publish_date':
			default:
				$orderBy = 'post_date_gmt';
				break;
		}

		switch ( strtolower( $attributes['order'] ) ) {
			case 'desc':
				$orderBy .= ' DESC';
				break;
			default:
				$orderBy .= ' ASC';
		}

		$query = aioseo()->core->db
			->start( 'posts' )
			->select( $fields )
			->where( 'post_status', 'publish' )
			->where( 'post_type', $postType );

		$excludedPosts = $this->getExcludedObjects( $attributes );
		if ( $excludedPosts ) {
			$query->whereRaw( "( `ID` NOT IN ( $excludedPosts ) )" );
		}

		$posts = $query->orderBy( $orderBy )
			->run()
			->result();

		foreach ( $posts as $post ) {
			$post->ID = (int) $post->ID;
		}

		return $posts;
	}

	/**
	 * Returns all eligble sitemap entries for a given taxonomy.
	 *
	 * @since 4.1.3
	 *
	 * @param  string $taxonomy   The taxonomy name.
	 * @param  array  $attributes The attributes.
	 * @return array              The term objects.
	 */
	public function terms( $taxonomy, $attributes = [] ) {
		$fields                 = 't.term_id, t.name, tt.parent';
		$termRelationshipsTable = aioseo()->core->db->db->prefix . 'term_relationships';
		$termTaxonomyTable      = aioseo()->core->db->db->prefix . 'term_taxonomy';

		$orderBy = '';
		switch ( $attributes['order_by'] ) {
			case 'alphabetical':
				$orderBy = 't.name';
				break;
			// We can only sort by date after getting the terms.
			case 'id':
			case 'publish_date':
			case 'last_updated':
			default:
				$orderBy = 't.term_id';
				break;
		}

		switch ( strtolower( $attributes['order'] ) ) {
			case 'desc':
				$orderBy .= ' DESC';
				break;
			default:
				$orderBy .= ' ASC';
		}

		$query = aioseo()->core->db
			->start( 'terms as t' )
			->select( $fields )
			->join( 'term_taxonomy as tt', 't.term_id = tt.term_id' )
			->whereRaw( "
			( `t`.`term_id` IN
				(
					SELECT `tt`.`term_id`
					FROM `$termTaxonomyTable` as tt
					WHERE `tt`.`taxonomy` = '$taxonomy'
					AND `tt`.`count` > 0
				)
			)" );

		$excludedTerms = $this->getExcludedObjects( $attributes, false );
		if ( $excludedTerms ) {
			$query->whereRaw("
				( `t`.`term_id` NOT IN
					(
						SELECT `tr`.`term_taxonomy_id`
						FROM `$termRelationshipsTable` as tr
						WHERE `tr`.`term_taxonomy_id` IN ( $excludedTerms )
					)
				)" );
		}

		$terms = $query->orderBy( $orderBy )
			->run()
			->result();

		foreach ( $terms as $term ) {
			$term->term_id  = (int) $term->term_id;
			$term->taxonomy = $taxonomy;
		}

		$shouldSort = false;
		if ( 'last_updated' === $attributes['order_by'] ) {
			$shouldSort = true;
			foreach ( $terms as $term ) {
				$term->timestamp = strtotime( aioseo()->sitemap->content->getTermLastModified( $term->term_id ) );
			}
		}

		if ( 'publish_date' === $attributes['order_by'] ) {
			$shouldSort = true;
			foreach ( $terms as $term ) {
				$term->timestamp = strtotime( $this->getTermPublishDate( $term->term_id ) );
			}
		}

		if ( $shouldSort ) {
			if ( 'asc' === strtolower( $attributes['order'] ) ) {
				usort( $terms, function( $term1, $term2 ) {
					return $term1->timestamp > $term2->timestamp ? 1 : 0;
				} );
			} else {
				usort( $terms, function( $term1, $term2 ) {
					return $term1->timestamp < $term2->timestamp ? 1 : 0;
				} );
			}
		}

		return $terms;
	}

	/**
	 * Returns a list of date archives that can be included.
	 *
	 * @since 4.1.3
	 *
	 * @return array The date archives.
	 */
	public function archives() {
		$result = aioseo()->core->db
			->start( 'posts', false, 'SELECT DISTINCT' )
			->select( 'YEAR(post_date) AS year, MONTH(post_date) AS month' )
			->where( 'post_type', 'post' )
			->where( 'post_status', 'publish' )
			->whereRaw( "post_password=''" )
			->orderBy( 'year DESC' )
			->orderBy( 'month DESC' )
			->run()
			->result();

		$dates = [];
		foreach ( $result as $date ) {
			$dates[ $date->year ][ $date->month ] = 1;
		}

		return $dates;
	}

	/**
	 * Returns the publish date for a given term.
	 * This is the publish date of the oldest post that is assigned to the term.
	 *
	 * @since 4.1.3
	 *
	 * @param  int $termId The term ID.
	 * @return int         The publish date timestamp.
	 */
	public function getTermPublishDate( $termId ) {
		$termRelationshipsTable = aioseo()->core->db->db->prefix . 'term_relationships';

		$post = aioseo()->core->db
			->start( 'posts as p' )
			->select( 'MIN(`p`.`post_date_gmt`) as publish_date' )
			->whereRaw( "
			( `p`.`ID` IN
				(
					SELECT `tr`.`object_id`
					FROM `$termRelationshipsTable` as tr
					WHERE `tr`.`term_taxonomy_id` = '$termId'
				)
			)" )
			->run()
			->result();

		return ! empty( $post[0]->publish_date ) ? strtotime( $post[0]->publish_date ) : 0;
	}

	/**
	 * Returns a comma-separated string of excluded object IDs.
	 *
	 * @since 4.1.3
	 *
	 * @param  array   $attributes The attributes.
	 * @param  boolean $posts      Whether the objects are posts.
	 * @return string              The excluded object IDs.
	 */
	private function getExcludedObjects( $attributes, $posts = true ) {
		$excludedObjects = $posts
			? aioseo()->sitemap->helpers->excludedPosts()
			: aioseo()->sitemap->helpers->excludedTerms();
		$key             = $posts ? 'excluded_posts' : 'excluded_terms';

		if ( ! empty( $attributes[ $key ] ) ) {
			$ids = explode( ',', $excludedObjects );

			$extraIds = [];
			if ( is_array( $attributes[ $key ] ) ) {
				$extraIds = $attributes[ $key ];
			}
			if ( is_string( $attributes[ $key ] ) ) {
				$extraIds = array_map( 'trim', explode( ',', $attributes[ $key ] ) );
			}

			$ids = array_filter( array_merge( $ids, $extraIds ), 'is_numeric' );

			$excludedObjects = esc_sql( implode( ', ', $ids ) );
		}

		return $excludedObjects;
	}
}Shortcode.php000066600000001336151147003600007213 0ustar00<?php
namespace AIOSEO\Plugin\Common\Sitemap\Html;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles the HTML sitemap shortcode.
 *
 * @since 4.1.3
 */
class Shortcode {
	/**
	 * Class constructor.
	 *
	 * @since 4.1.3
	 */
	public function __construct() {
		add_shortcode( 'aioseo_html_sitemap', [ $this, 'render' ] );
	}

	/**
	 * Shortcode callback.
	 *
	 * @since 4.1.3
	 *
	 * @param  array       $attributes The shortcode attributes.
	 * @return string|void             The HTML sitemap.
	 */
	public function render( $attributes ) {
		$attributes = aioseo()->htmlSitemap->frontend->getAttributes( $attributes );

		return aioseo()->htmlSitemap->frontend->output( false, $attributes );
	}
}Frontend.php000066600000031777151147003600007054 0ustar00<?php
namespace AIOSEO\Plugin\Common\Sitemap\Html;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles the output of the HTML sitemap.
 *
 * @since 4.1.3
 */
class Frontend {
	/**
	 * Instance of Query class.
	 *
	 * @since 4.1.3
	 *
	 * @var Query
	 */
	public $query;

	/**
	 * The attributes for the block/widget/shortcode.
	 *
	 * @since 4.1.3
	 *
	 * @var array
	 */
	private $attributes = [];

	/**
	 * Class constructor.
	 *
	 * @since 4.1.3
	 */
	public function __construct() {
		$this->query = new Query();
	}

	/**
	 * Returns the attributes.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $attributes The user-defined attributes
	 * @return array             The defaults with user-defined attributes merged.
	 */
	public function getAttributes( $attributes = [] ) {
		aioseo()->sitemap->type = 'html';

		$defaults = [
			'label_tag'        => 'h4',
			'show_label'       => true,
			'order'            => aioseo()->options->sitemap->html->sortDirection,
			'order_by'         => aioseo()->options->sitemap->html->sortOrder,
			'nofollow_links'   => false,
			'publication_date' => aioseo()->options->sitemap->html->publicationDate,
			'archives'         => aioseo()->options->sitemap->html->compactArchives,
			'post_types'       => aioseo()->sitemap->helpers->includedPostTypes(),
			'taxonomies'       => aioseo()->sitemap->helpers->includedTaxonomies(),
			'excluded_posts'   => [],
			'excluded_terms'   => [],
			'is_admin'         => false
		];

		$attributes                   = shortcode_atts( $defaults, $attributes );
		$attributes['show_label']     = filter_var( $attributes['show_label'], FILTER_VALIDATE_BOOLEAN );
		$attributes['nofollow_links'] = filter_var( $attributes['nofollow_links'], FILTER_VALIDATE_BOOLEAN );
		$attributes['is_admin']       = filter_var( $attributes['is_admin'], FILTER_VALIDATE_BOOLEAN );

		return $attributes;
	}

	/**
	 * Formats the publish date according to what's set under Settings > General.
	 *
	 * @since 4.1.3
	 *
	 * @param  string $date The date that should be formatted.
	 * @return string       The formatted date.
	 */
	private function formatDate( $date ) {
		$dateFormat = apply_filters( 'aioseo_html_sitemap_date_format', get_option( 'date_format' ) );

		return date_i18n( $dateFormat, strtotime( $date ) );
	}

	/**
	 * Returns the posts of a given post type that should be included.
	 *
	 * @since 4.1.3
	 *
	 * @param  string $postType       The post type.
	 * @param  array  $additionalArgs Additional arguments for the post query (optional).
	 * @return array                  The post entries.
	 */
	private function posts( $postType, $additionalArgs = [] ) {
		$posts = $this->query->posts( $postType, $additionalArgs );
		if ( ! $posts ) {
			return [];
		}

		$entries = [];
		foreach ( $posts as $post ) {
			$entry = [
				'id'     => $post->ID,
				'title'  => get_the_title( $post ),
				'loc'    => get_permalink( $post->ID ),
				'date'   => $this->formatDate( $post->post_date_gmt ),
				'parent' => ! empty( $post->post_parent ) ? $post->post_parent : null
			];

			$entries[] = $entry;
		}

		return apply_filters( 'aioseo_html_sitemap_posts', $entries, $postType );
	}

	/**
	 * Returns the terms of a given taxonomy that should be included.
	 *
	 * @since 4.1.3
	 *
	 * @param  string $taxonomy        The taxonomy name.
	 * @param  array  $additionalArgs  Additional arguments for the query (optional).
	 * @return array                   The term entries.
	 */
	private function terms( $taxonomy, $additionalArgs = [] ) {
		$terms = $this->query->terms( $taxonomy, $additionalArgs );
		if ( ! $terms ) {
			return [];
		}

		$entries = [];
		foreach ( $terms as $term ) {
			$entries[] = [
				'id'     => $term->term_id,
				'title'  => $term->name,
				'loc'    => get_term_link( $term->term_id ),
				'parent' => ! empty( $term->parent ) ? $term->parent : null
			];
		}

		return apply_filters( 'aioseo_html_sitemap_terms', $entries, $taxonomy );
	}

	/**
	 * Outputs the sitemap to the frontend.
	 *
	 * @since 4.1.3
	 *
	 * @param  bool  $echo       Whether the sitemap should be printed to the screen.
	 * @param  array $attributes The shortcode attributes.
	 * @return string|void       The HTML sitemap.
	 */
	public function output( $echo = true, $attributes = [] ) {
		$this->attributes = $attributes;

		if ( ! aioseo()->options->sitemap->html->enable ) {
			return;
		}

		aioseo()->sitemap->type = 'html';
		if ( filter_var( $attributes['archives'], FILTER_VALIDATE_BOOLEAN ) ) {
			return ( new CompactArchive() )->output( $attributes, $echo );
		}

		if ( ! empty( $attributes['default'] ) ) {
			$attributes = $this->getAttributes();
		}

		$noResultsMessage = esc_html__( 'No posts/terms could be found.', 'all-in-one-seo-pack' );
		if ( empty( $this->attributes['post_types'] ) && empty( $this->attributes['taxonomies'] ) ) {
			if ( $echo ) {
				echo $noResultsMessage; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			}

			return $noResultsMessage;
		}

		// TODO: Consider moving all remaining HTML code below to a dedicated view instead of printing it in PHP.
		$sitemap = sprintf(
			'<div class="aioseo-html-sitemap%s">',
			! $this->attributes['show_label'] ? ' labels-hidden' : ''
		);

		$sitemap .= '<style>.aioseo-html-sitemap.labels-hidden ul { margin: 0; }</style>';

		$hasPosts  = false;
		$postTypes = $this->getIncludedObjects( $this->attributes['post_types'] );
		foreach ( $postTypes as $postType ) {
			if ( 'attachment' === $postType ) {
				continue;
			}

			// Check if post type is still registered.
			if ( ! in_array( $postType, aioseo()->helpers->getPublicPostTypes( true ), true ) ) {
				continue;
			}

			$posts = $this->posts( $postType, $attributes );
			if ( empty( $posts ) ) {
				continue;
			}

			$hasPosts = true;

			$postTypeObject = get_post_type_object( $postType );
			$label          = ! empty( $postTypeObject->label ) ? $postTypeObject->label : ucfirst( $postType );

			$sitemap .= '<div class="aioseo-html-' . esc_attr( $postType ) . '-sitemap">';
			$sitemap .= $this->generateLabel( $label );

			if ( is_post_type_hierarchical( $postType ) ) {
				$sitemap .= $this->generateHierarchicalList( $posts ) . '</div>';
				if ( $this->attributes['show_label'] ) {
					$sitemap .= '<br />';
				}
				continue;
			}

			$sitemap .= $this->generateList( $posts );
			if ( $this->attributes['show_label'] ) {
				$sitemap .= '<br />';
			}
		}

		$hasTerms   = false;
		$taxonomies = $this->getIncludedObjects( $this->attributes['taxonomies'], false );
		foreach ( $taxonomies as $taxonomy ) {
			// Check if post type is still registered.
			if ( ! in_array( $taxonomy, aioseo()->helpers->getPublicTaxonomies( true ), true ) ) {
				continue;
			}

			$terms = $this->terms( $taxonomy, $attributes );
			if ( empty( $terms ) ) {
				continue;
			}

			$hasTerms = true;

			$taxonomyObject = get_taxonomy( $taxonomy );
			$label          = ! empty( $taxonomyObject->label ) ? $taxonomyObject->label : ucfirst( $taxonomy );

			$sitemap .= '<div class="aioseo-html-' . esc_attr( $taxonomy ) . '-sitemap">';
			$sitemap .= $this->generateLabel( $label );

			if ( is_taxonomy_hierarchical( $taxonomy ) ) {
				$sitemap .= $this->generateHierarchicalList( $terms ) . '</div>';
				if ( $this->attributes['show_label'] ) {
					$sitemap .= '<br />';
				}
				continue;
			}

			$sitemap .= $this->generateList( $terms );
			if ( $this->attributes['show_label'] ) {
				$sitemap .= '<br />';
			}
		}

		$sitemap .= '</div>';

		// Check if we actually were able to fetch any results.
		if ( ! $hasPosts && ! $hasTerms ) {
			$sitemap = $noResultsMessage;
		}

		if ( $echo ) {
			echo $sitemap; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}

		return $sitemap;
	}

	/**
	 * Generates the label for a section of the sitemap.
	 *
	 * @since 4.1.3
	 *
	 * @param  string $label The label.
	 * @return string        The HTML code for the label.
	 */
	private function generateLabel( $label ) {
		$labelTag = ! empty( $this->attributes['label_tag'] ) ? $this->attributes['label_tag'] : 'h4';

		return $this->attributes['show_label']
			? wp_kses_post( sprintf( '<%2$s>%1$s</%2$s>', $label, $labelTag ) )
			: '';
	}

	/**
	 * Generates the HTML for a non-hierarchical list of objects.
	 *
	 * @since 4.1.3
	 *
	 * @param  array  $objects The object.
	 * @return string          The HTML code.
	 */
	private function generateList( $objects ) {
		$list = '<ul>';
		foreach ( $objects as $object ) {
			$list .= $this->generateListItem( $object ) . '</li>';
		}

		return $list . '</ul></div>';
	}

	/**
	 * Generates a list item for an object (without the closing tag).
	 * We cannot close it as the caller might need to generate a hierarchical structure inside the list item.
	 *
	 * @since 4.1.3
	 *
	 * @param  array  $object The object.
	 * @return string         The HTML code.
	 */
	private function generateListItem( $object ) {
		$li = '';
		if ( ! empty( $object['title'] ) ) {
			$li .= '<li>';

			// add nofollow to the link.
			if ( filter_var( $this->attributes['nofollow_links'], FILTER_VALIDATE_BOOLEAN ) ) {
				$li .= sprintf(
					'<a href="%1$s" %2$s %3$s>',
					esc_url( $object['loc'] ),
					'rel="nofollow"',
					$this->attributes['is_admin'] ? 'target="_blank"' : ''
				);
			} else {
				$li .= sprintf(
					'<a href="%1$s" %2$s>',
					esc_url( $object['loc'] ),
					$this->attributes['is_admin'] ? 'target="_blank"' : ''
				);
			}

			$li .= sprintf( '%s', esc_attr( $object['title'] ) );

			// add publication date on the list item.
			if ( ! empty( $object['date'] ) && filter_var( $this->attributes['publication_date'], FILTER_VALIDATE_BOOLEAN ) ) {
				$li .= sprintf( ' (%s)', esc_attr( $object['date'] ) );
			}

			$li .= '</a>';
		}

		return $li;
	}

	/**
	 * Generates the HTML for a hierarchical list of objects.
	 *
	 * @since 4.1.3
	 *
	 * @param  array  $objects The objects.
	 * @return string          The HTML of the hierarchical objects section.
	 */
	private function generateHierarchicalList( $objects ) {
		if ( empty( $objects ) ) {
			return '';
		}

		$objects = $this->buildHierarchicalTree( $objects );

		$list = '<ul>';
		foreach ( $objects as $object ) {
			$list .= $this->generateListItem( $object );

			if ( ! empty( $object['children'] ) ) {
				$list .= $this->generateHierarchicalTree( $object );
			}
			$list .= '</li>';
		}
		$list .= '</ul>';

		return $list;
	}

	/**
	 * Recursive helper function for generateHierarchicalList().
	 * Generates hierarchical structure for objects with child objects.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $object The object.
	 * @return string        The HTML code of the hierarchical tree.
	 */
	private function generateHierarchicalTree( $object ) {
		static $nestedLevel = 0;

		$tree = '<ul>';
		foreach ( $object['children'] as $child ) {
			$nestedLevel++;
			$tree .= $this->generateListItem( $child );
			if ( ! empty( $child['children'] ) ) {
				$tree .= $this->generateHierarchicalTree( $child );
			}
			$tree .= '</li>';
		}
		$tree .= '</ul>';

		return $tree;
	}

	/**
	 * Builds the structure for hierarchical objects that have a parent.
	 *
	 * @since 4.1.3
	 * @version 4.2.8
	 *
	 * @param  array $objects The list of hierarchical objects.
	 * @return array          Multidimensional array with the hierarchical structure.
	 */
	private function buildHierarchicalTree( $objects ) {
		$topLevelIds = [];
		$objects     = json_decode( wp_json_encode( $objects ) );

		foreach ( $objects as $listItem ) {

			// Create an array of top level IDs for later reference.
			if ( empty( $listItem->parent ) ) {
				array_push( $topLevelIds, $listItem->id );
			}

			// Create an array of children that belong to the current item.
			$children = array_filter( $objects, function( $child ) use ( $listItem ) {
				if ( ! empty( $child->parent ) ) {
					return absint( $child->parent ) === absint( $listItem->id );
				}
			} );

			if ( ! empty( $children ) ) {
				$listItem->children = $children;
			}
		}

		// Remove child objects from the root level since they've all been nested.
		$objects = array_filter( $objects, function ( $item ) use ( $topLevelIds ) {
			return in_array( $item->id, $topLevelIds, true );
		} );

		return array_values( json_decode( wp_json_encode( $objects ), true ) );
	}

	/**
	 * Returns the names of the included post types or taxonomies.
	 *
	 * @since 4.1.3
	 *
	 * @param  array|string $objects      The included post types/taxonomies.
	 * @param  boolean      $arePostTypes Whether the objects are post types.
	 * @return array                      The names of the included post types/taxonomies.
	 */
	private function getIncludedObjects( $objects, $arePostTypes = true ) {
		if ( is_array( $objects ) ) {
			return $objects;
		}

		if ( empty( $objects ) ) {
			return [];
		}

		$exploded = explode( ',', $objects );
		$objects  = array_map( function( $object ) {
			return trim( $object );
		}, $exploded );

		$publicObjects = $arePostTypes
			? aioseo()->helpers->getPublicPostTypes( true )
			: aioseo()->helpers->getPublicTaxonomies( true );

		$objects = array_filter( $objects, function( $object ) use ( $publicObjects ) {
			return in_array( $object, $publicObjects, true );
		});

		return $objects;
	}
}Block.php000066600000006636151147003600006323 0ustar00<?php
namespace AIOSEO\Plugin\Common\Sitemap\Html;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles the HTML sitemap block.
 *
 * @since 4.1.3
 */
class Block {
	/**
	 * Class constructor.
	 *
	 * @since 4.1.1
	 */
	public function __construct() {
		add_action( 'init', [ $this, 'register' ] );
	}

	/**
	 * Registers the block.
	 *
	 * @since  4.1.3
	 *
	 * @return void
	 */
	public function register() {
		aioseo()->blocks->registerBlock(
			'aioseo/html-sitemap', [
				'attributes'      => [
					'default'          => [
						'type'    => 'boolean',
						'default' => true
					],
					'post_types'       => [
						'type'    => 'string',
						'default' => wp_json_encode( [ 'post', 'page' ] )
					],
					'post_types_all'   => [
						'type'    => 'boolean',
						'default' => true
					],
					'taxonomies'       => [
						'type'    => 'string',
						'default' => wp_json_encode( [ 'category', 'post_tag' ] )
					],
					'taxonomies_all'   => [
						'type'    => 'boolean',
						'default' => true
					],
					'show_label'       => [
						'type'    => 'boolean',
						'default' => true
					],
					'archives'         => [
						'type'    => 'boolean',
						'default' => false
					],
					'publication_date' => [
						'type'    => 'boolean',
						'default' => true
					],
					'nofollow_links'   => [
						'type'    => 'boolean',
						'default' => false
					],
					'order_by'         => [
						'type'    => 'string',
						'default' => 'publish_date'
					],
					'order'            => [
						'type'    => 'string',
						'default' => 'asc'
					],
					'excluded_posts'   => [
						'type'    => 'string',
						'default' => wp_json_encode( [] )
					],
					'excluded_terms'   => [
						'type'    => 'string',
						'default' => wp_json_encode( [] )
					],
					'is_admin'         => [
						'type'    => 'boolean',
						'default' => false
					]
				],
				'render_callback' => [ $this, 'render' ],
				'editor_style'    => 'aioseo-html-sitemap'
			]
		);
	}

	/**
	 * Renders the block.
	 *
	 * @since 4.1.3
	 *
	 * @param  array  $attributes The attributes.
	 * @return string             The HTML sitemap code.
	 */
	public function render( $attributes ) {
		if ( ! $attributes['default'] ) {
			$jsonFields = [ 'post_types', 'taxonomies', 'excluded_posts', 'excluded_terms' ];
			foreach ( $attributes as $k => $v ) {
				if ( in_array( $k, $jsonFields, true ) ) {
					$attributes[ $k ] = json_decode( $v );
				}
			}

			$attributes['excluded_posts'] = $this->extractIds( $attributes['excluded_posts'] );
			$attributes['excluded_terms'] = $this->extractIds( $attributes['excluded_terms'] );

			if ( ! empty( $attributes['post_types_all'] ) ) {
				$attributes['post_types'] = aioseo()->helpers->getPublicPostTypes( true );
			}
			if ( ! empty( $attributes['taxonomies_all'] ) ) {
				$attributes['taxonomies'] = aioseo()->helpers->getPublicTaxonomies( true );
			}
		} else {
			$attributes = [];
		}

		$attributes = aioseo()->htmlSitemap->frontend->getAttributes( $attributes );

		return aioseo()->htmlSitemap->frontend->output( false, $attributes );
	}

	/**
	 * Extracts the IDs from the excluded objects.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $objects The objects.
	 * @return array          The object IDs.
	 */
	private function extractIds( $objects ) {
		return array_map( function ( $object ) {
			$object = json_decode( $object );

			return (int) $object->value;
		}, $objects );
	}
}Sitemap.php000066600000014025151147003600006662 0ustar00<?php
namespace AIOSEO\Plugin\Common\Sitemap\Html {
	// Exit if accessed directly.
	if ( ! defined( 'ABSPATH' ) ) {
		exit;
	}

	/**
	* Main class for the HTML sitemap.
	*
	* @since 4.1.3
	*/
	class Sitemap {
		/** Instance of the frontend class.
		 *
		 * @since 4.1.3
		 *
		 * @var Frontend
		 */
		public $frontend;

		/**
		 * Instance of the shortcode class.
		 *
		 * @since 4.1.3
		 *
		 * @var Shortcode
		 */
		public $shortcode;

		/**
		 * Instance of the block class.
		 *
		 * @since 4.1.3
		 *
		 * @var Block
		 */
		public $block;

		/**
		 * Whether the current queried page is the dedicated sitemap page.
		 *
		 * @since 4.1.3
		 *
		 * @var bool
		 */
		public $isDedicatedPage = false;

		/**
		 * Class constructor.
		 *
		 * @since 4.1.3
		 */
		public function __construct() {
			$this->frontend  = new Frontend();
			$this->shortcode = new Shortcode();
			$this->block     = new Block();

			add_action( 'widgets_init', [ $this, 'registerWidget' ] );
			add_filter( 'aioseo_canonical_url', [ $this, 'getCanonicalUrl' ] );

			if ( ! is_admin() || wp_doing_ajax() || wp_doing_cron() ) {
				add_action( 'template_redirect', [ $this, 'checkForDedicatedPage' ] );
			}
		}

		/**
		 * Register our HTML sitemap widget.
		 *
		 * @since 4.1.3
		 *
		 * @return void
		 */
		public function registerWidget() {
			if ( aioseo()->helpers->canRegisterLegacyWidget( 'aioseo-html-sitemap-widget' ) ) {
				register_widget( 'AIOSEO\Plugin\Common\Sitemap\Html\Widget' );
			}
		}

		/**
		 * Checks whether the current request is for our dedicated HTML sitemap page.
		 *
		 * @since 4.1.3
		 *
		 * @return void
		 */
		public function checkForDedicatedPage() {
			if ( ! aioseo()->options->sitemap->html->enable ) {
				return;
			}

			global $wp;
			$sitemapUrl = aioseo()->options->sitemap->html->pageUrl;
			if ( ! $sitemapUrl || empty( $wp->request ) ) {
				return;
			}

			$sitemapUrl = wp_parse_url( $sitemapUrl );
			if ( empty( $sitemapUrl['path'] ) ) {
				return;
			}

			$sitemapUrl = trim( $sitemapUrl['path'], '/' );
			if ( trim( $wp->request, '/' ) === $sitemapUrl ) {
				$this->isDedicatedPage = true;
				$this->generatePage();
			}
		}

		/**
		 * Checks whether the current request is for our dedicated HTML sitemap page.
		 *
		 * @since 4.1.3
		 *
		 * @return void
		 */
		private function generatePage() {
			global $wp_query, $wp, $post; // phpcs:ignore Squiz.NamingConventions.ValidVariableName

			$postId     = -1337; // Set a negative ID to prevent conflicts with existing posts.
			$sitemapUrl = aioseo()->options->sitemap->html->pageUrl;
			$path       = trim( wp_parse_url( $sitemapUrl )['path'], '/' );

			$fakePost                 = new \stdClass();
			$fakePost->ID             = $postId;
			$fakePost->post_author    = 1;
			$fakePost->post_date      = current_time( 'mysql' );
			$fakePost->post_date_gmt  = current_time( 'mysql', 1 );
			$fakePost->post_title     = apply_filters( 'aioseo_html_sitemap_page_title', __( 'Sitemap', 'all-in-one-seo-pack' ) );
			$fakePost->post_content   = '[aioseo_html_sitemap archives=false]';
			// We're using post instead of page to prevent calls to get_ancestors(), which will trigger errors.
			// To loead the page template, we set is_page to true on the WP_Query object.
			$fakePost->post_type      = 'post';
			$fakePost->post_status    = 'publish';
			$fakePost->comment_status = 'closed';
			$fakePost->ping_status    = 'closed';
			$fakePost->post_name      = $path;
			$fakePost->filter         = 'raw'; // Needed to prevent calls to the database when creating the WP_Post object.
			$postObject               = new \WP_Post( $fakePost );

			$post = $postObject;

			// We'll set as much properties on the WP_Query object as we can to prevent conflicts with other plugins/themes.
			// phpcs:disable Squiz.NamingConventions.ValidVariableName
			$wp_query->is_404            = false;
			$wp_query->is_page           = true;
			$wp_query->is_singular       = true;
			$wp_query->post              = $postObject;
			$wp_query->posts             = [ $postObject ];
			$wp_query->queried_object    = $postObject;
			$wp_query->queried_object_id = $postId;
			$wp_query->found_posts       = 1;
			$wp_query->post_count        = 1;
			$wp_query->max_num_pages     = 1;

			unset( $wp_query->query['error'] );
			$wp_query->query_vars['error'] = '';
			// phpcs:enable Squiz.NamingConventions.ValidVariableName

			// We need to add the post object to the cache so that get_post() calls don't trigger database calls.
			wp_cache_add( $postId, $postObject, 'posts' );

			$GLOBALS['wp_query'] = $wp_query; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
			$wp->register_globals();

			// Setting is_404 is not sufficient, so we still need to change the status code.
			status_header( 200 );
		}

		/**
		 * Get the canonical URL for the dedicated HTML sitemap page.
		 *
		 * @since 4.5.7
		 *
		 * @param  string $originalUrl The canonical URL.
		 * @return string              The canonical URL.
		 */
		public function getCanonicalUrl( $originalUrl ) {
			$sitemapOptions = aioseo()->options->sitemap->html;

			if ( ! $sitemapOptions->enable || ! $this->isDedicatedPage ) {
				return $originalUrl;
			}

			// If the user has set a custom URL for the sitemap page, use that.
			if ( $sitemapOptions->pageUrl ) {
				return $sitemapOptions->pageUrl;
			}

			// Return the current URL of WP.
			global $wp;

			return home_url( $wp->request );
		}
	}
}

namespace {
	// Exit if accessed directly.
	if ( ! defined( 'ABSPATH' ) ) {
		exit;
	}

	if ( ! function_exists( 'aioseo_html_sitemap' ) ) {
		/**
		 * Global function that can be used to print the HTML sitemap.
		 *
		 * @since 4.1.3
		 *
		 * @param  array   $attributes User-defined attributes that override the default settings.
		 * @param  boolean $echo       Whether to echo the output or return it.
		 * @return string              The HTML sitemap code.
		 */
		function aioseo_html_sitemap( $attributes = [], $echo = true ) {
			$attributes = aioseo()->htmlSitemap->frontend->getAttributes( $attributes );

			return aioseo()->htmlSitemap->frontend->output( $echo, $attributes );
		}
	}
}CompactArchive.php000066600000005677151147003600010165 0ustar00<?php
namespace AIOSEO\Plugin\Common\Sitemap\Html;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles the Compact Archive's output.
 *
 * @since 4.1.3
 */
class CompactArchive {
	/**
	 * The shortcode attributes.
	 *
	 * @since 4.1.3
	 *
	 * @var array
	 */
	private $attributes;

	/**
	 * Outputs the compact archives sitemap.
	 *
	 * @since 4.1.3
	 *
	 * @param  array   $attributes The shortcode attributes.
	 * @param  boolean $echo       Whether the HTML code should be printed or returned.
	 * @return string              The HTML for the compact archive.
	 */
	public function output( $attributes, $echo = true ) {
		$dateArchives     = ( new Query() )->archives();
		$this->attributes = $attributes;

		if ( 'asc' === strtolower( $this->attributes['order'] ) ) {
			$dateArchives = array_reverse( $dateArchives, true );
		}

		$data = [
			'dateArchives' => $dateArchives,
			'lines'        => ''
		];
		foreach ( $dateArchives as $year => $months ) {
			$data['lines'] .= $this->generateYearLine( $year, $months ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}

		ob_start();
		aioseo()->templates->getTemplate( 'sitemap/html/compact-archive.php', $data );
		$output = ob_get_clean();

		if ( $echo ) {
			echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}

		return $output;
	}
	// phpcs:enable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable

	/**
	* Generates the HTML for a year line.
	*
	* @since 4.1.3
	*
	* @param  int    $year   The year archive.
	* @param  array  $months The month archives for the current year.
	* @return string         The HTML code for the year.
	*/
	protected function generateYearLine( $year, $months ) {
		$html = '<li><strong><a href="' . get_year_link( $year ) . '">' . esc_html( $year ) . '</a>: </strong> ';

		for ( $month = 1; $month <= 12; $month++ ) {
			$html .= $this->generateMonth( $year, $months, $month );
		}

		$html .= '</li>' . "\n";

		return wp_kses_post( $html );
	}

	/**
	 * Generates the HTML for a month.
	 *
	 * @since 4.1.3
	 *
	 * @param  int    $year   The year archive.
	 * @param  array  $months All month archives for the current year.
	 * @param  int    $month  The month archive.
	 * @return string         The HTML code for the month.
	 */
	public function generateMonth( $year, $months, $month ) {
		$hasPosts         = isset( $months[ $month ] );
		$dummyDate        = strtotime( "2009/{$month}/25" );
		$monthAbbrevation = date_i18n( 'M', $dummyDate );

		$html = '<span class="aioseo-empty-month">' . esc_html( $monthAbbrevation ) . '</span> ';
		if ( $hasPosts ) {
			$noFollow = filter_var( $this->attributes['nofollow_links'], FILTER_VALIDATE_BOOLEAN );
			$html     = sprintf(
				'<a href="%1$s" title="%2$s"%3$s>%4$s</a> ',
				get_month_link( $year, $month ),
				esc_attr( date_i18n( 'F Y', $dummyDate ) ),
				$noFollow ? ' rel="nofollow"' : '',
				esc_html( $monthAbbrevation )
			);
		}

		return $html;
	}
}Widget.php000066600000013607151147003600006510 0ustar00<?php
namespace AIOSEO\Plugin\Common\Sitemap\Html;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Widget.
 *
 * @since 4.1.3
 */
class Widget extends \WP_Widget {
	/**
	 * The default attributes.
	 *
	 * @since 4.2.7
	 *
	 * @var array
	 */
	private $defaults = [];

	/**
	 * Class constructor.
	 *
	 * @since 4.1.3
	 */
	public function __construct() {
		// The default widget settings.
		$this->defaults = [
			'title'            => '',
			'show_label'       => 'on',
			'archives'         => '',
			'nofollow_links'   => '',
			'order'            => 'asc',
			'order_by'         => 'publish_date',
			'publication_date' => 'on',
			'post_types'       => [ 'post', 'page' ],
			'taxonomies'       => [ 'category', 'post_tag' ],
			'excluded_posts'   => '',
			'excluded_terms'   => ''
		];

		$widgetSlug     = 'aioseo-html-sitemap-widget';
		$widgetOptions  = [
			'classname'   => $widgetSlug,
			// Translators: The short plugin name ("AIOSEO").
			'description' => sprintf( esc_html__( '%1$s HTML sitemap widget.', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME )
		];
		$controlOptions = [
			'id_base' => $widgetSlug
		];

		// Translators: 1 - The plugin short name ("AIOSEO").
		$name = sprintf( esc_html__( '%1$s - HTML Sitemap', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME );
		$name .= ' ' . esc_html__( '(legacy)', 'all-in-one-seo-pack' );
		parent::__construct( $widgetSlug, $name, $widgetOptions, $controlOptions );
	}

	/**
	 * Callback for the widget.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $args     The widget arguments.
	 * @param  array $instance The widget instance options.
	 * @return void
	 */
	public function widget( $args, $instance ) {
		if ( ! aioseo()->options->sitemap->html->enable ) {
			return;
		}

		// Merge with defaults.
		$instance = wp_parse_args( (array) $instance, $this->defaults );

		echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		if ( ! empty( $instance['title'] ) ) {
			echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,Generic.Files.LineLength.MaxExceeded
		}

		$instance = aioseo()->htmlSitemap->frontend->getAttributes( $instance );
		aioseo()->htmlSitemap->frontend->output( true, $instance );
		echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Callback to update the widget options.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $newOptions The new options.
	 * @param  array $oldOptions The old options.
	 * @return array             The new options.
	 */
	public function update( $newOptions, $oldOptions ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		$settings = [
			'title',
			'order',
			'order_by',
			'show_label',
			'publication_date',
			'archives',
			'excluded_posts',
			'excluded_terms'
		];

		foreach ( $settings as $setting ) {
			$newOptions[ $setting ] = ! empty( $newOptions[ $setting ] ) ? wp_strip_all_tags( $newOptions[ $setting ] ) : '';
		}

		$includedPostTypes = [];
		if ( ! empty( $newOptions['post_types'] ) ) {
			$postTypes = $this->getPublicPostTypes( true );
			foreach ( $newOptions['post_types'] as $v ) {
				if ( is_numeric( $v ) ) {
					$includedPostTypes[] = $postTypes[ $v ];
				} else {
					$includedPostTypes[] = $v;
				}
			}
		}
		$newOptions['post_types'] = $includedPostTypes;

		$includedTaxonomies = [];
		if ( ! empty( $newOptions['taxonomies'] ) ) {
			$taxonomies = aioseo()->helpers->getPublicTaxonomies( true );
			foreach ( $newOptions['taxonomies'] as $v ) {
				if ( is_numeric( $v ) ) {
					$includedTaxonomies[] = $taxonomies[ $v ];
				} else {
					$includedTaxonomies[] = $v;
				}
			}
		}
		$newOptions['taxonomies'] = $includedTaxonomies;

		if ( ! empty( $newOptions['excluded_posts'] ) ) {
			$newOptions['excluded_posts'] = $this->sanitizeExcludedIds( $newOptions['excluded_posts'] );
		}

		if ( ! empty( $newOptions['excluded_terms'] ) ) {
			$newOptions['excluded_terms'] = $this->sanitizeExcludedIds( $newOptions['excluded_terms'] );
		}

		return $newOptions;
	}

	/**
	 * Callback for the widgets options form.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $instance The widget options.
	 * @return void
	 */
	public function form( $instance ) {
		// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		$instance        = wp_parse_args( (array) $instance, $this->defaults );
		$postTypeObjects = $this->getPublicPostTypes();
		$postTypes       = $this->getPublicPostTypes( true );
		$taxonomyObjects = aioseo()->helpers->getPublicTaxonomies();
		// phpcs:enable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable

		include AIOSEO_DIR . '/app/Common/Views/sitemap/html/widget-options.php';
	}

	/**
	 * Returns the public post types (without attachments).
	 *
	 * @since 4.1.3
	 *
	 * @param  boolean $namesOnly Whether only the names should be returned.
	 * @return array              The public post types.
	 */
	private function getPublicPostTypes( $namesOnly = false ) {
		$postTypes = aioseo()->helpers->getPublicPostTypes( $namesOnly );
		foreach ( $postTypes as $k => $postType ) {
			if ( is_array( $postType ) && 'attachment' === $postType['name'] ) {
				unset( $postTypes[ $k ] );
				break;
			}
			if ( ! is_array( $postType ) && 'attachment' === $postType ) {
				unset( $postTypes[ $k ] );
				break;
			}
		}

		return array_values( $postTypes );
	}

	/**
	 * Sanitizes the excluded IDs by removing any non-integer values.
	 *
	 * @since 4.1.3
	 *
	 * @param  string $ids The IDs as a string, comma-separated.
	 * @return string      The sanitized IDs as a string, comma-separated.
	 */
	private function sanitizeExcludedIds( $ids ) {
		$ids = array_map( 'trim', explode( ',', $ids ) );
		$ids = array_filter( $ids, 'is_numeric' );
		$ids = esc_sql( implode( ', ', $ids ) );

		return $ids;
	}
}