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/helpers.tar

indexables-page-helper.php000066600000011221151117651630011571 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Helpers\Options_Helper;

/**
 * A helper object for the indexable page.
 */
class Indexables_Page_Helper {

	/**
	 * The default size of the indexable lists.
	 *
	 * @var int
	 */
	const LIST_SIZE = 5;

	/**
	 * The default size of the buffer, in terms of how many times is bigger than the list size.
	 *
	 * @var int
	 */
	const BUFFER_SIZE = 20;

	/**
	 * The default minimum threshold for the amount of posts in the site.
	 *
	 * @var int
	 */
	const POSTS_THRESHOLD = 20;

	/**
	 * The default minimum threshold for the amount of analysed posts in the site, as a fraction of the total posts.
	 *
	 * @var float
	 */
	const ANALYSED_POSTS_THRESHOLD = 0.5;

	/**
	 * Indexables_Page_Helper constructor.
	 *
	 * @param Options_Helper $options The options helper.
	 */
	public function __construct( Options_Helper $options ) {
		$this->options = $options;
	}

	/**
	 * Retrieves the size of the Indexables lists. This size is the amount of indexables that are displayed in each list.
	 *
	 * @return int The size of the Indexables lists.
	 */
	public function get_indexables_list_size() {
		/**
		 * Filter 'wpseo_indexables_list_size' - Allow filtering the size of the Indexables lists.
		 *
		 * @api int The size of the Indexables lists.
		 */
		return \apply_filters( 'wpseo_indexables_list_size', self::LIST_SIZE );
	}

	/**
	 * Retrieves the size of the buffer for the Indexables lists, in terms of how many times bigger it is from the lists' size. This size is the amount of indexables that are fetched upon page load.
	 *
	 * @return int The size of the Indexables lists.
	 */
	public function get_buffer_size() {
		/**
		 * Filter 'wpseo_indexables_buffer_size' - Allow filtering the size of the buffer for the Indexables lists, in terms of how many times bigger it is from the lists' size.
		 *
		 * @api int The size of the buffer for the Indexables lists, in terms of how many times bigger it is from the lists' size.
		 */
		$times = \apply_filters( 'wpseo_indexables_buffer_size', self::BUFFER_SIZE );
		if ( $times < 3 ) {
			$times = 3;
		}

		return ( $this->get_indexables_list_size() * intval( $times ) );
	}

	/**
	 * Retrieves the minimum threshold for the amount of posts in the site, in order for lists to be relevant.
	 *
	 * @return int The size of the Indexables lists.
	 */
	public function get_minimum_posts_threshold() {
		/**
		 * Filter 'wpseo_posts_threshold' - Allow filtering the minimum threshold for the amount of posts in the site, in order for Indexable lists to be relevant.
		 *
		 * @api int The minimum threshold for the amount of posts in the site, in order for Indexable lists to be relevant.
		 */
		return \apply_filters( 'wpseo_posts_threshold', self::POSTS_THRESHOLD );
	}

	/**
	 * Retrieves the minimum threshold for the amount of analyzed posts in the site, in order for lists to be relevant.
	 *
	 * @return int The size of the Indexables lists.
	 */
	public function get_minimum_analyzed_posts_threshold() {
		/**
		 * Filter 'wpseo_analyzed_posts_threshold' - Allow filtering the minimum threshold for the amount of analyzed posts in the site, in order for Indexable lists to be relevant.
		 *
		 * @api int The minimum threshold for the amount of analyzed posts in the site, in order for Indexable lists to be relevant.
		 */
		return \apply_filters( 'wpseo_analyzed_posts_threshold', self::ANALYSED_POSTS_THRESHOLD );
	}

	/**
	 * Checks if link suggestions are enabled or not
	 *
	 * @return bool Wether enable_link_suggestions is set to true or not.
	 */
	public function get_link_suggestions_enabled() {
		return $this->options->get( 'enable_link_suggestions', false ) === true;
	}

	/**
	 * Returns the list names that are valid.
	 *
	 * @return array An array with valid list names.
	 */
	public function get_list_names() {
		$valid_list_names = [
			'least_readability',
			'least_seo_score',
			'most_linked',
			'least_linked',
		];

		return $valid_list_names;
	}

	/**
	 * Returns the ignore list names that are valid.
	 *
	 * @return array An array with valid ignore list names.
	 */
	public function get_ignore_list_names() {
		$valid_list_names        = $this->get_list_names();
		$valid_ignore_list_names = [];
		foreach ( $valid_list_names as $valid_list_name ) {
			$valid_ignore_list_names[] = $valid_list_name . '_ignore_list';
		}

		return $valid_ignore_list_names;
	}

	/**
	 * Checks if the ignore list name is a valid list name
	 *
	 * @param string $list_name The list name.
	 *
	 * @return bool Wether the list name is valid or not.
	 */
	public function is_valid_ignore_list_name( $list_name ) {
		$valid_list_names = $this->get_ignore_list_names();

		return in_array( $list_name, $valid_list_names, true );
	}
}
user-helper.php000066600000007263151117651630007532 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for the user.
 */
class User_Helper {

	/**
	 * Retrieves user meta field for a user.
	 *
	 * @param int    $user_id User ID.
	 * @param string $key     Optional. The meta key to retrieve. By default, returns data for all keys.
	 * @param bool   $single  Whether to return a single value.
	 *
	 * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true.
	 */
	public function get_meta( $user_id, $key = '', $single = false ) {
		return \get_user_meta( $user_id, $key, $single );
	}

	/**
	 * Counts the number of posts the user has written in this post type.
	 *
	 * @param int          $user_id   User ID.
	 * @param array|string $post_type Optional. Single post type or array of post types to count the number of posts
	 *                                for. Default 'post'.
	 *
	 * @return int The number of posts the user has written in this post type.
	 */
	public function count_posts( $user_id, $post_type = 'post' ) {
		return (int) \count_user_posts( $user_id, $post_type, true );
	}

	/**
	 * Retrieves the requested data of the author.
	 *
	 * @param string    $field   The user field to retrieve.
	 * @param int|false $user_id User ID.
	 *
	 * @return string The author's field from the current author's DB object.
	 */
	public function get_the_author_meta( $field, $user_id ) {
		return \get_the_author_meta( $field, $user_id );
	}

	/**
	 * Retrieves the archive url of the user.
	 *
	 * @param int|false $user_id User ID.
	 *
	 * @return string The author's archive url.
	 */
	public function get_the_author_posts_url( $user_id ) {
		return \get_author_posts_url( $user_id );
	}

	/**
	 * Retrieves the current user ID.
	 *
	 * @return int The current user's ID, or 0 if no user is logged in.
	 */
	public function get_current_user_id() {
		return \get_current_user_id();
	}

	/**
	 * Updates user meta field for a user.
	 *
	 * Use the $prev_value parameter to differentiate between meta fields with the
	 * same key and user ID.
	 *
	 * If the meta field for the user does not exist, it will be added.
	 *
	 * @param int    $user_id    User ID.
	 * @param string $meta_key   Metadata key.
	 * @param mixed  $meta_value Metadata value. Must be serializable if non-scalar.
	 * @param mixed  $prev_value Optional. Previous value to check before updating.
	 *                           If specified, only update existing metadata entries with
	 *                           this value. Otherwise, update all entries. Default empty.
	 *
	 * @return int|bool Meta ID if the key didn't exist, true on successful update,
	 *                  false on failure or if the value passed to the function
	 *                  is the same as the one that is already in the database.
	 */
	public function update_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
		return \update_user_meta( $user_id, $meta_key, $meta_value, $prev_value );
	}

	/**
	 * Removes metadata matching criteria from a user.
	 *
	 * You can match based on the key, or key and value. Removing based on key and
	 * value, will keep from removing duplicate metadata with the same key. It also
	 * allows removing all metadata matching key, if needed.
	 *
	 * @param int    $user_id    User ID.
	 * @param string $meta_key   Metadata name.
	 * @param mixed  $meta_value Optional. Metadata value. If provided,
	 *                           rows will only be removed that match the value.
	 *                           Must be serializable if non-scalar. Default empty.
	 *
	 * @return bool True on success, false on failure.
	 */
	public function delete_meta( $user_id, $meta_key, $meta_value = '' ) {
		return \delete_user_meta( $user_id, $meta_key, $meta_value );
	}
}
indexable-to-postmeta-helper.php000066600000015407151117651630012760 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Models\Indexable;

/**
 * A helper object to map indexable data to postmeta.
 */
class Indexable_To_Postmeta_Helper {

	/**
	 * The Meta helper.
	 *
	 * @var Meta_Helper
	 */
	public $meta;

	/**
	 * The map of yoast to post meta.
	 *
	 * @var array
	 */
	protected $yoast_to_postmeta = [
		'title'                  => [
			'post_meta_key' => 'title',
			'map_method'    => 'simple_map',
		],
		'description'            => [
			'post_meta_key' => 'metadesc',
			'map_method'    => 'simple_map',
		],
		'open_graph_title'       => [
			'post_meta_key' => 'opengraph-title',
			'map_method'    => 'simple_map',
		],
		'open_graph_description' => [
			'post_meta_key' => 'opengraph-description',
			'map_method'    => 'simple_map',
		],
		'twitter_title'          => [
			'post_meta_key' => 'twitter-title',
			'map_method'    => 'simple_map',
		],
		'twitter_description'    => [
			'post_meta_key' => 'twitter-description',
			'map_method'    => 'simple_map',
		],
		'canonical'              => [
			'post_meta_key' => 'canonical',
			'map_method'    => 'simple_map',
		],
		'primary_focus_keyword'  => [
			'post_meta_key' => 'focuskw',
			'map_method'    => 'simple_map',
		],
		'open_graph_image'       => [
			'post_meta_key' => 'opengraph-image',
			'map_method'    => 'social_image_map',
		],
		'open_graph_image_id'    => [
			'post_meta_key' => 'opengraph-image-id',
			'map_method'    => 'social_image_map',
		],
		'twitter_image'          => [
			'post_meta_key' => 'twitter-image',
			'map_method'    => 'social_image_map',
		],
		'twitter_image_id'       => [
			'post_meta_key' => 'twitter-image-id',
			'map_method'    => 'social_image_map',
		],
		'is_robots_noindex'      => [
			'post_meta_key' => 'meta-robots-noindex',
			'map_method'    => 'noindex_map',
		],
		'is_robots_nofollow'     => [
			'post_meta_key' => 'meta-robots-nofollow',
			'map_method'    => 'nofollow_map',
		],
		'meta_robots_adv'        => [
			'post_meta_key' => 'meta-robots-adv',
			'map_method'    => 'robots_adv_map',
		],
	];

	/**
	 * Indexable_To_Postmeta_Helper constructor.
	 *
	 * @param Meta_Helper $meta The Meta helper.
	 */
	public function __construct( Meta_Helper $meta ) {
		$this->meta = $meta;
	}

	/**
	 * Creates postmeta from a Yoast indexable.
	 *
	 * @param Indexable $indexable The Yoast indexable.
	 *
	 * @return void
	 */
	public function map_to_postmeta( $indexable ) {
		foreach ( $this->yoast_to_postmeta as $indexable_column => $map_info ) {
			\call_user_func( [ $this, $map_info['map_method'] ], $indexable, $map_info['post_meta_key'], $indexable_column );
		}
	}

	/**
	 * Uses a simple set_value for non-empty data.
	 *
	 * @param Indexable $indexable        The Yoast indexable.
	 * @param string    $post_meta_key    The post_meta key that will be populated.
	 * @param string    $indexable_column The indexable data that will be mapped to post_meta.
	 *
	 * @return void
	 */
	public function simple_map( $indexable, $post_meta_key, $indexable_column ) {
		if ( empty( $indexable->{$indexable_column} ) ) {
			return;
		}

		$this->meta->set_value( $post_meta_key, $indexable->{$indexable_column}, $indexable->object_id );
	}

	/**
	 * Map social image data only if social image is explicitly set.
	 *
	 * @param Indexable $indexable        The Yoast indexable.
	 * @param string    $post_meta_key    The post_meta key that will be populated.
	 * @param string    $indexable_column The indexable data that will be mapped to post_meta.
	 *
	 * @return void
	 */
	public function social_image_map( $indexable, $post_meta_key, $indexable_column ) {
		if ( empty( $indexable->{$indexable_column} ) ) {
			return;
		}

		switch ( $indexable_column ) {
			case 'open_graph_image':
			case 'open_graph_image_id':
				$source = $indexable->open_graph_image_source;
				break;
			case 'twitter_image':
			case 'twitter_image_id':
				$source = $indexable->twitter_image_source;
				break;
		}

		// Map the social image data only when the social image is explicitly set.
		if ( $source === 'set-by-user' || $source === 'imported' ) {
			$value = (string) $indexable->{$indexable_column};

			$this->meta->set_value( $post_meta_key, $value, $indexable->object_id );
		}
	}

	/**
	 * Deletes the noindex post_meta key if no noindex in the indexable. Populates the post_meta key appropriately if there is noindex in the indexable.
	 *
	 * @param Indexable $indexable     The Yoast indexable.
	 * @param string    $post_meta_key The post_meta key that will be populated.
	 *
	 * @return void
	 */
	public function noindex_map( $indexable, $post_meta_key ) {
		if ( \is_null( $indexable->is_robots_noindex ) ) {
			$this->meta->delete( $post_meta_key, $indexable->object_id );
			return;
		}

		if ( $indexable->is_robots_noindex === false ) {
			$this->meta->set_value( $post_meta_key, 2, $indexable->object_id );
		}

		if ( $indexable->is_robots_noindex === true ) {
			$this->meta->set_value( $post_meta_key, 1, $indexable->object_id );
		}
	}

	/**
	 * Deletes the nofollow post_meta key if no nofollow in the indexable or if nofollow is false. Populates the post_meta key appropriately if there is a true nofollow in the indexable.
	 *
	 * @param Indexable $indexable     The Yoast indexable.
	 * @param string    $post_meta_key The post_meta key that will be populated.
	 *
	 * @return void
	 */
	public function nofollow_map( $indexable, $post_meta_key ) {
		if ( \is_null( $indexable->is_robots_nofollow ) || $indexable->is_robots_nofollow === false ) {
			$this->meta->delete( $post_meta_key, $indexable->object_id );
		}

		if ( $indexable->is_robots_nofollow === true ) {
			$this->meta->set_value( $post_meta_key, 1, $indexable->object_id );
		}
	}

	/**
	 * Deletes the nofollow post_meta key if no nofollow in the indexable or if nofollow is false. Populates the post_meta key appropriately if there is a true nofollow in the indexable.
	 *
	 * @param Indexable $indexable     The Yoast indexable.
	 * @param string    $post_meta_key The post_meta key that will be populated.
	 *
	 * @return void
	 */
	public function robots_adv_map( $indexable, $post_meta_key ) {
		$adv_settings_to_be_imported = [];
		$no_adv_settings             = true;

		if ( $indexable->is_robots_noimageindex === true ) {
			$adv_settings_to_be_imported[] = 'noimageindex';
			$no_adv_settings               = false;
		}
		if ( $indexable->is_robots_noarchive === true ) {
			$adv_settings_to_be_imported[] = 'noarchive';
			$no_adv_settings               = false;
		}
		if ( $indexable->is_robots_nosnippet === true ) {
			$adv_settings_to_be_imported[] = 'nosnippet';
			$no_adv_settings               = false;
		}

		if ( $no_adv_settings === true ) {
			$this->meta->delete( $post_meta_key, $indexable->object_id );
			return;
		}

		$this->meta->set_value( $post_meta_key, \implode( ',', $adv_settings_to_be_imported ), $indexable->object_id );
	}
}
notification-helper.php000066600000001042151117651630011227 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast_Notification;
use Yoast_Notification_Center;

/**
 * A helper object for notifications.
 */
class Notification_Helper {

	/**
	 * Restores a notification (wrapper function).
	 *
	 * @codeCoverageIgnore
	 *
	 * @param Yoast_Notification $notification The notification to restore.
	 *
	 * @return bool True if restored, false otherwise.
	 */
	public function restore_notification( Yoast_Notification $notification ) {
		return Yoast_Notification_Center::restore_notification( $notification );
	}
}
primary-term-helper.php000066600000002567151117651630011206 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use stdClass;

/**
 * A helper object for primary terms.
 */
class Primary_Term_Helper {

	/**
	 * Generate the primary term taxonomies.
	 *
	 * @param int $post_id ID of the post.
	 *
	 * @return array The taxonomies.
	 */
	public function get_primary_term_taxonomies( $post_id ) {
		$post_type      = \get_post_type( $post_id );
		$all_taxonomies = \get_object_taxonomies( $post_type, 'objects' );
		$all_taxonomies = \array_filter( $all_taxonomies, [ $this, 'filter_hierarchical_taxonomies' ] );

		/**
		 * Filters which taxonomies for which the user can choose the primary term.
		 *
		 * @api array    $taxonomies An array of taxonomy objects that are primary_term enabled.
		 *
		 * @param string $post_type      The post type for which to filter the taxonomies.
		 * @param array  $all_taxonomies All taxonomies for this post types, even ones that don't have primary term
		 *                               enabled.
		 */
		$taxonomies = (array) \apply_filters( 'wpseo_primary_term_taxonomies', $all_taxonomies, $post_type, $all_taxonomies );

		return $taxonomies;
	}

	/**
	 * Returns whether or not a taxonomy is hierarchical.
	 *
	 * @param stdClass $taxonomy Taxonomy object.
	 *
	 * @return bool True for hierarchical taxonomy.
	 */
	protected function filter_hierarchical_taxonomies( $taxonomy ) {
		return (bool) $taxonomy->hierarchical;
	}
}
post-type-helper.php000066600000010364151117651630010514 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WP_Post_Type;

/**
 * A helper object for post types.
 */
class Post_Type_Helper {

	/**
	 * The options helper.
	 *
	 * @var Options_Helper
	 */
	protected $options_helper;

	/**
	 * Post_Type_Helper constructor.
	 *
	 * @param Options_Helper $options_helper The options helper.
	 */
	public function __construct( Options_Helper $options_helper ) {
		$this->options_helper = $options_helper;
	}

	/**
	 * Checks if the request post type is public and indexable.
	 *
	 * @codeCoverageIgnore We have to write test when this method contains own code.
	 *
	 * @param string $post_type_name The name of the post type to lookup.
	 *
	 * @return bool True when post type is set to index.
	 */
	public function is_indexable( $post_type_name ) {
		if ( $this->options_helper->get( 'disable-' . $post_type_name, false ) ) {
			return false;
		}

		return ( $this->options_helper->get( 'noindex-' . $post_type_name, false ) === false );
	}

	/**
	 * Checks if the request post type has the Yoast Metabox enabled.
	 *
	 * @param string $post_type_name The name of the post type to lookup.
	 *
	 * @return bool True if metabox is enabled.
	 */
	public function has_metabox( $post_type_name ) {
		return ( $this->options_helper->get( 'display-metabox-pt-' . $post_type_name, true ) === true );
	}

	/**
	 * Returns an array with the public post types.
	 *
	 * @codeCoverageIgnore It only wraps a WordPress function.
	 *
	 * @param string $output The output type to use.
	 *
	 * @return array Array with all the public post_types.
	 */
	public function get_public_post_types( $output = 'names' ) {
		return \get_post_types( [ 'public' => true ], $output );
	}

	/**
	 * Returns an array with the accessible post types.
	 *
	 * An accessible post type is a post type that is public and isn't set as no-index (robots).
	 *
	 * @return array Array with all the accessible post_types.
	 */
	public function get_accessible_post_types() {
		$post_types = \get_post_types( [ 'public' => true ] );
		$post_types = \array_filter( $post_types, 'is_post_type_viewable' );

		/**
		 * Filter: 'wpseo_accessible_post_types' - Allow changing the accessible post types.
		 *
		 * @api array $post_types The public post types.
		 */
		$post_types = \apply_filters( 'wpseo_accessible_post_types', $post_types );

		// When the array gets messed up somewhere.
		if ( ! \is_array( $post_types ) ) {
			return [];
		}

		return $post_types;
	}

	/**
	 * Returns an array of post types that are excluded from being indexed for the
	 * indexables.
	 *
	 * @return array The excluded post types.
	 */
	public function get_excluded_post_types_for_indexables() {
		/**
		 * Filter: 'wpseo_indexable_excluded_post_types' - Allow developers to prevent posts of a certain post
		 * type from being saved to the indexable table.
		 *
		 * @param array $excluded_post_types The currently excluded post types.
		 */
		$excluded_post_types = \apply_filters( 'wpseo_indexable_excluded_post_types', [] );

		// Failsafe, to always make sure that `excluded_post_types` is an array.
		if ( ! \is_array( $excluded_post_types ) ) {
			return [];
		}

		return $excluded_post_types;
	}

	/**
	 * Checks if the post type is excluded.
	 *
	 * @param string $post_type The post type to check.
	 *
	 * @return bool If the post type is exclude.
	 */
	public function is_excluded( $post_type ) {
		return \in_array( $post_type, $this->get_excluded_post_types_for_indexables(), true );
	}

	/**
	 * Checks if the post type with the given name has an archive page.
	 *
	 * @param WP_Post_Type|string $post_type The name of the post type to check.
	 *
	 * @return bool True when the post type has an archive page.
	 */
	public function has_archive( $post_type ) {
		if ( \is_string( $post_type ) ) {
			$post_type = \get_post_type_object( $post_type );
		}

		return ( ! empty( $post_type->has_archive ) );
	}

	/**
	 * Returns the post types that should be indexed.
	 *
	 * @return array The post types that should be indexed.
	 */
	public function get_indexable_post_types() {
		$public_post_types   = $this->get_public_post_types();
		$excluded_post_types = $this->get_excluded_post_types_for_indexables();

		// `array_values`, to make sure that the keys are reset.
		return \array_values( \array_diff( $public_post_types, $excluded_post_types ) );
	}
}
indexing-helper.php000066600000026552151117651630010363 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Actions\Indexing\Indexable_General_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Type_Archive_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexable_Term_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexation_Action_Interface;
use Yoast\WP\SEO\Actions\Indexing\Limited_Indexing_Action_Interface;
use Yoast\WP\SEO\Actions\Indexing\Post_Link_Indexing_Action;
use Yoast\WP\SEO\Actions\Indexing\Term_Link_Indexing_Action;
use Yoast\WP\SEO\Config\Indexing_Reasons;
use Yoast\WP\SEO\Integrations\Admin\Indexing_Notification_Integration;
use Yoast_Notification_Center;
use Yoast\WP\SEO\Repositories\Indexable_Repository;

/**
 * A helper object for indexing.
 */
class Indexing_Helper {

	/**
	 * The options helper.
	 *
	 * @var Options_Helper
	 */
	protected $options_helper;

	/**
	 * The date helper.
	 *
	 * @var Date_Helper
	 */
	protected $date_helper;

	/**
	 * The notification center.
	 *
	 * @var Yoast_Notification_Center
	 */
	protected $notification_center;

	/**
	 * The indexation actions.
	 *
	 * @var Indexation_Action_Interface[]|Limited_Indexing_Action_Interface[]
	 */
	protected $indexing_actions;

	/**
	 * The indexable repository.
	 *
	 * @var Indexable_Repository
	 */
	protected $indexable_repository;

	/**
	 * Indexing_Helper constructor.
	 *
	 * @param Options_Helper            $options_helper      The options helper.
	 * @param Date_Helper               $date_helper         The date helper.
	 * @param Yoast_Notification_Center $notification_center The notification center.
	 */
	public function __construct(
		Options_Helper $options_helper,
		Date_Helper $date_helper,
		Yoast_Notification_Center $notification_center
	) {
		$this->options_helper      = $options_helper;
		$this->date_helper         = $date_helper;
		$this->notification_center = $notification_center;
	}

	/**
	 * Sets the actions.
	 *
	 * @required
	 *
	 * @param Indexable_Post_Indexation_Action              $post_indexation              The post indexing action.
	 * @param Indexable_Term_Indexation_Action              $term_indexation              The term indexing action.
	 * @param Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation The posttype indexing action.
	 * @param Indexable_General_Indexation_Action           $general_indexation           The general indexing (homepage etc) action.
	 * @param Post_Link_Indexing_Action                     $post_link_indexing_action    The post crosslink indexing action.
	 * @param Term_Link_Indexing_Action                     $term_link_indexing_action    The term crossling indexing action.
	 */
	public function set_indexing_actions(
		Indexable_Post_Indexation_Action $post_indexation,
		Indexable_Term_Indexation_Action $term_indexation,
		Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation,
		Indexable_General_Indexation_Action $general_indexation,
		Post_Link_Indexing_Action $post_link_indexing_action,
		Term_Link_Indexing_Action $term_link_indexing_action
	) {
		$this->indexing_actions = [
			$post_indexation,
			$term_indexation,
			$post_type_archive_indexation,
			$general_indexation,
			$post_link_indexing_action,
			$term_link_indexing_action,
		];
	}

	/**
	 * Sets the indexable repository for the indexing helper class.
	 *
	 * @required
	 *
	 * @param Indexable_Repository $indexable_repository The indexable repository.
	 */
	public function set_indexable_repository(
		Indexable_Repository $indexable_repository
	) {
		$this->indexable_repository = $indexable_repository;
	}

	/**
	 * Sets several database options when the indexing process is started.
	 *
	 * @deprecated 17.4 This method was renamed to prepare for internal consistency.
	 * @codeCoverageIgnore
	 *
	 * @return void
	 */
	public function start() {
		$this->prepare();
	}

	/**
	 * Prepares the indexing process by setting several database options and removing the indexing notification.
	 *
	 * @return void
	 */
	public function prepare() {
		$this->set_first_time( false );
		$this->set_started( $this->date_helper->current_time() );
		$this->remove_indexing_notification();
		// Do not set_reason here; if the process is cancelled, the reason to start indexing is still valid.
	}

	/**
	 * Sets several database options when the indexing process is finished.
	 *
	 * @deprecated 17.4 This method was renamed to complete for internal consistency.
	 * @codeCoverageIgnore
	 *
	 * @return void
	 */
	public function finish() {
		$this->complete();
	}

	/**
	 * Sets several database options when the indexing process is finished.
	 *
	 * @return void
	 */
	public function complete() {
		$this->set_reason( '' );
		$this->set_started( null );
	}

	/**
	 * Sets appropriate flags when the indexing process fails.
	 *
	 * @return void
	 */
	public function indexing_failed() {
		$this->set_reason( Indexing_Reasons::REASON_INDEXING_FAILED );
		$this->set_started( null );
	}

	/**
	 * Sets the indexing reason.
	 *
	 * @param string $reason The indexing reason.
	 *
	 * @return void
	 */
	public function set_reason( $reason ) {
		$this->options_helper->set( 'indexing_reason', $reason );
		$this->remove_indexing_notification();
	}

	/**
	 * Removes any pre-existing notification, so that a new notification (with a possible new reason) can be added.
	 */
	protected function remove_indexing_notification() {
		$this->notification_center->remove_notification_by_id(
			Indexing_Notification_Integration::NOTIFICATION_ID
		);
	}

	/**
	 * Determines whether an indexing reason has been set in the options.
	 *
	 * @return bool Whether an indexing reason has been set in the options.
	 */
	public function has_reason() {
		$reason = $this->get_reason();

		return ! empty( $reason );
	}

	/**
	 * Returns the indexing reason. The reason why the site-wide indexing process should be run.
	 *
	 * @return string The indexing reason, defaults to the empty string if no reason has been set.
	 */
	public function get_reason() {
		return $this->options_helper->get( 'indexing_reason', '' );
	}

	/**
	 * Sets the start time when the indexing process has started but not completed.
	 *
	 * @param int|bool $timestamp The start time when the indexing process has started but not completed, false otherwise.
	 *
	 * @return void
	 */
	public function set_started( $timestamp ) {
		$this->options_helper->set( 'indexing_started', $timestamp );
	}

	/**
	 * Gets the start time when the indexing process has started but not completed.
	 *
	 * @return int|bool The start time when the indexing process has started but not completed, false otherwise.
	 */
	public function get_started() {
		return $this->options_helper->get( 'indexing_started' );
	}

	/**
	 * Sets a boolean that indicates whether or not a site still has to be indexed for the first time.
	 *
	 * @param bool $is_first_time_indexing Whether or not a site still has to be indexed for the first time.
	 *
	 * @return void
	 */
	public function set_first_time( $is_first_time_indexing ) {
		$this->options_helper->set( 'indexing_first_time', $is_first_time_indexing );
	}

	/**
	 * Gets a boolean that indicates whether or not the site still has to be indexed for the first time.
	 *
	 * @return bool Whether the site still has to be indexed for the first time.
	 */
	public function is_initial_indexing() {
		return $this->options_helper->get( 'indexing_first_time', true );
	}

	/**
	 * Gets a boolean that indicates whether or not the indexing of the indexables has completed.
	 *
	 * @return bool Whether the indexing of the indexables has completed.
	 */
	public function is_finished_indexables_indexing() {
		return $this->options_helper->get( 'indexables_indexing_completed', false );
	}

	/**
	 * Returns the total number of unindexed objects.
	 *
	 * @return int The total number of unindexed objects.
	 */
	public function get_unindexed_count() {
		$unindexed_count = 0;

		foreach ( $this->indexing_actions as $indexing_action ) {
			$unindexed_count += $indexing_action->get_total_unindexed();
		}

		return $unindexed_count;
	}

	/**
	 * Returns the amount of un-indexed posts expressed in percentage, which will be needed to set a threshold.
	 *
	 * @param int $unindexed_count The number of unindexed objects.
	 *
	 * @return int The amount of unindexed posts expressed in percentage.
	 */
	public function get_unindexed_percentage( $unindexed_count ) {
		// Gets the amount of indexed objects in the site.
		$indexed_count = $this->indexable_repository->get_total_number_of_indexables();
		// The total amount of objects in the site.
		$total_objects_count = ( $indexed_count + $unindexed_count );

		return ( ( $unindexed_count / $total_objects_count ) * 100 );
	}

	/**
	 * Returns whether the SEO optimization button should show.
	 *
	 * @return bool Whether the SEO optimization button should show.
	 */
	public function should_show_optimization_button() {
		// Gets the amount of unindexed objects in the site.
		$unindexed_count = $this->get_filtered_unindexed_count();

		// If the amount of unidexed posts is <10 don't show configuration button.
		if ( $unindexed_count <= 10 ) {
			return false;
		}
		// If the amount of unidexed posts is >10, but the total amount of unidexed posts is ≤4% of the total amount of objects in the site, don't show configuration button.
		if ( $this->get_unindexed_percentage( $unindexed_count ) <= 4 ) {
			return false;
		}
		return true;
	}

	/**
	 * Returns the total number of unindexed objects and applies a filter for third party integrations.
	 *
	 * @return int The total number of unindexed objects.
	 */
	public function get_filtered_unindexed_count() {
		$unindexed_count = $this->get_unindexed_count();

		/**
		 * Filter: 'wpseo_indexing_get_unindexed_count' - Allow changing the amount of unindexed objects.
		 *
		 * @param int $unindexed_count The amount of unindexed objects.
		 */
		return \apply_filters( 'wpseo_indexing_get_unindexed_count', $unindexed_count );
	}

	/**
	 * Returns a limited number of unindexed objects.
	 *
	 * @param int $limit Limit the number of unindexed objects that are counted.
	 *
	 * @return int The total number of unindexed objects.
	 */
	public function get_limited_unindexed_count( $limit ) {
		$unindexed_count = 0;

		foreach ( $this->indexing_actions as $indexing_action ) {
			$unindexed_count += $indexing_action->get_limited_unindexed_count( $limit - $unindexed_count + 1 );
			if ( $unindexed_count > $limit ) {
				return $unindexed_count;
			}
		}

		return $unindexed_count;
	}

	/**
	 * Returns the total number of unindexed objects and applies a filter for third party integrations.
	 *
	 * @param int $limit Limit the number of unindexed objects that are counted.
	 *
	 * @return int The total number of unindexed objects.
	 */
	public function get_limited_filtered_unindexed_count( $limit ) {
		$unindexed_count = $this->get_limited_unindexed_count( $limit );

		if ( $unindexed_count > $limit ) {
			return $unindexed_count;
		}

		/**
		 * Filter: 'wpseo_indexing_get_limited_unindexed_count' - Allow changing the amount of unindexed objects,
		 * and allow for a maximum number of items counted to improve performance.
		 *
		 * @param int       $unindexed_count The amount of unindexed objects.
		 * @param int|false $limit           Limit the number of unindexed objects that need to be counted.
		 *                                   False if it doesn't need to be limited.
		 */
		return \apply_filters( 'wpseo_indexing_get_limited_unindexed_count', $unindexed_count, $limit );
	}
}
image-helper.php000066600000024512151117651630007632 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WPSEO_Image_Utils;
use Yoast\WP\SEO\Models\SEO_Links;
use Yoast\WP\SEO\Repositories\Indexable_Repository;

/**
 * A helper object for images.
 */
class Image_Helper {

	/**
	 * Image types that are supported by Open Graph.
	 *
	 * @var array
	 */
	protected static $valid_image_types = [ 'image/jpeg', 'image/gif', 'image/png', 'image/webp' ];

	/**
	 * Image extensions that are supported by Open Graph.
	 *
	 * @var array
	 */
	protected static $valid_image_extensions = [ 'jpeg', 'jpg', 'gif', 'png', 'webp' ];

	/**
	 * Represents the indexables repository.
	 *
	 * @var Indexable_Repository
	 */
	protected $indexable_repository;

	/**
	 * The options helper.
	 *
	 * @var Options_Helper
	 */
	private $options_helper;

	/**
	 * The URL helper.
	 *
	 * @var Url_Helper
	 */
	private $url_helper;

	/**
	 * Image_Helper constructor.
	 *
	 * @param Indexable_Repository $indexable_repository The indexable repository.
	 * @param Options_Helper       $options              The options helper.
	 * @param Url_Helper           $url_helper           The URL helper.
	 */
	public function __construct(
		Indexable_Repository $indexable_repository,
		Options_Helper $options,
		Url_Helper $url_helper
	) {
		$this->indexable_repository = $indexable_repository;
		$this->options_helper       = $options;
		$this->url_helper           = $url_helper;
	}

	/**
	 * Determines whether or not the wanted attachment is considered valid.
	 *
	 * @param int $attachment_id The attachment ID to get the attachment by.
	 *
	 * @return bool Whether or not the attachment is valid.
	 */
	public function is_valid_attachment( $attachment_id ) {
		if ( ! \wp_attachment_is_image( $attachment_id ) ) {
			return false;
		}

		$post_mime_type = \get_post_mime_type( $attachment_id );
		if ( $post_mime_type === false ) {
			return false;
		}

		return $this->is_valid_image_type( $post_mime_type );
	}

	/**
	 * Checks if the given extension is a valid extension
	 *
	 * @param string $image_extension The image extension.
	 *
	 * @return bool True when valid.
	 */
	public function is_extension_valid( $image_extension ) {
		return \in_array( $image_extension, static::$valid_image_extensions, true );
	}

	/**
	 * Determines whether the passed mime type is a valid image type.
	 *
	 * @param string $mime_type The detected mime type.
	 *
	 * @return bool Whether or not the attachment is a valid image type.
	 */
	public function is_valid_image_type( $mime_type ) {
		return \in_array( $mime_type, static::$valid_image_types, true );
	}

	/**
	 * Retrieves the image source for an attachment.
	 *
	 * @param int    $attachment_id The attachment.
	 * @param string $image_size    The image size to retrieve.
	 *
	 * @return string The image url or an empty string when not found.
	 */
	public function get_attachment_image_source( $attachment_id, $image_size = 'full' ) {
		$attachment = \wp_get_attachment_image_src( $attachment_id, $image_size );

		if ( ! $attachment ) {
			return '';
		}

		return $attachment[0];
	}

	/**
	 * Retrieves the ID of the featured image.
	 *
	 * @param int $post_id The post id to get featured image id for.
	 *
	 * @return int|bool ID when found, false when not.
	 */
	public function get_featured_image_id( $post_id ) {
		if ( ! \has_post_thumbnail( $post_id ) ) {
			return false;
		}

		return \get_post_thumbnail_id( $post_id );
	}

	/**
	 * Gets the image url from the content.
	 *
	 * @param int $post_id The post id to extract the images from.
	 *
	 * @return string The image url or an empty string when not found.
	 */
	public function get_post_content_image( $post_id ) {
		$image_url = $this->get_first_usable_content_image_for_post( $post_id );

		if ( $image_url === null ) {
			return '';
		}

		return $image_url;
	}

	/**
	 * Gets the first image url of a gallery.
	 *
	 * @param int $post_id Post ID to use.
	 *
	 * @return string The image url or an empty string when not found.
	 */
	public function get_gallery_image( $post_id ) {
		$post = \get_post( $post_id );
		if ( \strpos( $post->post_content, '[gallery' ) === false ) {
			return '';
		}

		$images = \get_post_gallery_images( $post );
		if ( empty( $images ) ) {
			return '';
		}

		return \reset( $images );
	}

	/**
	 * Gets the image url from the term content.
	 *
	 * @param int $term_id The term id to extract the images from.
	 *
	 * @return string The image url or an empty string when not found.
	 */
	public function get_term_content_image( $term_id ) {
		$image_url = $this->get_first_content_image_for_term( $term_id );

		if ( $image_url === null ) {
			return '';
		}

		return $image_url;
	}

	/**
	 * Retrieves the caption for an attachment.
	 *
	 * @param int $attachment_id Attachment ID.
	 *
	 * @return string The caption when found, empty string when no caption is found.
	 */
	public function get_caption( $attachment_id ) {
		$caption = \wp_get_attachment_caption( $attachment_id );
		if ( ! empty( $caption ) ) {
			return $caption;
		}

		$caption = \get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
		if ( ! empty( $caption ) ) {
			return $caption;
		}

		return '';
	}

	/**
	 * Retrieves the attachment metadata.
	 *
	 * @param int $attachment_id Attachment ID.
	 *
	 * @return array The metadata, empty array when no metadata is found.
	 */
	public function get_metadata( $attachment_id ) {
		$metadata = \wp_get_attachment_metadata( $attachment_id );
		if ( ! $metadata || ! \is_array( $metadata ) ) {
			return [];
		}

		return $metadata;
	}

	/**
	 * Retrieves the attachment image url.
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $size          The size to get.
	 *
	 * @return string The url when found, empty string otherwise.
	 */
	public function get_attachment_image_url( $attachment_id, $size ) {
		$url = \wp_get_attachment_image_url( $attachment_id, $size );
		if ( ! $url ) {
			return '';
		}

		return $url;
	}

	/**
	 * Find the right version of an image based on size.
	 *
	 * @codeCoverageIgnore - We have to write test when this method contains own code.
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $size          Size name.
	 *
	 * @return array|false Returns an array with image data on success, false on failure.
	 */
	public function get_image( $attachment_id, $size ) {
		return WPSEO_Image_Utils::get_image( $attachment_id, $size );
	}

	/**
	 * Retrieves the best attachment variation for the given attachment.
	 *
	 * @codeCoverageIgnore - We have to write test when this method contains own code.
	 *
	 * @param int $attachment_id The attachment id.
	 *
	 * @return bool|string The attachment url or false when no variations found.
	 */
	public function get_best_attachment_variation( $attachment_id ) {
		$variations = WPSEO_Image_Utils::get_variations( $attachment_id );
		$variations = WPSEO_Image_Utils::filter_usable_file_size( $variations );

		// If we are left without variations, there is no valid variation for this attachment.
		if ( empty( $variations ) ) {
			return false;
		}

		// The variations are ordered so the first variations is by definition the best one.
		return \reset( $variations );
	}

	/**
	 * Find an attachment ID for a given URL.
	 *
	 * @param string $url The URL to find the attachment for.
	 *
	 * @return int The found attachment ID, or 0 if none was found.
	 */
	public function get_attachment_by_url( $url ) {
		// Strip out the size part of an image URL.
		$url = \preg_replace( '/(.*)-\d+x\d+\.(jpeg|jpg|png|gif)$/', '$1.$2', $url );

		// Don't try to do this for external URLs.
		if ( $this->url_helper->get_link_type( $url ) === SEO_Links::TYPE_EXTERNAL ) {
			return 0;
		}

		$indexable = $this->indexable_repository->find_by_permalink( $url );

		if ( $indexable && $indexable->object_type === 'post' && $indexable->object_sub_type === 'attachment' ) {
			return $indexable->object_id;
		}

		$post_id = WPSEO_Image_Utils::get_attachment_by_url( $url );

		if ( $post_id !== 0 ) {
			// Find the indexable, this triggers creating it so it can be found next time.
			$this->indexable_repository->find_by_id_and_type( $post_id, 'post' );
		}

		return $post_id;
	}

	/**
	 * Retrieves an attachment ID for an image uploaded in the settings.
	 *
	 * Due to self::get_attachment_by_url returning 0 instead of false.
	 * 0 is also a possibility when no ID is available.
	 *
	 * @codeCoverageIgnore - We have to write test when this method contains own code.
	 *
	 * @param string $setting The setting the image is stored in.
	 *
	 * @return int|bool The attachment id, or false or 0 if no ID is available.
	 */
	public function get_attachment_id_from_settings( $setting ) {
		return WPSEO_Image_Utils::get_attachment_id_from_settings( $setting );
	}

	/**
	 * Based on and image ID return array with the best variation of that image. If it's not saved to the DB,  save it to an option.
	 *
	 * @param string $setting The setting name. Should be company or person.
	 *
	 * @return array|bool Array with image details when the image is found, boolean when it's not found.
	 */
	public function get_attachment_meta_from_settings( $setting ) {
		$image_meta = $this->options_helper->get( $setting . '_meta', false );
		if ( ! $image_meta ) {
			$image_id = $this->options_helper->get( $setting . '_id', false );
			if ( $image_id ) {
				// There is not an option to put a URL in an image field in the settings anymore, only to upload it through the media manager.
				// This means an attachment always exists, so doing this is only needed once.
				$image_meta = $this->get_best_attachment_variation( $image_id );
				if ( $image_meta ) {
					$this->options_helper->set( $setting . '_meta', $image_meta );
				}
			}
		}

		return $image_meta;
	}

	/**
	 * Retrieves the first usable content image for a post.
	 *
	 * @codeCoverageIgnore - We have to write test when this method contains own code.
	 *
	 * @param int $post_id The post id to extract the images from.
	 *
	 * @return string|null
	 */
	protected function get_first_usable_content_image_for_post( $post_id ) {
		return WPSEO_Image_Utils::get_first_usable_content_image_for_post( $post_id );
	}

	/**
	 * Gets the term's first usable content image. Null if none is available.
	 *
	 * @codeCoverageIgnore - We have to write test when this method contains own code.
	 *
	 * @param int $term_id The term id.
	 *
	 * @return string|null The image URL.
	 */
	protected function get_first_content_image_for_term( $term_id ) {
		return WPSEO_Image_Utils::get_first_content_image_for_term( $term_id );
	}
}
taxonomy-helper.php000066600000010731151117651630010424 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WP_Taxonomy;
use WP_Term;
use WPSEO_Taxonomy_Meta;

/**
 * A helper object for terms.
 */
class Taxonomy_Helper {

	/**
	 * The options helper.
	 *
	 * @var Options_Helper
	 */
	private $options;

	/**
	 * The string helper.
	 *
	 * @var String_Helper
	 */
	private $string;

	/**
	 * Taxonomy_Helper constructor.
	 *
	 * @codeCoverageIgnore It only sets dependencies.
	 *
	 * @param Options_Helper $options       The options helper.
	 * @param String_Helper  $string_helper The string helper.
	 */
	public function __construct( Options_Helper $options, String_Helper $string_helper ) {
		$this->options = $options;
		$this->string  = $string_helper;
	}

	/**
	 * Checks if the requested term is indexable.
	 *
	 * @param string $taxonomy The taxonomy slug.
	 *
	 * @return bool True when taxonomy is set to index.
	 */
	public function is_indexable( $taxonomy ) {
		return ! $this->options->get( 'noindex-tax-' . $taxonomy, false );
	}

	/**
	 * Returns an array with the public taxonomies.
	 *
	 * @param string $output The output type to use.
	 *
	 * @return string[]|WP_Taxonomy[] Array with all the public taxonomies.
	 *                                The type depends on the specified output variable.
	 */
	public function get_public_taxonomies( $output = 'names' ) {
		return \get_taxonomies( [ 'public' => true ], $output );
	}

	/**
	 * Retrieves the term description (without tags).
	 *
	 * @param int $term_id Term ID.
	 *
	 * @return string Term description (without tags).
	 */
	public function get_term_description( $term_id ) {
		return $this->string->strip_all_tags( \term_description( $term_id ) );
	}

	/**
	 * Retrieves the taxonomy term's meta values.
	 *
	 * @codeCoverageIgnore We have to write test when this method contains own code.
	 *
	 * @param WP_Term $term Term to get the meta value for.
	 *
	 * @return array|bool Array of all the meta data for the term.
	 *                    False if the term does not exist or the $meta provided is invalid.
	 */
	public function get_term_meta( $term ) {
		return WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, null );
	}

	/**
	 * Gets the passed taxonomy's slug.
	 *
	 * @param string $taxonomy The name of the taxonomy.
	 *
	 * @return string The slug for the taxonomy. Returns the taxonomy's name if no slug could be found.
	 */
	public function get_taxonomy_slug( $taxonomy ) {
		$taxonomy_object = \get_taxonomy( $taxonomy );

		if ( $taxonomy_object && \property_exists( $taxonomy_object, 'rewrite' ) && \is_array( $taxonomy_object->rewrite ) && isset( $taxonomy_object->rewrite['slug'] ) ) {
			return $taxonomy_object->rewrite['slug'];
		}

		return \strtolower( $taxonomy_object->name );
	}

	/**
	 * Returns an array with the custom taxonomies.
	 *
	 * @param string $output The output type to use.
	 *
	 * @return string[]|WP_Taxonomy[] Array with all the custom taxonomies.
	 *                                The type depends on the specified output variable.
	 */
	public function get_custom_taxonomies( $output = 'names' ) {
		return \get_taxonomies( [ '_builtin' => false ], $output );
	}

	/**
	 * Returns an array of taxonomies that are excluded from being indexed for the
	 * indexables.
	 *
	 * @return array The excluded taxonomies.
	 */
	public function get_excluded_taxonomies_for_indexables() {
		/**
		 * Filter: 'wpseo_indexable_excluded_taxonomies' - Allow developers to prevent a certain taxonomy
		 * from being saved to the indexable table.
		 *
		 * @param array $excluded_taxonomies The currently excluded taxonomies.
		 */
		$excluded_taxonomies = \apply_filters( 'wpseo_indexable_excluded_taxonomies', [] );

		// Failsafe, to always make sure that `excluded_taxonomies` is an array.
		if ( ! \is_array( $excluded_taxonomies ) ) {
			return [];
		}

		return $excluded_taxonomies;
	}

	/**
	 * Checks if the taxonomy is excluded.
	 *
	 * @param string $taxonomy The taxonomy to check.
	 *
	 * @return bool If the taxonomy is excluded.
	 */
	public function is_excluded( $taxonomy ) {
		return \in_array( $taxonomy, $this->get_excluded_taxonomies_for_indexables(), true );
	}

	/**
	 * This builds a list of indexable taxonomies.
	 *
	 * @return array The indexable taxonomies.
	 */
	public function get_indexable_taxonomies() {
		$public_taxonomies   = $this->get_public_taxonomies();
		$excluded_taxonomies = $this->get_excluded_taxonomies_for_indexables();

		// `array_values`, to make sure that the keys are reset.
		return \array_values( \array_diff( $public_taxonomies, $excluded_taxonomies ) );
	}
}
product-helper.php000066600000001715151117651630010230 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for the Yoast products.
 */
class Product_Helper {

	/**
	 * Gets the product name.
	 *
	 * @return string
	 */
	public function get_product_name() {
		if ( $this->is_premium() ) {
			return 'Yoast SEO Premium';
		}

		return 'Yoast SEO';
	}

	/**
	 * Gets the product name in the head section.
	 *
	 * @return string
	 */
	public function get_name() {
		return $this->get_product_name() . ' plugin';
	}

	/**
	 * Checks if the installed version is Yoast SEO Premium.
	 *
	 * @return bool True when is premium.
	 */
	public function is_premium() {
		return \defined( 'WPSEO_PREMIUM_FILE' );
	}

	/**
	 * Gets the Premium version if defined, returns null otherwise.
	 *
	 * @return string|null The Premium version or null when premium version is not defined.
	 */
	public function get_premium_version() {
		if ( \defined( 'WPSEO_PREMIUM_VERSION' ) ) {
			return \WPSEO_PREMIUM_VERSION;
		}

		return null;
	}
}
import-helper.php000066600000001314151117651630010055 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * The Import Helper.
 */
class Import_Helper {

	/**
	 * Flattens a multidimensional array of settings. Recursive.
	 *
	 * @param array  $array_to_flatten The array to be flattened.
	 * @param string $key_prefix       The key to be used as a prefix.
	 *
	 * @return array The flattened array.
	 */
	public function flatten_settings( $array_to_flatten, $key_prefix = '' ) {
		$result = [];
		foreach ( $array_to_flatten as $key => $value ) {
			if ( \is_array( $value ) ) {
				$result = \array_merge( $result, $this->flatten_settings( $value, $key_prefix . '/' . $key ) );
			}
			else {
				$result[ $key_prefix . '/' . $key ] = $value;
			}
		}

		return $result;
	}
}
wpdb-helper.php000066600000001656151117651630007510 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use wpdb;

/**
 * A helper object for the wpdb.
 */
class Wpdb_Helper {

	/**
	 * The WordPress database instance.
	 *
	 * @var wpdb
	 */
	private $wpdb;

	/**
	 * Constructs a Wpdb_Helper instance.
	 *
	 * @param wpdb $wpdb The WordPress database instance.
	 */
	public function __construct( wpdb $wpdb ) {
		$this->wpdb = $wpdb;
	}

	/**
	 * Check if table exists.
	 *
	 * @param string $table The table to be checked.
	 *
	 * @return bool Whether the table exists.
	 */
	public function table_exists( $table ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
		$table_exists = $this->wpdb->get_var( "SHOW TABLES LIKE '{$table}'" );
		if ( \is_wp_error( $table_exists ) || \is_null( $table_exists ) ) {
			return false;
		}

		return true;
	}
}
meta-helper.php000066600000005646151117651630007505 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WPSEO_Meta;
use WPSEO_Taxonomy_Meta;

/**
 * A helper object for meta.
 */
class Meta_Helper {

	/**
	 * Get a custom post meta value.
	 *
	 * Returns the default value if the meta value has not been set.
	 *
	 * {@internal Unfortunately there isn't a filter available to hook into before returning
	 *            the results for get_post_meta(), get_post_custom() and the likes. That
	 *            would have been the preferred solution.}}
	 *
	 * @codeCoverageIgnore We have to write test when this method contains own code.
	 *
	 * @param string $key    Internal key of the value to get (without prefix).
	 * @param int    $postid Post ID of the post to get the value for.
	 *
	 * @return string All 'normal' values returned from get_post_meta() are strings.
	 *                Objects and arrays are possible, but not used by this plugin
	 *                and therefore discarted (except when the special 'serialized' field def
	 *                value is set to true - only used by add-on plugins for now).
	 *                Will return the default value if no value was found.
	 *                Will return empty string if no default was found (not one of our keys) or
	 *                if the post does not exist.
	 */
	public function get_value( $key, $postid = 0 ) {
		return WPSEO_Meta::get_value( $key, $postid );
	}

	/**
	 * Retrieve a taxonomy term's meta value(s).
	 *
	 * @param mixed       $term     Term to get the meta value for
	 *                              either (string) term name, (int) term id or (object) term.
	 * @param string      $taxonomy Name of the taxonomy to which the term is attached.
	 * @param string|null $meta     Optional. Meta value to get (without prefix).
	 *
	 * @return mixed|bool Value for the $meta if one is given, might be the default.
	 *                    If no meta is given, an array of all the meta data for the term.
	 *                    False if the term does not exist or the $meta provided is invalid.
	 */
	public function get_term_value( $term, $taxonomy, $meta = null ) {
		return WPSEO_Taxonomy_Meta::get_term_meta( $term, $taxonomy, $meta );
	}

	/**
	 * Set a custom post meta value.
	 *
	 * @param string $key        Internal key of the value to set (without prefix).
	 * @param mixed  $meta_value The value to set the meta value to.
	 * @param int    $post_id    Post ID of the post to set the value for.
	 *
	 * @return bool Whether the value was changed.
	 */
	public function set_value( $key, $meta_value, $post_id ) {
		return WPSEO_Meta::set_value( $key, $meta_value, $post_id );
	}

	/**
	 * Deletes a meta value for a post.
	 *
	 * @param string $key     The internal key of the meta value to change (without prefix).
	 * @param int    $post_id The ID of the post to delete the meta for.
	 *
	 * @return bool Whether the delete was successful or not.
	 */
	public function delete( $key, $post_id ) {
		return WPSEO_Meta::delete( $key, $post_id );
	}
}
wordproof-helper.php000066600000006007151117651630010570 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use ReflectionClass;
use Yoast\WP\SEO\Conditionals\Non_Multisite_Conditional;
use Yoast\WP\SEO\Conditionals\Third_Party\Wordproof_Plugin_Inactive_Conditional;
use Yoast\WP\SEO\Conditionals\User_Can_Publish_Posts_And_Pages_Conditional;

/**
 * A helper object for WordProof integration.
 */
class Wordproof_Helper {

	/**
	 * Holds the Current Page helper instance.
	 *
	 * @var Current_Page_Helper
	 */
	protected $current_page;

	/**
	 * Holds the WooCommerce helper instance.
	 *
	 * @var Woocommerce_Helper
	 */
	protected $woocommerce;

	/**
	 * Holds the Options Page helper instance.
	 *
	 * @var Options_Helper
	 */
	protected $options;

	/**
	 * WordProof_Helper constructor.
	 *
	 * @param Current_Page_Helper $current_page The current page helper.
	 * @param Woocommerce_Helper  $woocommerce  The woocommerce helper.
	 * @param Options_Helper      $options      The options helper.
	 */
	public function __construct( Current_Page_Helper $current_page, Woocommerce_Helper $woocommerce, Options_Helper $options ) {
		$this->current_page = $current_page;
		$this->woocommerce  = $woocommerce;
		$this->options      = $options;
	}

	/**
	 * Remove site options after disabling the integration.
	 *
	 * @return bool Returns if the options are deleted
	 */
	public function remove_site_options() {
		return \delete_site_option( 'wordproof_access_token' )
			&& \delete_site_option( 'wordproof_source_id' );
	}

	/**
	 * Returns if conditionals are met. If not, the integration should be disabled.
	 *
	 * @param bool $return_conditional If the conditional class name that was unmet should be returned.
	 *
	 * @return bool|string Returns if the integration should be disabled.
	 */
	public function integration_is_disabled( $return_conditional = false ) {
		$conditionals = [ new Non_Multisite_Conditional(), new Wordproof_Plugin_Inactive_Conditional() ];

		foreach ( $conditionals as $conditional ) {
			if ( ! $conditional->is_met() ) {

				if ( $return_conditional === true ) {
					return ( new ReflectionClass( $conditional ) )->getShortName();
				}

				return true;
			}
		}

		return false;
	}

	/**
	 * Returns if the WordProof integration toggle is turned on.
	 *
	 * @return bool Returns if the integration toggle is set to true if conditionals are met.
	 */
	public function integration_is_active() {
		if ( $this->integration_is_disabled() ) {
			return false;
		}

		return $this->options->get( 'wordproof_integration_active', true );
	}

	/**
	 * Return if WordProof should be active for this post editor page.
	 *
	 * @return bool Returns if WordProof should be active for this page.
	 */
	public function is_active() {
		$is_wordproof_active = $this->integration_is_active();

		if ( ! $is_wordproof_active ) {
			return false;
		}

		$user_can_publish = ( new User_Can_Publish_Posts_And_Pages_Conditional() )->is_met();

		if ( ! $user_can_publish ) {
			return false;
		}

		return ( $this->current_page->current_post_is_privacy_policy() || $this->woocommerce->current_post_is_terms_and_conditions_page() );
	}
}
date-helper.php000066600000006210151117651630007460 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use DateTime;
use DateTimeZone;
use Exception;

/**
 * A helper object for dates.
 */
class Date_Helper {

	/**
	 * Convert given date string to the W3C format.
	 *
	 * If $translate is true then the given date and format string will
	 * be passed to date_i18n() for translation.
	 *
	 * @param string $date      Date string to convert.
	 * @param bool   $translate Whether the return date should be translated. Default false.
	 *
	 * @return string Formatted date string.
	 */
	public function mysql_date_to_w3c_format( $date, $translate = false ) {
		return \mysql2date( \DATE_W3C, $date, $translate );
	}

	/**
	 * Formats a given date in UTC TimeZone format.
	 *
	 * @param string $date   String representing the date / time.
	 * @param string $format The format that the passed date should be in.
	 *
	 * @return string The formatted date.
	 */
	public function format( $date, $format = \DATE_W3C ) {
		if ( ! \is_string( $date ) ) {
			return $date;
		}

		$immutable_date = \date_create_immutable_from_format( 'Y-m-d H:i:s', $date, new DateTimeZone( 'UTC' ) );

		if ( ! $immutable_date ) {
			return $date;
		}

		return $immutable_date->format( $format );
	}

	/**
	 * Formats the given timestamp to the needed format.
	 *
	 * @param int    $timestamp The timestamp to use for the formatting.
	 * @param string $format    The format that the passed date should be in.
	 *
	 * @return string The formatted date.
	 */
	public function format_timestamp( $timestamp, $format = \DATE_W3C ) {
		if ( ! \is_string( $timestamp ) && ! \is_int( $timestamp ) ) {
			return $timestamp;
		}

		$immutable_date = \date_create_immutable_from_format( 'U', $timestamp, new DateTimeZone( 'UTC' ) );

		if ( ! $immutable_date ) {
			return $timestamp;
		}

		return $immutable_date->format( $format );
	}

	/**
	 * Formats a given date in UTC TimeZone format and translate it to the set language.
	 *
	 * @param string $date   String representing the date / time.
	 * @param string $format The format that the passed date should be in.
	 *
	 * @return string The formatted and translated date.
	 */
	public function format_translated( $date, $format = \DATE_W3C ) {
		return \date_i18n( $format, $this->format( $date, 'U' ) );
	}

	/**
	 * Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).
	 *
	 * @return int The current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).
	 */
	public function current_time() {
		return \time();
	}

	/**
	 * Check if a string is a valid datetime.
	 *
	 * @param string $datetime String input to check as valid input for DateTime class.
	 *
	 * @return bool True when datetime is valid.
	 */
	public function is_valid_datetime( $datetime ) {
		if ( $datetime === null ) {
			/*
			 * While not "officially" supported, `null` will be handled as `"now"` until PHP 9.0.
			 * @link https://3v4l.org/tYp2k
			 */
			return true;
		}

		if ( \is_string( $datetime ) && \substr( $datetime, 0, 1 ) === '-' ) {
			return false;
		}

		try {
			return new DateTime( $datetime ) !== false;
		} catch ( Exception $exception ) {
			return false;
		}
	}
}
open-graph/image-helper.php000066600000005541151117651630011673 0ustar00<?php

namespace Yoast\WP\SEO\Helpers\Open_Graph;

use Yoast\WP\SEO\Helpers\Image_Helper as Base_Image_Helper;
use Yoast\WP\SEO\Helpers\Url_Helper;

/**
 * A helper object for Open Graph images.
 */
class Image_Helper {

	/**
	 * The URL helper.
	 *
	 * @var Url_Helper
	 */
	private $url;

	/**
	 * The base image helper.
	 *
	 * @var Base_Image_Helper
	 */
	private $image;

	/**
	 * Image_Helper constructor.
	 *
	 * @codeCoverageIgnore
	 *
	 * @param Url_Helper        $url   The url helper.
	 * @param Base_Image_Helper $image The image helper.
	 */
	public function __construct( Url_Helper $url, Base_Image_Helper $image ) {
		$this->url   = $url;
		$this->image = $image;
	}

	/**
	 * Determines whether the passed URL is considered valid.
	 *
	 * @param array $image The image array.
	 *
	 * @return bool Whether or not the URL is a valid image.
	 */
	public function is_image_url_valid( array $image ) {
		if ( empty( $image['url'] ) || ! \is_string( $image['url'] ) ) {
			return false;
		}

		$image_extension = $this->url->get_extension_from_url( $image['url'] );
		$is_valid        = $this->image->is_extension_valid( $image_extension );

		/**
		 * Filter: 'wpseo_opengraph_is_valid_image_url' - Allows extra validation for an image url.
		 *
		 * @api bool - Current validation result.
		 *
		 * @param string $url The image url to validate.
		 */
		return (bool) \apply_filters( 'wpseo_opengraph_is_valid_image_url', $is_valid, $image['url'] );
	}

	/**
	 * Retrieves the overridden image size value.
	 *
	 * @return string|null The image size when overriden by filter or null when not.
	 */
	public function get_override_image_size() {
		/**
		 * Filter: 'wpseo_opengraph_image_size' - Allow overriding the image size used
		 * for Open Graph sharing. If this filter is used, the defined size will always be
		 * used for the og:image. The image will still be rejected if it is too small.
		 *
		 * Only use this filter if you manually want to determine the best image size
		 * for the `og:image` tag.
		 *
		 * Use the `wpseo_image_sizes` filter if you want to use our logic. That filter
		 * can be used to add an image size that needs to be taken into consideration
		 * within our own logic.
		 *
		 * @api string|false $size Size string.
		 */
		return \apply_filters( 'wpseo_opengraph_image_size', null );
	}

	/**
	 * Retrieves the image data by a given attachment id.
	 *
	 * @param int $attachment_id The attachment id.
	 *
	 * @return array|false The image data when found, `false` when not.
	 */
	public function get_image_by_id( $attachment_id ) {
		if ( ! $this->image->is_valid_attachment( $attachment_id ) ) {
			return false;
		}

		$override_image_size = $this->get_override_image_size();
		if ( $override_image_size ) {
			return $this->image->get_image( $attachment_id, $override_image_size );
		}

		return $this->image->get_best_attachment_variation( $attachment_id );
	}
}
open-graph/values-helper.php000066600000005134151117651630012106 0ustar00<?php

namespace Yoast\WP\SEO\Helpers\Open_Graph;

/**
 * A helper object for the filtering of values.
 */
class Values_Helper {

	/**
	 * Filters the Open Graph title.
	 *
	 * @param string $title          The default title.
	 * @param string $object_type    The object type.
	 * @param string $object_subtype The object subtype.
	 *
	 * @return string The open graph title.
	 */
	public function get_open_graph_title( $title, $object_type, $object_subtype ) {
		/**
		 * Allow changing the Open Graph title.
		 *
		 * @param string $title          The default title.
		 * @param string $object_subtype The object subtype.
		 */
		return \apply_filters( 'Yoast\WP\SEO\open_graph_title_' . $object_type, $title, $object_subtype );
	}

	/**
	 * Filters the Open Graph description.
	 *
	 * @param string $description    The default description.
	 * @param string $object_type    The object type.
	 * @param string $object_subtype The object subtype.
	 *
	 * @return string The open graph description.
	 */
	public function get_open_graph_description( $description, $object_type, $object_subtype ) {
		/**
		 * Allow changing the Open Graph description.
		 *
		 * @param string $description    The default description.
		 * @param string $object_subtype The object subtype.
		 */
		return \apply_filters( 'Yoast\WP\SEO\open_graph_description_' . $object_type, $description, $object_subtype );
	}

	/**
	 * Filters the Open Graph image ID.
	 *
	 * @param int    $image_id       The default image ID.
	 * @param string $object_type    The object type.
	 * @param string $object_subtype The object subtype.
	 *
	 * @return string The open graph image ID.
	 */
	public function get_open_graph_image_id( $image_id, $object_type, $object_subtype ) {
		/**
		 * Allow changing the Open Graph image ID.
		 *
		 * @param int    $image_id       The default image ID.
		 * @param string $object_subtype The object subtype.
		 */
		return \apply_filters( 'Yoast\WP\SEO\open_graph_image_id_' . $object_type, $image_id, $object_subtype );
	}

	/**
	 * Filters the Open Graph image URL.
	 *
	 * @param string $image          The default image URL.
	 * @param string $object_type    The object type.
	 * @param string $object_subtype The object subtype.
	 *
	 * @return string The open graph image URL.
	 */
	public function get_open_graph_image( $image, $object_type, $object_subtype ) {
		/**
		 * Allow changing the Open Graph image URL.
		 *
		 * @param string $image          The default image URL.
		 * @param string $object_subtype The object subtype.
		 */
		return \apply_filters( 'Yoast\WP\SEO\open_graph_image_' . $object_type, $image, $object_subtype );
	}
}
permalink-helper.php000066600000002406151117651630010530 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Models\Indexable;

/**
 * A helper object for permalinks.
 */
class Permalink_Helper {

	/**
	 * Retrieves the permalink for an indexable.
	 *
	 * @param Indexable $indexable The indexable.
	 *
	 * @return string|null The permalink.
	 */
	public function get_permalink_for_indexable( $indexable ) {
		switch ( true ) {
			case $indexable->object_type === 'post':
				if ( $indexable->object_sub_type === 'attachment' ) {
					return \wp_get_attachment_url( $indexable->object_id );
				}
				return \get_permalink( $indexable->object_id );
			case $indexable->object_type === 'home-page':
				return \home_url( '/' );
			case $indexable->object_type === 'term':
				$term = \get_term( $indexable->object_id );

				if ( $term === null || \is_wp_error( $term ) ) {
					return null;
				}

				return \get_term_link( $term, $term->taxonomy );
			case $indexable->object_type === 'system-page' && $indexable->object_sub_type === 'search-page':
				return \get_search_link();
			case $indexable->object_type === 'post-type-archive':
				return \get_post_type_archive_link( $indexable->object_sub_type );
			case $indexable->object_type === 'user':
				return \get_author_posts_url( $indexable->object_id );
		}

		return null;
	}
}
wincher-helper.php000066600000004566151117651630010216 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Conditionals\Non_Multisite_Conditional;
use Yoast\WP\SEO\Config\Wincher_Client;
use Yoast\WP\SEO\Exceptions\OAuth\Authentication_Failed_Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Property_Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Token_Exception;

/**
 * A helper object for Wincher matters.
 */
class Wincher_Helper {

	/**
	 * Holds the Options Page helper instance.
	 *
	 * @var Options_Helper
	 */
	protected $options;

	/**
	 * Options_Helper constructor.
	 *
	 * @param Options_Helper $options The options helper.
	 */
	public function __construct( Options_Helper $options ) {
		$this->options = $options;
	}

	/**
	 * Checks if the integration should be active for the current user.
	 *
	 * @return bool Whether the integration is active.
	 */
	public function is_active() {
		$conditional = new Non_Multisite_Conditional();

		if ( ! $conditional->is_met() ) {
			return false;
		}

		if ( ! \current_user_can( 'publish_posts' ) && ! \current_user_can( 'publish_pages' ) ) {
			return false;
		}

		return (bool) $this->options->get( 'wincher_integration_active', true );
	}

	/**
	 * Checks if the user is logged in to Wincher.
	 *
	 * @return bool The Wincher login status.
	 */
	public function login_status() {
		try {
			$wincher = \YoastSEO()->classes->get( Wincher_Client::class );
		} catch ( Empty_Property_Exception $e ) {
			// Return false if token is malformed (empty property).
			return false;
		}

		// Get token (and refresh it if it's expired).
		try {
			$wincher->get_tokens();
		} catch ( Authentication_Failed_Exception $e ) {
			return false;
		} catch ( Empty_Token_Exception $e ) {
			return false;
		}

		return $wincher->has_valid_tokens();
	}

	/**
	 * Returns the Wincher links that can be used to localize the global admin
	 * script. Mainly exists to avoid duplicating these links in multiple places
	 * around the code base.
	 *
	 * @return string[]
	 */
	public function get_admin_global_links() {
		return [
			'links.wincher.website' => 'https://www.wincher.com?utm_medium=plugin&utm_source=yoast&referer=yoast&partner=yoast',
			'links.wincher.pricing' => 'https://www.wincher.com/pricing?utm_medium=plugin&utm_source=yoast&referer=yoast&partner=yoast',
			'links.wincher.login'   => 'https://app.wincher.com/login?utm_medium=plugin&utm_source=yoast&referer=yoast&partner=yoast',
		];
	}
}
blocks-helper.php000066600000004415151117651630010025 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WP_Block_Parser_Block;

/**
 * A helper object for blocks.
 */
class Blocks_Helper {

	/**
	 * Holds the Post_Helper instance.
	 *
	 * @var Post_Helper
	 */
	private $post;

	/**
	 * Constructs a Blocks_Helper instance.
	 *
	 * @codeCoverageIgnore It handles dependencies.
	 *
	 * @param Post_Helper $post The post helper.
	 */
	public function __construct( Post_Helper $post ) {
		$this->post = $post;
	}

	/**
	 * Returns all blocks in a given post.
	 *
	 * @param int $post_id The post id.
	 *
	 * @return array The blocks in a block-type => WP_Block_Parser_Block[] format.
	 */
	public function get_all_blocks_from_post( $post_id ) {
		if ( ! $this->has_blocks_support() ) {
			return [];
		}

		$post = $this->post->get_post( $post_id );
		return $this->get_all_blocks_from_content( $post->post_content );
	}

	/**
	 * Returns all blocks in a given content.
	 *
	 * @param string $content The content.
	 *
	 * @return array The blocks in a block-type => WP_Block_Parser_Block[] format.
	 */
	public function get_all_blocks_from_content( $content ) {
		if ( ! $this->has_blocks_support() ) {
			return [];
		}

		$collection = [];
		$blocks     = \parse_blocks( $content );
		return $this->collect_blocks( $blocks, $collection );
	}

	/**
	 * Checks if the installation has blocks support.
	 *
	 * @codeCoverageIgnore It only checks if a WordPress function exists.
	 *
	 * @return bool True when function parse_blocks exists.
	 */
	protected function has_blocks_support() {
		return \function_exists( 'parse_blocks' );
	}

	/**
	 * Collects an array of blocks into an organised collection.
	 *
	 * @param WP_Block_Parser_Block[] $blocks     The blocks.
	 * @param array                   $collection The collection.
	 *
	 * @return array The blocks in a block-type => WP_Block_Parser_Block[] format.
	 */
	private function collect_blocks( $blocks, $collection ) {
		foreach ( $blocks as $block ) {
			if ( ! isset( $collection[ $block['blockName'] ] ) || ! \is_array( $collection[ $block['blockName'] ] ) ) {
				$collection[ $block['blockName'] ] = [];
			}
			$collection[ $block['blockName'] ][] = $block;

			if ( isset( $block['innerBlocks'] ) ) {
				$collection = $this->collect_blocks( $block['innerBlocks'], $collection );
			}
		}

		return $collection;
	}
}
environment-helper.php000066600000001124151117651630011106 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for site environment.
 */
class Environment_Helper {

	/**
	 * Determines if the site is running on production.
	 *
	 * @return bool True if WordPress is currently running on production, false for all other environments.
	 */
	public function is_production_mode() {
		return $this->get_wp_environment() === 'production';
	}

	/**
	 * Determines on which environment WordPress is running.
	 *
	 * @return string The current WordPress environment.
	 */
	public function get_wp_environment() {
		return \wp_get_environment_type();
	}
}
require-file-helper.php000066600000000461151117651630011136 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * Represents a file helper.
 */
class Require_File_Helper {

	/**
	 * Activates the plugin based on the given plugin file.
	 *
	 * @param string $path The path to the required file.
	 */
	public function require_file_once( $path ) {
		require_once $path;
	}
}
robots-helper.php000066600000003366151117651630010064 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Models\Indexable;

/**
 * A helper object for the robots meta tag.
 */
class Robots_Helper {

	/**
	 * Holds the Post_Type_Helper.
	 *
	 * @var Post_Type_Helper
	 */
	protected $post_type_helper;

	/**
	 * Holds the Taxonomy_Helper.
	 *
	 * @var Taxonomy_Helper
	 */
	protected $taxonomy_helper;

	/**
	 * Constructs a Score_Helper.
	 *
	 * @param Post_Type_Helper $post_type_helper The Post_Type_Helper.
	 * @param Taxonomy_Helper  $taxonomy_helper  The Taxonomy_Helper.
	 */
	public function __construct(
		Post_Type_Helper $post_type_helper,
		Taxonomy_Helper $taxonomy_helper
	) {
		$this->post_type_helper = $post_type_helper;
		$this->taxonomy_helper  = $taxonomy_helper;
	}

	/**
	 * Retrieves whether the Indexable is indexable.
	 *
	 * @param Indexable $indexable The Indexable.
	 *
	 * @return bool Whether the Indexable is indexable.
	 */
	public function is_indexable( Indexable $indexable ) {
		if ( $indexable->is_robots_noindex === null ) {
			// No individual value set, check the global setting.
			switch ( $indexable->object_type ) {
				case 'post':
					return $this->post_type_helper->is_indexable( $indexable->object_sub_type );
				case 'term':
					return $this->taxonomy_helper->is_indexable( $indexable->object_sub_type );
			}
		}

		return $indexable->is_robots_noindex === false;
	}

	/**
	 * Sets the robots index to noindex.
	 *
	 * @param array $robots The current robots value.
	 *
	 * @return array The altered robots string.
	 */
	public function set_robots_no_index( $robots ) {
		if ( ! \is_array( $robots ) ) {
			\_deprecated_argument( __METHOD__, '14.1', '$robots has to be a key-value paired array.' );
			return $robots;
		}

		$robots['index'] = 'noindex';

		return $robots;
	}
}
asset-helper.php000066600000005024151117651630007664 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for author archives.
 */
class Asset_Helper {

	/**
	 * Recursively retrieves all dependency urls of a given handle.
	 *
	 * @param string $handle The handle.
	 *
	 * @return string[] All dependency urls of the given handle.
	 */
	public function get_dependency_urls_by_handle( $handle ) {
		$urls = [];

		foreach ( $this->get_dependency_handles( $handle ) as $other_handle ) {
			$urls[ $other_handle ] = $this->get_asset_url( $other_handle );
		}

		return $urls;
	}

	/**
	 * Recursively retrieves all dependencies of a given handle.
	 *
	 * @param string $handle The handle.
	 *
	 * @return string[]|bool All dependencies of the given handle.
	 */
	public function get_dependency_handles( $handle ) {
		$scripts = \wp_scripts();

		if ( ! isset( $scripts->registered[ $handle ] ) ) {
			return false;
		}

		$obj  = $scripts->registered[ $handle ];
		$deps = $obj->deps;
		foreach ( $obj->deps as $other_handle ) {
			$nested_deps = $this->get_dependency_handles( $other_handle );
			if ( ! $nested_deps ) {
				continue;
			}

			// Place nested dependencies before primary dependencies, they need to be loaded first.
			$deps = \array_merge( $nested_deps, $deps );
		}

		// Array unique keeps the first of each element so dependencies will be as early as they're required.
		return \array_values( \array_unique( $deps ) );
	}

	/**
	 * Gets the URL of a given asset.
	 *
	 * This logic is copied from WP_Scripts::do_item as unfortunately that logic is not properly isolated.
	 *
	 * @param string $handle The handle of the asset.
	 *
	 * @return string|false The URL of the asset or false if the asset does not exist.
	 */
	public function get_asset_url( $handle ) {
		$scripts = \wp_scripts();

		if ( ! isset( $scripts->registered[ $handle ] ) ) {
			return false;
		}

		$obj = $scripts->registered[ $handle ];

		if ( $obj->ver === null ) {
			$ver = '';
		}
		else {
			$ver = ( $obj->ver ) ? $obj->ver : $scripts->default_version;
		}
		if ( isset( $scripts->args[ $handle ] ) ) {
			$ver = ( $ver ) ? $ver . '&amp;' . $scripts->args[ $handle ] : $scripts->args[ $handle ];
		}

		$src = $obj->src;

		if ( ! \preg_match( '|^(https?:)?//|', $src ) && ! ( $scripts->content_url && \strpos( $src, $scripts->content_url ) === 0 ) ) {
			$src = $scripts->base_url . $src;
		}

		if ( ! empty( $ver ) ) {
			$src = \add_query_arg( 'ver', $ver, $src );
		}

		/** This filter is documented in wp-includes/class.wp-scripts.php */
		return \esc_url( \apply_filters( 'script_loader_src', $src, $handle ) );
	}
}
short-link-helper.php000066600000006457151117651630010652 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * Helper to get shortlinks for Yoast SEO.
 */
class Short_Link_Helper {

	/**
	 * The options helper.
	 *
	 * @var Options_Helper
	 */
	protected $options_helper;

	/**
	 * The product helper.
	 *
	 * @var Product_Helper
	 */
	protected $product_helper;

	/**
	 * Short_Link_Helper constructor.
	 *
	 * @param Options_Helper $options_helper The options helper.
	 * @param Product_Helper $product_helper The product helper.
	 */
	public function __construct(
		Options_Helper $options_helper,
		Product_Helper $product_helper
	) {
		$this->options_helper = $options_helper;
		$this->product_helper = $product_helper;
	}

	/**
	 * Builds a URL to use in the plugin as shortlink.
	 *
	 * @param string $url The URL to build upon.
	 *
	 * @return string The final URL.
	 */
	public function build( $url ) {
		return \add_query_arg( $this->collect_additional_shortlink_data(), $url );
	}

	/**
	 * Returns a version of the URL with a utm_content with the current version.
	 *
	 * @param string $url The URL to build upon.
	 *
	 * @return string The final URL.
	 */
	public function get( $url ) {
		return $this->build( $url );
	}

	/**
	 * Echoes a version of the URL with a utm_content with the current version.
	 *
	 * @param string $url The URL to build upon.
	 */
	public function show( $url ) {
		echo \esc_url( $this->get( $url ) );
	}

	/**
	 * Gets the shortlink's query params.
	 *
	 * @return array The shortlink's query params.
	 */
	public function get_query_params() {
		return $this->collect_additional_shortlink_data();
	}

	/**
	 * Gets the current site's PHP version, without the extra info.
	 *
	 * @return string The PHP version.
	 */
	private function get_php_version() {
		$version = \explode( '.', \PHP_VERSION );

		return (int) $version[0] . '.' . (int) $version[1];
	}

	/**
	 * Gets the current site's platform version.
	 *
	 * @return string The wp_version.
	 */
	protected function get_platform_version() {
		return $GLOBALS['wp_version'];
	}

	/**
	 * Collects the additional data necessary for the shortlink.
	 *
	 * @return array The shortlink data.
	 */
	protected function collect_additional_shortlink_data() {
		return [
			'php_version'      => $this->get_php_version(),
			'platform'         => 'wordpress',
			'platform_version' => $this->get_platform_version(),
			'software'         => $this->get_software(),
			'software_version' => \WPSEO_VERSION,
			'days_active'      => $this->get_days_active(),
			'user_language'    => \get_user_locale(),
		];
	}

	/**
	 * Get our software and whether it's active or not.
	 *
	 * @return string The software name.
	 */
	protected function get_software() {
		if ( $this->product_helper->is_premium() ) {
			return 'premium';
		}

		return 'free';
	}

	/**
	 * Gets the number of days the plugin has been active.
	 *
	 * @return int The number of days the plugin is active.
	 */
	protected function get_days_active() {
		$date_activated = $this->options_helper->get( 'first_activated_on' );
		$datediff       = ( \time() - $date_activated );
		$days           = (int) \round( $datediff / \DAY_IN_SECONDS );
		switch ( $days ) {
			case 0:
			case 1:
				$cohort = '0-1';
				break;
			case ( $days < 5 ):
				$cohort = '2-5';
				break;
			case ( $days < 30 ):
				$cohort = '6-30';
				break;
			default:
				$cohort = '30plus';
		}
		return $cohort;
	}
}
url-helper.php000066600000016514151117651630007355 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Models\SEO_Links;

/**
 * A helper object for URLs.
 */
class Url_Helper {

	/**
	 * Retrieve home URL with proper trailing slash.
	 *
	 * @param string      $path   Path relative to home URL.
	 * @param string|null $scheme Scheme to apply.
	 *
	 * @return string Home URL with optional path, appropriately slashed if not.
	 */
	public function home( $path = '', $scheme = null ) {
		$home_url = \home_url( $path, $scheme );

		if ( ! empty( $path ) ) {
			return $home_url;
		}

		$home_path = \wp_parse_url( $home_url, \PHP_URL_PATH );

		if ( $home_path === '/' ) { // Home at site root, already slashed.
			return $home_url;
		}

		if ( \is_null( $home_path ) ) { // Home at site root, always slash.
			return \trailingslashit( $home_url );
		}

		if ( \is_string( $home_path ) ) { // Home in subdirectory, slash if permalink structure has slash.
			return \user_trailingslashit( $home_url );
		}

		return $home_url;
	}

	/**
	 * Determines whether the plugin is active for the entire network.
	 *
	 * @return bool Whether or not the plugin is network-active.
	 */
	public function is_plugin_network_active() {
		static $network_active = null;

		if ( ! \is_multisite() ) {
			return false;
		}

		// If a cached result is available, bail early.
		if ( $network_active !== null ) {
			return $network_active;
		}

		$network_active_plugins = \wp_get_active_network_plugins();

		// Consider MU plugins and network-activated plugins as network-active.
		$network_active = \strpos( \wp_normalize_path( \WPSEO_FILE ), \wp_normalize_path( \WPMU_PLUGIN_DIR ) ) === 0
			|| \in_array( \WP_PLUGIN_DIR . '/' . \WPSEO_BASENAME, $network_active_plugins, true );

		return $network_active;
	}

	/**
	 * Retrieve network home URL if plugin is network-activated, or home url otherwise.
	 *
	 * @return string Home URL with optional path, appropriately slashed if not.
	 */
	public function network_safe_home_url() {
		/**
		 * Action: 'wpseo_home_url' - Allows overriding of the home URL.
		 */
		\do_action( 'wpseo_home_url' );

		// If the plugin is network-activated, use the network home URL.
		if ( self::is_plugin_network_active() ) {
			return \network_home_url();
		}

		return \home_url();
	}

	/**
	 * Check whether a url is relative.
	 *
	 * @param string $url URL string to check.
	 *
	 * @return bool True when url is relative.
	 */
	public function is_relative( $url ) {
		return ( \strpos( $url, 'http' ) !== 0 && \strpos( $url, '//' ) !== 0 );
	}

	/**
	 * Gets the path from the passed URL.
	 *
	 * @param string $url The URL to get the path from.
	 *
	 * @return string The path of the URL. Returns an empty string if URL parsing fails.
	 */
	public function get_url_path( $url ) {
		if ( \is_string( $url ) === false
			&& \is_object( $url ) === false
			|| ( \is_object( $url ) === true && \method_exists( $url, '__toString' ) === false )
		) {
			return '';
		}

		return (string) \wp_parse_url( $url, \PHP_URL_PATH );
	}

	/**
	 * Determines the file extension of the given url.
	 *
	 * @param string $url The URL.
	 *
	 * @return string The extension.
	 */
	public function get_extension_from_url( $url ) {
		$path = $this->get_url_path( $url );

		if ( $path === '' ) {
			return '';
		}

		$parts = \explode( '.', $path );
		if ( empty( $parts ) || \count( $parts ) === 1 ) {
			return '';
		}

		return \end( $parts );
	}

	/**
	 * Ensures that the given url is an absolute url.
	 *
	 * @param string $url The url that needs to be absolute.
	 *
	 * @return string The absolute url.
	 */
	public function ensure_absolute_url( $url ) {
		if ( ! \is_string( $url ) || $url === '' ) {
			return $url;
		}

		if ( $this->is_relative( $url ) === true ) {
			return $this->build_absolute_url( $url );
		}

		return $url;
	}

	/**
	 * Parse the home URL setting to find the base URL for relative URLs.
	 *
	 * @param string|null $path Optional path string.
	 *
	 * @return string
	 */
	public function build_absolute_url( $path = null ) {
		$path      = \wp_parse_url( $path, \PHP_URL_PATH );
		$url_parts = \wp_parse_url( \home_url() );

		$base_url = \trailingslashit( $url_parts['scheme'] . '://' . $url_parts['host'] );

		if ( ! \is_null( $path ) ) {
			$base_url .= \ltrim( $path, '/' );
		}

		return $base_url;
	}

	/**
	 * Returns the link type.
	 *
	 * @param array      $url      The URL, as parsed by wp_parse_url.
	 * @param array|null $home_url Optional. The home URL, as parsed by wp_parse_url. Used to avoid reparsing the home_url.
	 * @param bool       $is_image Whether or not the link is an image.
	 *
	 * @return string The link type.
	 */
	public function get_link_type( $url, $home_url = null, $is_image = false ) {
		// If there is no scheme and no host the link is always internal.
		// Beware, checking just the scheme isn't enough as a link can be //yoast.com for instance.
		if ( empty( $url['scheme'] ) && empty( $url['host'] ) ) {
			return ( $is_image ) ? SEO_Links::TYPE_INTERNAL_IMAGE : SEO_Links::TYPE_INTERNAL;
		}

		// If there is a scheme but it's not http(s) then the link is always external.
		if ( \array_key_exists( 'scheme', $url ) && ! \in_array( $url['scheme'], [ 'http', 'https' ], true ) ) {
			return ( $is_image ) ? SEO_Links::TYPE_EXTERNAL_IMAGE : SEO_Links::TYPE_EXTERNAL;
		}

		if ( \is_null( $home_url ) ) {
			$home_url = \wp_parse_url( \home_url() );
		}

		// When the base host is equal to the host.
		if ( isset( $url['host'] ) && $url['host'] !== $home_url['host'] ) {
			return ( $is_image ) ? SEO_Links::TYPE_EXTERNAL_IMAGE : SEO_Links::TYPE_EXTERNAL;
		}

		// There is no base path and thus all URLs of the same domain are internal.
		if ( empty( $home_url['path'] ) ) {
			return ( $is_image ) ? SEO_Links::TYPE_INTERNAL_IMAGE : SEO_Links::TYPE_INTERNAL;
		}

		// When there is a path and it matches the start of the url.
		if ( isset( $url['path'] ) && \strpos( $url['path'], $home_url['path'] ) === 0 ) {
			return ( $is_image ) ? SEO_Links::TYPE_INTERNAL_IMAGE : SEO_Links::TYPE_INTERNAL;
		}

		return ( $is_image ) ? SEO_Links::TYPE_EXTERNAL_IMAGE : SEO_Links::TYPE_EXTERNAL;
	}

	/**
	 * Recreate current URL.
	 *
	 * @param bool $with_request_uri Whether we want the REQUEST_URI appended.
	 *
	 * @return string
	 */
	public function recreate_current_url( $with_request_uri = true ) {
		$current_url = 'http';
		if ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] === 'on' ) {
			$current_url .= 's';
		}
		$current_url .= '://';

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- We know this is scary.
		$suffix = ( $with_request_uri && isset( $_SERVER['REQUEST_URI'] ) ) ? $_SERVER['REQUEST_URI'] : '';

		if ( isset( $_SERVER['SERVER_NAME'] ) && ! empty( $_SERVER['SERVER_NAME'] ) ) {
			// phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- We know this is scary.
			$server_name = $_SERVER['SERVER_NAME'];
		}
		else {
			// Early return with just the path.
			return $suffix;
		}

		$server_port = '';
		if ( isset( $_SERVER['SERVER_PORT'] ) && $_SERVER['SERVER_PORT'] !== '80' && $_SERVER['SERVER_PORT'] !== '443' ) {
			// phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- We know this is scary.
			$server_port = $_SERVER['SERVER_PORT'];
		}

		if ( ! empty( $server_port ) ) {
			$current_url .= $server_name . ':' . $server_port . $suffix;
		}
		else {
			// phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- We know this is scary.
			$current_url .= $server_name . $suffix;
		}

		return $current_url;
	}
}
import-cursor-helper.php000066600000002563151117651630011377 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * The Import Cursor Helper.
 */
class Import_Cursor_Helper {

	/**
	 * The Options_Helper.
	 *
	 * @var Options_Helper
	 */
	public $options;

	/**
	 * Class constructor.
	 *
	 * @param Options_Helper $options The options helper.
	 */
	public function __construct(
		Options_Helper $options
	) {
		$this->options = $options;
	}

	/**
	 * Returns the stored cursor value.
	 *
	 * @param string $cursor_id     The cursor id.
	 * @param mixed  $default_value The default value if no cursor has been set yet.
	 *
	 * @return int The stored cursor value.
	 */
	public function get_cursor( $cursor_id, $default_value = 0 ) {
		$import_cursors = $this->options->get( 'import_cursors', [] );

		return ( isset( $import_cursors[ $cursor_id ] ) ) ? $import_cursors[ $cursor_id ] : $default_value;
	}

	/**
	 * Stores the current cursor value.
	 *
	 * @param string $cursor_id        The cursor id.
	 * @param int    $last_imported_id The id of the lastly imported entry.
	 *
	 * @return void
	 */
	public function set_cursor( $cursor_id, $last_imported_id ) {
		$current_cursors = $this->options->get( 'import_cursors', [] );

		if ( ! isset( $current_cursors[ $cursor_id ] ) || $current_cursors[ $cursor_id ] < $last_imported_id ) {
			$current_cursors[ $cursor_id ] = $last_imported_id;
			$this->options->set( 'import_cursors', $current_cursors );
		}
	}
}
request-helper.php000066600000000540151117651630010233 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for the request state.
 */
class Request_Helper {

	/**
	 * Checks if the current request is a REST request.
	 *
	 * @return bool True when the current request is a REST request.
	 */
	public function is_rest_request() {
		return \defined( 'REST_REQUEST' ) && \REST_REQUEST === true;
	}
}
input-helper.php000066600000001226151117651630007704 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for the filter_input.
 */
class Input_Helper {

	/**
	 * Returns the result of the filter_input. This is mostly a wrapper so we can test.
	 *
	 * @param int    $input_type    The type of input constant (e.g. INPUT_POST, INPUT_GET ).
	 * @param string $search_string The property to get from the input.
	 * @param int    $filter        Optional. The constant that defines the sanitization.
	 *
	 * @return string The result of the get input.
	 */
	public function filter( $input_type, $search_string, $filter = \FILTER_DEFAULT ) {
		return \filter_input( $input_type, $search_string, $filter );
	}
}
wordpress-helper.php000066600000001000151117651630010563 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

// phpcs:disable WordPress.WP.CapitalPDangit.MisspelledClassName -- It is spelled like `Wordpress_Helper` because of Yoast's naming conventions for classes, which would otherwise break dependency injection in some cases.

/**
 * A helper object for WordPress matters.
 */
class Wordpress_Helper {

	/**
	 * Returns the WordPress version.
	 *
	 * @return string The version.
	 */
	public function get_wordpress_version() {
		global $wp_version;

		return $wp_version;
	}
}
sanitization-helper.php000066600000002040151117651630011254 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WPSEO_Utils;

/**
 * A helper object for sanitization.
 */
class Sanitization_Helper {

	/**
	 * Emulate the WP native sanitize_text_field function in a %%variable%% safe way.
	 *
	 * @codeCoverageIgnore We have to write test when this method contains own code.
	 *
	 * @param string $value String value to sanitize.
	 *
	 * @return string The sanitized string.
	 */
	public function sanitize_text_field( $value ) {
		return WPSEO_Utils::sanitize_text_field( $value );
	}

	/**
	 * Sanitize a url for saving to the database.
	 * Not to be confused with the old native WP function.
	 *
	 * @codeCoverageIgnore We have to write test when this method contains own code.
	 *
	 * @param string $value             String URL value to sanitize.
	 * @param array  $allowed_protocols Optional set of allowed protocols.
	 *
	 * @return string The sanitized URL.
	 */
	public function sanitize_url( $value, $allowed_protocols = [ 'http', 'https' ] ) {
		return WPSEO_Utils::sanitize_url( $value, $allowed_protocols );
	}
}
indexable-helper.php000066600000016151151117651630010503 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Type_Archive_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexable_Term_Indexation_Action;
use Yoast\WP\SEO\Config\Indexing_Reasons;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Repositories\Indexable_Repository;

/**
 * A helper object for indexables.
 */
class Indexable_Helper {

	/**
	 * Represents the indexable repository.
	 *
	 * @var Indexable_Repository
	 */
	protected $repository;

	/**
	 * Represents the options helper.
	 *
	 * @var Options_Helper
	 */
	protected $options_helper;

	/**
	 * Represents the environment helper.
	 *
	 * @var Environment_Helper
	 */
	protected $environment_helper;

	/**
	 * Represents the indexing helper.
	 *
	 * @var Indexing_Helper
	 */
	protected $indexing_helper;

	/**
	 * Default values of certain columns.
	 *
	 * @var array
	 */
	protected $default_values = [
		'title'                  => [
			'default_value'   => null,
		],
		'description'            => [
			'default_value'   => null,
		],
		'open_graph_title'       => [
			'default_value'   => null,
		],
		'open_graph_description' => [
			'default_value'   => null,
		],
		'twitter_title'          => [
			'default_value'   => null,
		],
		'twitter_description'    => [
			'default_value'   => null,
		],
		'canonical'              => [
			'default_value'   => null,
		],
		'primary_focus_keyword'  => [
			'default_value'   => null,
		],
		'is_robots_noindex'      => [
			'default_value'   => null,
		],
		'is_robots_nofollow'     => [
			'default_value'   => false,
		],
		'is_robots_noarchive'    => [
			'default_value'   => null,
		],
		'is_robots_noimageindex' => [
			'default_value'   => null,
		],
		'is_robots_nosnippet'    => [
			'default_value'   => null,
		],
	];

	/**
	 * Indexable_Helper constructor.
	 *
	 * @param Options_Helper     $options_helper     The options helper.
	 * @param Environment_Helper $environment_helper The environment helper.
	 * @param Indexing_Helper    $indexing_helper    The indexing helper.
	 */
	public function __construct( Options_Helper $options_helper, Environment_Helper $environment_helper, Indexing_Helper $indexing_helper ) {
		$this->options_helper     = $options_helper;
		$this->environment_helper = $environment_helper;
		$this->indexing_helper    = $indexing_helper;
	}

	/**
	 * Sets the indexable repository. Done to avoid circular dependencies.
	 *
	 * @required
	 *
	 * @param Indexable_Repository $repository The indexable repository.
	 */
	public function set_indexable_repository( Indexable_Repository $repository ) {
		$this->repository = $repository;
	}

	/**
	 * Returns the page type of an indexable.
	 *
	 * @param Indexable $indexable The indexable.
	 *
	 * @return string|false The page type. False if it could not be determined.
	 */
	public function get_page_type_for_indexable( $indexable ) {
		switch ( $indexable->object_type ) {
			case 'post':
				$front_page_id = (int) \get_option( 'page_on_front' );
				if ( $indexable->object_id === $front_page_id ) {
					return 'Static_Home_Page';
				}
				$posts_page_id = (int) \get_option( 'page_for_posts' );
				if ( $indexable->object_id === $posts_page_id ) {
					return 'Static_Posts_Page';
				}

				return 'Post_Type';
			case 'term':
				return 'Term_Archive';
			case 'user':
				return 'Author_Archive';
			case 'home-page':
				return 'Home_Page';
			case 'post-type-archive':
				return 'Post_Type_Archive';
			case 'date-archive':
				return 'Date_Archive';
			case 'system-page':
				if ( $indexable->object_sub_type === 'search-result' ) {
					return 'Search_Result_Page';
				}
				if ( $indexable->object_sub_type === '404' ) {
					return 'Error_Page';
				}
		}

		return false;
	}

	/**
	 * Resets the permalinks of the indexables.
	 *
	 * @param string|null $type    The type of the indexable.
	 * @param string|null $subtype The subtype. Can be null.
	 * @param string      $reason  The reason that the permalink has been changed.
	 */
	public function reset_permalink_indexables( $type = null, $subtype = null, $reason = Indexing_Reasons::REASON_PERMALINK_SETTINGS ) {
		$result = $this->repository->reset_permalink( $type, $subtype );

		$this->indexing_helper->set_reason( $reason );

		if ( $result !== false && $result > 0 ) {
			\delete_transient( Indexable_Post_Indexation_Action::UNINDEXED_COUNT_TRANSIENT );
			\delete_transient( Indexable_Post_Type_Archive_Indexation_Action::UNINDEXED_COUNT_TRANSIENT );
			\delete_transient( Indexable_Term_Indexation_Action::UNINDEXED_COUNT_TRANSIENT );
		}
	}

	/**
	 * Determines whether indexing indexables is appropriate at this time.
	 *
	 * @return bool Whether the indexables should be indexed.
	 */
	public function should_index_indexables() {
		// Currently, the only reason to index is when we're on a production website.
		$should_index = $this->environment_helper->is_production_mode();

		/**
		 * Filter: 'Yoast\WP\SEO\should_index_indexables' - Allow developers to enable / disable
		 * creating indexables. Warning: overriding
		 * the intended action may cause problems when moving from a staging to a
		 * production environment because indexable permalinks may get set incorrectly.
		 *
		 * @since 18.2
		 *
		 * @param bool $should_index Whether the site's indexables should be created.
		 */
		return (bool) \apply_filters( 'Yoast\WP\SEO\should_index_indexables', $should_index );
	}

	/**
	 * Returns whether or not dynamic permalinks should be used.
	 *
	 * @return bool Whether or not the dynamic permalinks should be used.
	 */
	public function dynamic_permalinks_enabled() {
		/**
		 * Filters the value of the `dynamic_permalinks` option.
		 *
		 * @param bool $value The value of the `dynamic_permalinks` option.
		 */
		return (bool) \apply_filters( 'wpseo_dynamic_permalinks_enabled', $this->options_helper->get( 'dynamic_permalinks', false ) );
	}

	/**
	 * Sets a boolean to indicate that the indexing of the indexables has completed.
	 *
	 * @return void
	 */
	public function finish_indexing() {
		$this->options_helper->set( 'indexables_indexing_completed', true );
	}

	/**
	 * Checks whether the indexable has default values in given fields.
	 *
	 * @param Indexable $indexable The Yoast indexable that we're checking.
	 * @param array     $fields    The Yoast indexable fields that we're checking against.
	 *
	 * @return bool Whether the indexable has default values.
	 */
	public function check_if_default_indexable( $indexable, $fields ) {
		foreach ( $fields as $field ) {
			$is_default = $this->check_if_default_field( $indexable, $field );
			if ( ! $is_default ) {
				break;
			}
		}

		return $is_default;
	}

	/**
	 * Checks if an indexable field contains the default value.
	 *
	 * @param Indexable $indexable The Yoast indexable that we're checking.
	 * @param string    $field     The field that we're checking.
	 *
	 * @return bool True if default value.
	 */
	public function check_if_default_field( $indexable, $field ) {
		$defaults = $this->default_values;
		if ( ! isset( $defaults[ $field ] ) ) {
			return false;
		}

		if ( $indexable->$field === $defaults[ $field ]['default_value'] ) {
			return true;
		}

		return false;
	}
}
author-archive-helper.php000066600000012056151117651630011471 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WP_Query;
use Yoast\WP\Lib\Model;

/**
 * A helper object for author archives.
 */
class Author_Archive_Helper {

	/**
	 * The options helper.
	 *
	 * @var Options_Helper
	 */
	private $options_helper;

	/**
	 * The post type helper.
	 *
	 * @var Post_Type_Helper
	 */
	private $post_type_helper;

	/**
	 * Creates a new author archive helper.
	 *
	 * @param Options_Helper   $options_helper   The options helper.
	 * @param Post_Type_Helper $post_type_helper The post type helper.
	 */
	public function __construct(
		Options_Helper $options_helper,
		Post_Type_Helper $post_type_helper
	) {
		$this->options_helper   = $options_helper;
		$this->post_type_helper = $post_type_helper;
	}

	/**
	 * Gets the array of post types that are shown on an author's archive.
	 *
	 * @return array The post types that are shown on an author's archive.
	 */
	public function get_author_archive_post_types() {
		/**
		 * Filters the array of post types that are shown on an author's archive.
		 *
		 * @param array $args The post types that are shown on an author archive.
		 */
		return \apply_filters( 'wpseo_author_archive_post_types', [ 'post' ] );
	}

	/**
	 * Returns whether the author has at least one public post.
	 *
	 * @param int $author_id The author ID.
	 *
	 * @return bool|null Whether the author has at least one public post.
	 */
	public function author_has_public_posts( $author_id ) {
		// First check if the author has at least one public post.
		$has_public_post = $this->author_has_a_public_post( $author_id );
		if ( $has_public_post ) {
			return true;
		}

		// Then check if the author has at least one post where the status is the same as the global setting.
		$has_public_post_depending_on_the_global_setting = $this->author_has_a_post_with_is_public_null( $author_id );
		if ( $has_public_post_depending_on_the_global_setting ) {
			return null;
		}

		return false;
	}

	/**
	 * Returns whether the author has at least one public post.
	 *
	 * **Note**: It uses WP_Query to determine the number of posts,
	 * not the indexables table.
	 *
	 * @param string $user_id The user ID.
	 *
	 * @return bool Whether the author has at least one public post.
	 */
	public function author_has_public_posts_wp( $user_id ) {
		$post_types        = \array_intersect( $this->get_author_archive_post_types(), $this->post_type_helper->get_indexable_post_types() );
		$public_post_stati = \array_values( \array_filter( \get_post_stati(), 'is_post_status_viewable' ) );

		$args  = [
			'post_type'   => $post_types,
			'post_status' => $public_post_stati,
			'author'      => $user_id,
		];
		$query = new WP_Query( $args );

		if ( ! empty( $query->posts ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Checks whether author archives are disabled.
	 *
	 * @return bool `true` if author archives are disabled, `false` if not.
	 */
	public function are_disabled() {
		return $this->options_helper->get( 'disable-author' );
	}

	/**
	 * Returns whether the author has at least one public post.
	 *
	 * @codeCoverageIgnore It looks for the first ID through the ORM and converts it to a boolean.
	 *
	 * @param int $author_id The author ID.
	 *
	 * @return bool Whether the author has at least one public post.
	 */
	protected function author_has_a_public_post( $author_id ) {
		$cache_key        = 'author_has_a_public_post_' . $author_id;
		$indexable_exists = \wp_cache_get( $cache_key );

		if ( $indexable_exists === false ) {
			$indexable_exists = Model::of_type( 'Indexable' )
				->select( 'id' )
				->where( 'object_type', 'post' )
				->where_in( 'object_sub_type', $this->get_author_archive_post_types() )
				->where( 'author_id', $author_id )
				->where( 'is_public', 1 )
				->find_one();

			if ( $indexable_exists === false ) {
				// Cache no results to prevent full table scanning on authors with no public posts.
				\wp_cache_set( $cache_key, 0, '', \wp_rand( ( 2 * \HOUR_IN_SECONDS ), ( 4 * \HOUR_IN_SECONDS ) ) );
			}
		}

		return (bool) $indexable_exists;
	}

	/**
	 * Returns whether the author has at least one post with the is public null.
	 *
	 * @codeCoverageIgnore It looks for the first ID through the ORM and converts it to a boolean.
	 *
	 * @param int $author_id The author ID.
	 *
	 * @return bool Whether the author has at least one post with the is public null.
	 */
	protected function author_has_a_post_with_is_public_null( $author_id ) {
		$cache_key        = 'author_has_a_post_with_is_public_null_' . $author_id;
		$indexable_exists = \wp_cache_get( $cache_key );

		if ( $indexable_exists === false ) {
			$indexable_exists = Model::of_type( 'Indexable' )
				->select( 'id' )
				->where( 'object_type', 'post' )
				->where_in( 'object_sub_type', $this->get_author_archive_post_types() )
				->where( 'author_id', $author_id )
				->where_null( 'is_public' )
				->find_one();

			if ( $indexable_exists === false ) {
				// Cache no results to prevent full table scanning on authors with no is public null posts.
				\wp_cache_set( $cache_key, 0, '', \wp_rand( ( 2 * \HOUR_IN_SECONDS ), ( 4 * \HOUR_IN_SECONDS ) ) );
			}
		}

		return (bool) $indexable_exists;
	}
}
aioseo-helper.php000066600000002367151117651630010033 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use wpdb;

/**
 * The AIOSEO Helper.
 */
class Aioseo_Helper {

	/**
	 * The WordPress database instance.
	 *
	 * @var wpdb
	 */
	protected $wpdb;

	/**
	 * The wpdb helper.
	 *
	 * @var Wpdb_Helper
	 */
	protected $wpdb_helper;

	/**
	 * Class constructor.
	 *
	 * @param wpdb        $wpdb        The WordPress database instance.
	 * @param Wpdb_Helper $wpdb_helper The wpdb helper.
	 */
	public function __construct(
		wpdb $wpdb,
		Wpdb_Helper $wpdb_helper
	) {
		$this->wpdb        = $wpdb;
		$this->wpdb_helper = $wpdb_helper;
	}

	/**
	 * Retrieves the AIOSEO table name along with the db prefix.
	 *
	 * @return string The AIOSEO table name along with the db prefix.
	 */
	public function get_table() {
		return $this->wpdb->prefix . 'aioseo_posts';
	}

	/**
	 * Determines if the AIOSEO database table exists.
	 *
	 * @return bool True if the table is found.
	 */
	public function aioseo_exists() {
		return $this->wpdb_helper->table_exists( $this->get_table() ) === true;
	}

	/**
	 * Retrieves the option where the global settings exist.
	 *
	 * @return array The option where the global settings exist.
	 */
	public function get_global_option() {
		return \json_decode( \get_option( 'aioseo_options', '' ), true );
	}
}
options-helper.php000066600000007571151117651630010251 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WPSEO_Option_Social;
use WPSEO_Option_Titles;
use WPSEO_Options;

/**
 * A helper object for options.
 */
class Options_Helper {

	/**
	 * Retrieves a single field from any option for the SEO plugin. Keys are always unique.
	 *
	 * @codeCoverageIgnore We have to write test when this method contains own code.
	 *
	 * @param string $key           The key it should return.
	 * @param mixed  $default_value The default value that should be returned if the key isn't set.
	 *
	 * @return mixed|null Returns value if found, $default_value if not.
	 */
	public function get( $key, $default_value = null ) {
		return WPSEO_Options::get( $key, $default_value );
	}

	/**
	 * Sets a single field to the options.
	 *
	 * @param string $key   The key to set.
	 * @param mixed  $value The value to set.
	 *
	 * @return mixed|null Returns value if found.
	 */
	public function set( $key, $value ) {
		return WPSEO_Options::set( $key, $value );
	}

	/**
	 * Get a specific default value for an option.
	 *
	 * @param string $option_name The option for which you want to retrieve a default.
	 * @param string $key         The key within the option who's default you want.
	 *
	 * @return mixed The default value.
	 */
	public function get_default( $option_name, $key ) {
		return WPSEO_Options::get_default( $option_name, $key );
	}

	/**
	 * Retrieves the title separator.
	 *
	 * @return string The title separator.
	 */
	public function get_title_separator() {
		$default = $this->get_default( 'wpseo_titles', 'separator' );

		// Get the titles option and the separator options.
		$separator         = $this->get( 'separator' );
		$seperator_options = $this->get_separator_options();

		// This should always be set, but just to be sure.
		if ( isset( $seperator_options[ $separator ] ) ) {
			// Set the new replacement.
			$replacement = $seperator_options[ $separator ];
		}
		elseif ( isset( $seperator_options[ $default ] ) ) {
			$replacement = $seperator_options[ $default ];
		}
		else {
			$replacement = \reset( $seperator_options );
		}

		/**
		 * Filter: 'wpseo_replacements_filter_sep' - Allow customization of the separator character(s).
		 *
		 * @api string $replacement The current separator.
		 */
		return \apply_filters( 'wpseo_replacements_filter_sep', $replacement );
	}

	/**
	 * Retrieves a default value from the option titles.
	 *
	 * @param string $option_titles_key The key of the option title you wish to get.
	 *
	 * @return string The option title.
	 */
	public function get_title_default( $option_titles_key ) {
		$default_titles = $this->get_title_defaults();
		if ( ! empty( $default_titles[ $option_titles_key ] ) ) {
			return $default_titles[ $option_titles_key ];
		}

		return '';
	}

	/**
	 * Retrieves the default option titles.
	 *
	 * @codeCoverageIgnore We have to write test when this method contains own code.
	 *
	 * @return array The title defaults.
	 */
	protected function get_title_defaults() {
		return WPSEO_Option_Titles::get_instance()->get_defaults();
	}

	/**
	 * Get the available separator options.
	 *
	 * @return array
	 */
	protected function get_separator_options() {
		return WPSEO_Option_Titles::get_instance()->get_separator_options();
	}

	/**
	 * Checks whether a social URL is valid, with empty strings being valid social URLs.
	 *
	 * @param string $url The url to be checked.
	 *
	 * @return bool Whether the URL is valid.
	 */
	public function is_social_url_valid( $url ) {
		return $url === '' || WPSEO_Option_Social::get_instance()->validate_social_url( $url );
	}

	/**
	 * Checks whether a twitter id is valid, with empty strings being valid twitter id.
	 *
	 * @param string $twitter_id The twitter id to be checked.
	 *
	 * @return bool Whether the twitter id is valid.
	 */
	public function is_twitter_id_valid( $twitter_id ) {
		return empty( $twitter_id ) || WPSEO_Option_Social::get_instance()->validate_twitter_id( $twitter_id, false );
	}
}
string-helper.php000066600000002302151117651630010047 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for string operations.
 */
class String_Helper {

	/**
	 * Strips all HTML tags including script and style.
	 *
	 * @param string $text The text to strip the tags from.
	 *
	 * @return string The processed string.
	 */
	public function strip_all_tags( $text ) {
		return \wp_strip_all_tags( $text );
	}

	/**
	 * Standardize whitespace in a string.
	 *
	 * Replace line breaks, carriage returns, tabs with a space, then remove double spaces.
	 *
	 * @param string $text Text input to standardize.
	 *
	 * @return string
	 */
	public function standardize_whitespace( $text ) {
		return \trim( \str_replace( '  ', ' ', \str_replace( [ "\t", "\n", "\r", "\f" ], ' ', $text ) ) );
	}

	/**
	 * First strip out registered and enclosing shortcodes using native WordPress strip_shortcodes function.
	 * Then strip out the shortcodes with a filthy regex, because people don't properly register their shortcodes.
	 *
	 * @param string $text Input string that might contain shortcodes.
	 *
	 * @return string String without shortcodes.
	 */
	public function strip_shortcode( $text ) {
		return \preg_replace( '`\[[^\]]+\]`s', '', \strip_shortcodes( $text ) );
	}
}
robots-txt-helper.php000066600000005272151117651630010677 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Values\Robots\User_Agent_List;

/**
 * A helper object for the robots txt file.
 */
class Robots_Txt_Helper {

	/**
	 * Holds a list of user agents with directives.
	 *
	 * @var User_Agent_List $robots_txt_user_agents
	 */
	protected $robots_txt_user_agents;

	/**
	 * Holds an array with absolute URLs of sitemaps.
	 *
	 * @var array
	 */
	protected $robots_txt_sitemaps;

	/**
	 * Constructor for Robots_Txt_Helper.
	 */
	public function __construct() {
		$this->robots_txt_user_agents = new User_Agent_List();
		$this->robots_txt_sitemaps    = [];
	}

	/**
	 * Add a disallow rule for a specific user agent if it does not exist yet.
	 *
	 * @param string $user_agent The user agent to add the disallow rule to.
	 * @param string $path       The path to add as a disallow rule.
	 *
	 * @return void
	 */
	public function add_disallow( $user_agent, $path ) {
		$user_agent_container = $this->robots_txt_user_agents->get_user_agent( $user_agent );
		$user_agent_container->add_disallow_directive( $path );
	}

	/**
	 * Add an allow rule for a specific user agent if it does not exist yet.
	 *
	 * @param string $user_agent The user agent to add the allow rule to.
	 * @param string $path       The path to add as a allow rule.
	 *
	 * @return void
	 */
	public function add_allow( $user_agent, $path ) {
		$user_agent_container = $this->robots_txt_user_agents->get_user_agent( $user_agent );
		$user_agent_container->add_allow_directive( $path );
	}

	/**
	 * Add sitemap to robots.txt if it does not exist yet.
	 *
	 * @param string $absolute_path The absolute path to the sitemap to add.
	 *
	 * @return void
	 */
	public function add_sitemap( $absolute_path ) {
		if ( ! \in_array( $absolute_path, $this->robots_txt_sitemaps, true ) ) {
			$this->robots_txt_sitemaps[] = $absolute_path;
		}
	}

	/**
	 * Get all registered disallow directives per user agent.
	 *
	 * @return array The registered disallow directives per user agent.
	 */
	public function get_disallow_directives() {
		return $this->robots_txt_user_agents->get_disallow_directives();
	}

	/**
	 * Get all registered allow directives per user agent.
	 *
	 * @return array The registered allow directives per user agent.
	 */
	public function get_allow_directives() {
		return $this->robots_txt_user_agents->get_allow_directives();
	}

	/**
	 * Get all registered sitemap rules.
	 *
	 * @return array The registered sitemap rules.
	 */
	public function get_sitemap_rules() {
		return $this->robots_txt_sitemaps;
	}

	/**
	 * Get all registered user agents
	 *
	 * @return array The registered user agents.
	 */
	public function get_robots_txt_user_agents() {
		return $this->robots_txt_user_agents->get_user_agents();
	}
}
woocommerce-helper.php000066600000002440151117651630011063 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * Represents helper methods for WooCommerce.
 */
class Woocommerce_Helper {

	/**
	 * Checks if WooCommerce is active.
	 *
	 * @return bool Is WooCommerce active.
	 */
	public function is_active() {
		return \class_exists( 'WooCommerce' );
	}

	/**
	 * Returns the id of the set WooCommerce shop page.
	 *
	 * @return int The ID of the set page.
	 */
	public function get_shop_page_id() {
		if ( ! \function_exists( 'wc_get_page_id' ) ) {
			return -1;
		}

		return \wc_get_page_id( 'shop' );
	}

	/**
	 * Checks if the current page is a WooCommerce shop page.
	 *
	 * @return bool True when the page is a shop page.
	 */
	public function is_shop_page() {
		if ( ! \function_exists( 'is_shop' ) ) {
			return false;
		}

		if ( ! \is_shop() ) {
			return false;
		}

		if ( \is_search() ) {
			return false;
		}

		return true;
	}

	/**
	 * Checks if the current page is a WooCommerce shop page.
	 *
	 * @return bool True when the page is a shop page.
	 */
	public function current_post_is_terms_and_conditions_page() {
		if ( ! \function_exists( 'wc_terms_and_conditions_page_id' ) ) {
			return false;
		}

		global $post;

		if ( ! isset( $post->ID ) ) {
			return false;
		}

		return \intval( $post->ID ) === \intval( \wc_terms_and_conditions_page_id() );
	}
}
pagination-helper.php000066600000006745151117651630010711 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use Yoast\WP\SEO\Wrappers\WP_Query_Wrapper;
use Yoast\WP\SEO\Wrappers\WP_Rewrite_Wrapper;

/**
 * A helper object for pagination.
 *
 * Used for the canonical URL and the rel "next" and "prev" meta tags.
 */
class Pagination_Helper {

	/**
	 * Holds the WP rewrite wrapper instance.
	 *
	 * @var WP_Rewrite_Wrapper WP_Rewrite wrapper.
	 */
	protected $wp_rewrite_wrapper;

	/**
	 * Holds the WP query wrapper instance.
	 *
	 * @var WP_Query_Wrapper WP_Query wrapper.
	 */
	protected $wp_query_wrapper;

	/**
	 * Pagination_Helper constructor.
	 *
	 * @param WP_Rewrite_Wrapper $wp_rewrite_wrapper The rewrite wrapper.
	 * @param WP_Query_Wrapper   $wp_query_wrapper   The query wrapper.
	 */
	public function __construct(
		WP_Rewrite_Wrapper $wp_rewrite_wrapper,
		WP_Query_Wrapper $wp_query_wrapper
	) {
		$this->wp_rewrite_wrapper = $wp_rewrite_wrapper;
		$this->wp_query_wrapper   = $wp_query_wrapper;
	}

	/**
	 * Checks whether adjacent rel links are disabled.
	 *
	 * @return bool Whether adjacent rel links are disabled or not.
	 */
	public function is_rel_adjacent_disabled() {
		/**
		 * Filter: 'wpseo_disable_adjacent_rel_links' - Allows disabling of Yoast adjacent links if this is being handled by other code.
		 *
		 * @api bool $links_generated Indicates if other code has handled adjacent links.
		 */
		return \apply_filters( 'wpseo_disable_adjacent_rel_links', false );
	}

	/**
	 * Builds a paginated URL.
	 *
	 * @param string $url                   The un-paginated URL of the current archive.
	 * @param string $page                  The page number to add on to $url for the $link tag.
	 * @param bool   $add_pagination_base   Optional. Whether to add the pagination base (`page`) to the url.
	 * @param string $pagination_query_name Optional. The name of the query argument that holds the current page.
	 *
	 * @return string The paginated URL.
	 */
	public function get_paginated_url( $url, $page, $add_pagination_base = true, $pagination_query_name = 'page' ) {
		$wp_rewrite = $this->wp_rewrite_wrapper->get();

		if ( $wp_rewrite->using_permalinks() ) {
			$url = \trailingslashit( $url );
			if ( $add_pagination_base ) {
				$url .= \trailingslashit( $wp_rewrite->pagination_base );
			}

			return \user_trailingslashit( $url . $page );
		}

		return \add_query_arg( $pagination_query_name, $page, \user_trailingslashit( $url ) );
	}

	/**
	 * Gets the number of archive pages.
	 *
	 * @return int The number of archive pages.
	 */
	public function get_number_of_archive_pages() {
		$wp_query = $this->wp_query_wrapper->get_query();

		return (int) $wp_query->max_num_pages;
	}

	/**
	 * Returns the current page for paged archives.
	 *
	 * @return int The current archive page.
	 */
	public function get_current_archive_page_number() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return (int) $wp_query->get( 'paged' );
	}

	/**
	 * Returns the current page for paged post types.
	 *
	 * @return int The current post page.
	 */
	public function get_current_post_page_number() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return (int) $wp_query->get( 'page' );
	}

	/**
	 * Returns the current page number.
	 *
	 * @return int The current page number.
	 */
	public function get_current_page_number() {
		// Get the page number for an archive page.
		$page_number = \get_query_var( 'paged', 1 );
		if ( $page_number > 1 ) {
			return $page_number;
		}

		// Get the page number for a page in a paginated post.
		return \get_query_var( 'page', 1 );
	}
}
score-icon-helper.php000066600000004321151117651630010605 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WPSEO_Rank;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Presenters\Score_Icon_Presenter;

/**
 * A helper object for score icons.
 */
class Score_Icon_Helper {

	/**
	 * Holds the Robots_Helper.
	 *
	 * @var Robots_Helper
	 */
	protected $robots_helper;

	/**
	 * Constructs a Score_Helper.
	 *
	 * @param Robots_Helper $robots_helper The Robots_Helper.
	 */
	public function __construct( Robots_Helper $robots_helper ) {
		$this->robots_helper = $robots_helper;
	}

	/**
	 * Creates a Score_Icon_Presenter for the readability analysis.
	 *
	 * @param int    $score       The readability analysis score.
	 * @param string $extra_class Optional. Any extra class.
	 *
	 * @return Score_Icon_Presenter The Score_Icon_Presenter.
	 */
	public function for_readability( $score, $extra_class = '' ) {
		$rank  = WPSEO_Rank::from_numeric_score( (int) $score );
		$class = $rank->get_css_class();
		if ( $extra_class ) {
			$class .= " $extra_class";
		}

		return new Score_Icon_Presenter( $rank->get_label(), $class );
	}

	/**
	 * Creates a Score_Icon_Presenter for the SEO analysis from an indexable.
	 *
	 * @param Indexable|false $indexable      The Indexable.
	 * @param string          $extra_class    Optional. Any extra class.
	 * @param string          $no_index_title Optional. Override the title when not indexable.
	 *
	 * @return Score_Icon_Presenter The Score_Icon_Presenter.
	 */
	public function for_seo( $indexable, $extra_class = '', $no_index_title = '' ) {
		$is_indexable = $indexable && $this->robots_helper->is_indexable( $indexable );

		if ( ! $is_indexable ) {
			$rank  = new WPSEO_Rank( WPSEO_Rank::NO_INDEX );
			$title = empty( $no_index_title ) ? $rank->get_label() : $no_index_title;
		}
		elseif ( empty( $indexable && $indexable->primary_focus_keyword ) ) {
			$rank  = new WPSEO_Rank( WPSEO_Rank::BAD );
			$title = \__( 'Focus keyphrase not set', 'wordpress-seo' );
		}
		else {
			$rank  = WPSEO_Rank::from_numeric_score( ( $indexable ) ? $indexable->primary_focus_keyword_score : 0 );
			$title = $rank->get_label();
		}

		$class = $rank->get_css_class();
		if ( $extra_class ) {
			$class .= " $extra_class";
		}

		return new Score_Icon_Presenter( $title, $class );
	}
}
current-page-helper.php000066600000030404151117651630011141 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WP_Post;
use Yoast\WP\SEO\Wrappers\WP_Query_Wrapper;

/**
 * A helper object for WordPress posts.
 */
class Current_Page_Helper {

	/**
	 * The WP Query wrapper.
	 *
	 * @var WP_Query_Wrapper
	 */
	private $wp_query_wrapper;

	/**
	 * Current_Page_Helper constructor.
	 *
	 * @codeCoverageIgnore It only sets dependencies.
	 *
	 * @param WP_Query_Wrapper $wp_query_wrapper The wrapper for WP_Query.
	 */
	public function __construct(
		WP_Query_Wrapper $wp_query_wrapper
	) {
		$this->wp_query_wrapper = $wp_query_wrapper;
	}

	/**
	 * Returns the page type for the current request.
	 *
	 * @codeCoverageIgnore It just depends on other functions for its result.
	 *
	 * @return string Page type.
	 */
	public function get_page_type() {
		switch ( true ) {
			case $this->is_search_result():
				return 'Search_Result_Page';
			case $this->is_static_posts_page():
				return 'Static_Posts_Page';
			case $this->is_home_static_page():
				return 'Static_Home_Page';
			case $this->is_home_posts_page():
				return 'Home_Page';
			case $this->is_simple_page():
				return 'Post_Type';
			case $this->is_post_type_archive():
				return 'Post_Type_Archive';
			case $this->is_term_archive():
				return 'Term_Archive';
			case $this->is_author_archive():
				return 'Author_Archive';
			case $this->is_date_archive():
				return 'Date_Archive';
			case $this->is_404():
				return 'Error_Page';
		}

		return 'Fallback';
	}

	/**
	 * Checks if the currently opened page is a simple page.
	 *
	 * @return bool Whether the currently opened page is a simple page.
	 */
	public function is_simple_page() {
		return $this->get_simple_page_id() > 0;
	}

	/**
	 * Returns the id of the currently opened page.
	 *
	 * @return int The id of the currently opened page.
	 */
	public function get_simple_page_id() {
		if ( \is_singular() ) {
			return \get_the_ID();
		}

		if ( $this->is_posts_page() ) {
			return \get_option( 'page_for_posts' );
		}

		/**
		 * Filter: Allow changing the default page id.
		 *
		 * @api int $page_id The default page id.
		 */
		return \apply_filters( 'wpseo_frontend_page_type_simple_page_id', 0 );
	}

	/**
	 * Returns the id of the currently opened author archive.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return int The id of the currently opened author archive.
	 */
	public function get_author_id() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->get( 'author' );
	}

	/**
	 * Returns the id of the front page.
	 *
	 * @return int The id of the front page. 0 if the front page is not a static page.
	 */
	public function get_front_page_id() {
		if ( \get_option( 'show_on_front' ) !== 'page' ) {
			return 0;
		}

		return (int) \get_option( 'page_on_front' );
	}

	/**
	 * Returns the id of the currently opened term archive.
	 *
	 * @return int The id of the currently opened term archive.
	 */
	public function get_term_id() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		if ( $wp_query->is_category() ) {
			return $wp_query->get( 'cat' );
		}
		if ( $wp_query->is_tag() ) {
			return $wp_query->get( 'tag_id' );
		}
		if ( $wp_query->is_tax() ) {
			$queried_object = $wp_query->get_queried_object();
			if ( $queried_object && ! \is_wp_error( $queried_object ) ) {
				return $queried_object->term_id;
			}
		}

		return 0;
	}

	/**
	 * Returns the post type of the main query.
	 *
	 * @return string The post type of the main query.
	 */
	public function get_queried_post_type() {
		$wp_query  = $this->wp_query_wrapper->get_main_query();
		$post_type = $wp_query->get( 'post_type' );
		if ( \is_array( $post_type ) ) {
			$post_type = \reset( $post_type );
		}

		return $post_type;
	}

	/**
	 * Returns the permalink of the currently opened date archive.
	 * If the permalink was cached, it returns this permalink.
	 * If not, we call another function to get the permalink through wp_query.
	 *
	 * @return string The permalink of the currently opened date archive.
	 */
	public function get_date_archive_permalink() {
		static $date_archive_permalink;

		if ( isset( $date_archive_permalink ) ) {
			return $date_archive_permalink;
		}

		$date_archive_permalink = $this->get_non_cached_date_archive_permalink();

		return $date_archive_permalink;
	}

	/**
	 * Determine whether this is the homepage and shows posts.
	 *
	 * @return bool Whether or not the current page is the homepage that displays posts.
	 */
	public function is_home_posts_page() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		if ( ! $wp_query->is_home() ) {
			return false;
		}

		/*
		 * Whether the static page's `Homepage` option is actually not set to a page.
		 * Otherwise WordPress proceeds to handle the homepage as a `Your latest posts` page.
		 */
		if ( (int) \get_option( 'page_on_front' ) === 0 ) {
			return true;
		}

		return \get_option( 'show_on_front' ) === 'posts';
	}

	/**
	 * Determine whether this is the static frontpage.
	 *
	 * @return bool Whether or not the current page is a static frontpage.
	 */
	public function is_home_static_page() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		if ( ! $wp_query->is_front_page() ) {
			return false;
		}

		if ( \get_option( 'show_on_front' ) !== 'page' ) {
			return false;
		}

		return $wp_query->is_page( \get_option( 'page_on_front' ) );
	}

	/**
	 * Determine whether this is the static posts page.
	 *
	 * @return bool Whether or not the current page is a static posts page.
	 */
	public function is_static_posts_page() {
		$wp_query       = $this->wp_query_wrapper->get_main_query();
		$queried_object = $wp_query->get_queried_object();

		return (
			$wp_query->is_posts_page
			&& \is_a( $queried_object, WP_Post::class )
			&& $queried_object->post_type === 'page'
		);
	}

	/**
	 * Determine whether this is the statically set posts page, when it's not the frontpage.
	 *
	 * @return bool Whether or not it's a non-frontpage, statically set posts page.
	 */
	public function is_posts_page() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		if ( ! $wp_query->is_home() ) {
			return false;
		}

		return \get_option( 'show_on_front' ) === 'page';
	}

	/**
	 * Determine whether this is a post type archive.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return bool Whether nor not the current page is a post type archive.
	 */
	public function is_post_type_archive() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->is_post_type_archive();
	}

	/**
	 * Determine whether this is a term archive.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return bool Whether nor not the current page is a term archive.
	 */
	public function is_term_archive() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->is_tax || $wp_query->is_tag || $wp_query->is_category;
	}

	/**
	 * Determine whether this is an attachment page.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return bool Whether nor not the current page is an attachment page.
	 */
	public function is_attachment() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->is_attachment;
	}

	/**
	 * Determine whether this is an author archive.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return bool Whether nor not the current page is an author archive.
	 */
	public function is_author_archive() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->is_author();
	}

	/**
	 * Determine whether this is an date archive.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return bool Whether nor not the current page is an date archive.
	 */
	public function is_date_archive() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->is_date();
	}

	/**
	 * Determine whether this is a search result.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return bool Whether nor not the current page is a search result.
	 */
	public function is_search_result() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->is_search();
	}

	/**
	 * Determine whether this is a 404 page.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return bool Whether nor not the current page is a 404 page.
	 */
	public function is_404() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->is_404();
	}

	/**
	 * Checks if the current page is the post format archive.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return bool Whether or not the current page is the post format archive.
	 */
	public function is_post_format_archive() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->is_tax( 'post_format' );
	}

	/**
	 * Determine whether this page is an taxonomy archive page for multiple terms (url: /term-1,term2/).
	 *
	 * @return bool Whether or not the current page is an archive page for multiple terms.
	 */
	public function is_multiple_terms_page() {
		if ( ! $this->is_term_archive() ) {
			return false;
		}

		return $this->count_queried_terms() > 1;
	}

	/**
	 * Checks whether the current page is paged.
	 *
	 * @codeCoverageIgnore This method only calls a WordPress function.
	 *
	 * @return bool Whether the current page is paged.
	 */
	public function is_paged() {
		return \is_paged();
	}

	/**
	 * Checks if the current page is the front page.
	 *
	 * @codeCoverageIgnore It wraps WordPress functionality.
	 *
	 * @return bool Whether or not the current page is the front page.
	 */
	public function is_front_page() {
		$wp_query = $this->wp_query_wrapper->get_main_query();

		return $wp_query->is_front_page();
	}

	/**
	 * Retrieves the current admin page.
	 *
	 * @codeCoverageIgnore It only wraps a global WordPress variable.
	 *
	 * @return string The current page.
	 */
	public function get_current_admin_page() {
		global $pagenow;

		return $pagenow;
	}

	/**
	 * Check if the current opened page is a Yoast SEO page.
	 *
	 * @return bool True when current page is a yoast seo plugin page.
	 */
	public function is_yoast_seo_page() {
		static $is_yoast_seo;

		if ( $is_yoast_seo === null ) {
			$current_page = \filter_input( \INPUT_GET, 'page' );
			$is_yoast_seo = ( \is_string( $current_page ) && \strpos( $current_page, 'wpseo_' ) === 0 );
		}

		return $is_yoast_seo;
	}

	/**
	 * Returns the current Yoast SEO page.
	 * (E.g. the `page` query variable in the URL).
	 *
	 * @return string The current Yoast SEO page.
	 */
	public function get_current_yoast_seo_page() {
		static $current_yoast_seo_page;

		if ( $current_yoast_seo_page === null ) {
			$current_yoast_seo_page = \filter_input( \INPUT_GET, 'page' );
		}

		return $current_yoast_seo_page;
	}

	/**
	 * Checks if the current global post is the privacy policy page.
	 *
	 * @return bool current global post is set as privacy page
	 */
	public function current_post_is_privacy_policy() {
		global $post;

		if ( ! isset( $post->ID ) ) {
			return false;
		}

		return \intval( $post->ID ) === \intval( \get_option( 'wp_page_for_privacy_policy', false ) );
	}

	/**
	 * Returns the permalink of the currently opened date archive.
	 *
	 * @return string The permalink of the currently opened date archive.
	 */
	protected function get_non_cached_date_archive_permalink() {
		$date_archive_permalink = '';
		$wp_query               = $this->wp_query_wrapper->get_main_query();

		if ( $wp_query->is_day() ) {
			$date_archive_permalink = \get_day_link( $wp_query->get( 'year' ), $wp_query->get( 'monthnum' ), $wp_query->get( 'day' ) );
		}
		if ( $wp_query->is_month() ) {
			$date_archive_permalink = \get_month_link( $wp_query->get( 'year' ), $wp_query->get( 'monthnum' ) );
		}
		if ( $wp_query->is_year() ) {
			$date_archive_permalink = \get_year_link( $wp_query->get( 'year' ) );
		}

		return $date_archive_permalink;
	}

	/**
	 * Counts the total amount of queried terms.
	 *
	 * @codeCoverageIgnore This relies too much on WordPress dependencies.
	 *
	 * @return int The amoumt of queried terms.
	 */
	protected function count_queried_terms() {
		$wp_query      = $this->wp_query_wrapper->get_main_query();
		$term          = $wp_query->get_queried_object();
		$queried_terms = $wp_query->tax_query->queried_terms;
		if ( empty( $queried_terms[ $term->taxonomy ]['terms'] ) ) {
			return 0;
		}

		return \count( $queried_terms[ $term->taxonomy ]['terms'] );
	}
}
home-url-helper.php000066600000001402151117651630010271 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for the home URL.
 */
class Home_Url_Helper {

	/**
	 * The home url.
	 *
	 * @var string
	 */
	protected static $home_url;

	/**
	 * The parsed home url.
	 *
	 * @var array
	 */
	protected static $parsed_home_url;

	/**
	 * Retrieves the home url.
	 *
	 * @return string The home url.
	 */
	public function get() {
		if ( static::$home_url === null ) {
			static::$home_url = \home_url();
		}

		return static::$home_url;
	}

	/**
	 * Retrieves the home url that has been parsed.
	 *
	 * @return array The parsed url.
	 */
	public function get_parsed() {
		if ( static::$parsed_home_url === null ) {
			static::$parsed_home_url = \wp_parse_url( $this->get() );
		}

		return static::$parsed_home_url;
	}
}
twitter/image-helper.php000066600000002200151117651630011322 0ustar00<?php

namespace Yoast\WP\SEO\Helpers\Twitter;

use Yoast\WP\SEO\Helpers\Image_Helper as Base_Image_Helper;

/**
 * A helper object for Twitter images.
 */
class Image_Helper {

	/**
	 * The base image helper.
	 *
	 * @var Base_Image_Helper
	 */
	private $image;

	/**
	 * Image_Helper constructor.
	 *
	 * @codeCoverageIgnore
	 *
	 * @param Base_Image_Helper $image The image helper.
	 */
	public function __construct( Base_Image_Helper $image ) {
		$this->image = $image;
	}

	/**
	 * The image size to use for Twitter.
	 *
	 * @return string Image size string.
	 */
	public function get_image_size() {
		/**
		 * Filter: 'wpseo_twitter_image_size' - Allow changing the Twitter Card image size.
		 *
		 * @api string $featured_img Image size string.
		 */
		return (string) \apply_filters( 'wpseo_twitter_image_size', 'full' );
	}

	/**
	 * Retrieves an image url by its id.
	 *
	 * @codeCoverageIgnore It is a wrapper method.
	 *
	 * @param int $image_id The image id.
	 *
	 * @return string The image url.
	 */
	public function get_by_id( $image_id ) {
		return $this->image->get_attachment_image_source( $image_id, $this->get_image_size() );
	}
}
post-helper.php000066600000012721151117651630007534 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WP_Post;
use Yoast\WP\SEO\Repositories\Indexable_Repository;

/**
 * A helper object for post related things.
 */
class Post_Helper {

	/**
	 * Holds the String_Helper instance.
	 *
	 * @var String_Helper
	 */
	private $string;

	/**
	 * Holds the Post_Type_Helper instance.
	 *
	 * @var Post_Type_Helper
	 */
	private $post_type;

	/**
	 * Represents the indexables repository.
	 *
	 * @var Indexable_Repository
	 */
	private $repository;

	/**
	 * Post_Helper constructor.
	 *
	 * @codeCoverageIgnore It only sets dependencies.
	 *
	 * @param String_Helper    $string_helper The string helper.
	 * @param Post_Type_Helper $post_type_helper The string helper.
	 */
	public function __construct(
		String_Helper $string_helper,
		Post_Type_Helper $post_type_helper
	) {
		$this->string    = $string_helper;
		$this->post_type = $post_type_helper;
	}

	/**
	 * Sets the indexable repository. Done to avoid circular dependencies.
	 *
	 * @required
	 *
	 * @param Indexable_Repository $repository The indexable repository.
	 */
	public function set_indexable_repository( Indexable_Repository $repository ) {
		$this->repository = $repository;
	}

	/**
	 * Removes all shortcode tags from the given content.
	 *
	 * @codeCoverageIgnore It only wraps a WordPress function.
	 *
	 * @param string $content Content to remove all the shortcode tags from.
	 *
	 * @return string Content without shortcode tags.
	 */
	public function strip_shortcodes( $content ) {
		return \strip_shortcodes( $content );
	}

	/**
	 * Retrieves the post excerpt (without tags).
	 *
	 * @codeCoverageIgnore It only wraps another helper method.
	 *
	 * @param int $post_id Post ID.
	 *
	 * @return string Post excerpt (without tags).
	 */
	public function get_the_excerpt( $post_id ) {
		return $this->string->strip_all_tags( \get_the_excerpt( $post_id ) );
	}

	/**
	 * Retrieves the post type of the current post.
	 *
	 * @codeCoverageIgnore It only wraps a WordPress function.
	 *
	 * @param WP_Post|null $post The post.
	 *
	 * @return string|false Post type on success, false on failure.
	 */
	public function get_post_type( $post = null ) {
		return \get_post_type( $post );
	}

	/**
	 * Retrieves the post title with fallback to `No title`.
	 *
	 * @param int $post_id Optional. Post ID.
	 *
	 * @return string The post title with fallback to `No title`.
	 */
	public function get_post_title_with_fallback( $post_id = 0 ) {
		$post_title = \get_the_title( $post_id );
		if ( $post_title ) {
			return $post_title;
		}

		return \__( 'No title', 'wordpress-seo' );
	}

	/**
	 * Retrieves post data given a post ID.
	 *
	 * @codeCoverageIgnore It wraps a WordPress function.
	 *
	 * @param int $post_id Post ID.
	 *
	 * @return WP_Post|null The post.
	 */
	public function get_post( $post_id ) {
		return \get_post( $post_id );
	}

	/**
	 * Updates the has_public_posts field on attachments for a post_parent.
	 *
	 * An attachment is represented by their post parent when:
	 * - The attachment has a post parent.
	 * - The attachment inherits the post status.
	 *
	 * @codeCoverageIgnore It relies too much on dependencies.
	 *
	 * @param int $post_parent      Post ID.
	 * @param int $has_public_posts Whether the parent is public.
	 *
	 * @return bool Whether the update was successful.
	 */
	public function update_has_public_posts_on_attachments( $post_parent, $has_public_posts ) {
		$query = $this->repository->query()
			->select( 'id' )
			->where( 'object_type', 'post' )
			->where( 'object_sub_type', 'attachment' )
			->where( 'post_status', 'inherit' )
			->where( 'post_parent', $post_parent );

		if ( $has_public_posts !== null ) {
			$query->where_raw( '( has_public_posts IS NULL OR has_public_posts <> %s )', [ $has_public_posts ] );
		}
		else {
			$query->where_not_null( 'has_public_posts' );
		}
		$results = $query->find_array();

		if ( empty( $results ) ) {
			return true;
		}

		$updated = $this->repository->query()
			->set( 'has_public_posts', $has_public_posts )
			->where_id_in( \wp_list_pluck( $results, 'id' ) )
			->update_many();

		return $updated !== false;
	}

	/**
	 * Determines if the post can be indexed.
	 *
	 * @param int $post_id Post ID to check.
	 *
	 * @return bool True if the post can be indexed.
	 */
	public function is_post_indexable( $post_id ) {
		// Don't index posts which are not public (i.e. viewable).
		$post_type    = \get_post_type( $post_id );
		$public_types = $this->post_type->get_indexable_post_types();
		if ( ! \in_array( $post_type, $public_types, true ) ) {
			return false;
		}

		// Don't index excluded post statuses.
		if ( \in_array( \get_post_status( $post_id ), $this->get_excluded_post_statuses(), true ) ) {
			return false;
		}

		// Don't index revisions of posts.
		if ( \wp_is_post_revision( $post_id ) ) {
			return false;
		}

		// Don't index autosaves that are not caught by the auto-draft check.
		if ( \wp_is_post_autosave( $post_id ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Retrieves the list of excluded post statuses.
	 *
	 * @return array The excluded post statuses.
	 */
	public function get_excluded_post_statuses() {
		return [ 'auto-draft' ];
	}

	/**
	 * Retrieves the list of public posts statuses.
	 *
	 * @return array The public post statuses.
	 */
	public function get_public_post_statuses() {
		/**
		 * Filter: 'wpseo_public_post_statuses' - List of public post statuses.
		 *
		 * @api array $post_statuses Post status list, defaults to array( 'publish' ).
		 */
		return \apply_filters( 'wpseo_public_post_statuses', [ 'publish' ] );
	}
}
redirect-helper.php000066600000003112151117651630010342 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for redirects.
 */
class Redirect_Helper {

	/**
	 * Wraps wp_redirect to allow testing for redirects.
	 *
	 * @codeCoverageIgnore It only wraps a WordPress function.
	 *
	 * @param string $location The path to redirect to.
	 * @param int    $status   The status code to use.
	 * @param string $reason   The reason for the redirect.
	 */
	public function do_unsafe_redirect( $location, $status = 302, $reason = 'Yoast SEO' ) {
		// phpcs:ignore WordPress.Security.SafeRedirect -- intentional, function has been renamed to make unsafe more clear.
		\wp_redirect( $location, $status, $reason );
		exit;
	}

	/**
	 * Wraps wp_safe_redirect to allow testing for safe redirects.
	 *
	 * @codeCoverageIgnore It only wraps a WordPress function.
	 *
	 * @param string $location The path to redirect to.
	 * @param int    $status   The status code to use.
	 * @param string $reason   The reason for the redirect.
	 */
	public function do_safe_redirect( $location, $status = 302, $reason = 'Yoast SEO' ) {
		\wp_safe_redirect( $location, $status, $reason );
		exit;
	}

	/**
	 * Wraps wp_redirect to allow testing for redirects.
	 *
	 * @deprecated 16.x
	 * @codeCoverageIgnore It only wraps a WordPress function.
	 *
	 * @param string $location The path to redirect to.
	 * @param int    $status   The status code to use.
	 */
	public function do_redirect( $location, $status = 302 ) {
		\_deprecated_function( __METHOD__, 'WPSEO 16.x', 'Yoast\WP\SEO\Helpers\Redirect_Helper::do_unsafe_redirect' );

		$this->do_unsafe_redirect( $location, $status );
	}
}
site-helper.php000066600000001066151117651630007513 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for site options.
 */
class Site_Helper {

	/**
	 * Retrieves the site name.
	 *
	 * @return string
	 */
	public function get_site_name() {
		return \wp_strip_all_tags( \get_bloginfo( 'name' ), true );
	}

	/**
	 * Checks if the current installation is a multisite and there has been a switch
	 * between the set multisites.
	 *
	 * @return bool True when there was a switch between the multisites.
	 */
	public function is_multisite_and_switched() {
		return \is_multisite() && \ms_is_switched();
	}
}
schema/html-helper.php000066600000003752151117651630010757 0ustar00<?php

namespace Yoast\WP\SEO\Helpers\Schema;

/**
 * Class HTML_Helper.
 */
class HTML_Helper {

	/**
	 * Sanitizes a HTML string by stripping all tags except headings, breaks, lists, links, paragraphs and formatting.
	 *
	 * @param string $html The original HTML.
	 *
	 * @return string The sanitized HTML.
	 */
	public function sanitize( $html ) {
		if ( ! $this->is_non_empty_string_or_stringable( $html ) ) {
			if ( \is_int( $html ) || \is_float( $html ) ) {
				return (string) $html;
			}

			return '';
		}

		return \strip_tags( $html, '<h1><h2><h3><h4><h5><h6><br><ol><ul><li><a><p><b><strong><i><em>' );
	}

	/**
	 * Strips the tags in a smart way.
	 *
	 * @param string $html The original HTML.
	 *
	 * @return string The sanitized HTML.
	 */
	public function smart_strip_tags( $html ) {
		if ( ! $this->is_non_empty_string_or_stringable( $html ) ) {
			if ( \is_int( $html ) || \is_float( $html ) ) {
				return (string) $html;
			}

			return '';
		}

		// Replace all new lines with spaces.
		$html = \preg_replace( '/(\r|\n)/', ' ', $html );

		// Replace <br> tags with spaces.
		$html = \preg_replace( '/<br(\s*)?\/?>/i', ' ', $html );

		// Replace closing </p> and other tags with the same tag with a space after it, so we don't end up connecting words when we remove them later.
		$html = \preg_replace( '/<\/(p|div|h\d)>/i', '</$1> ', $html );

		// Replace list items with list identifiers so it still looks natural.
		$html = \preg_replace( '/(<li[^>]*>)/i', '$1• ', $html );

		// Strip tags.
		$html = \wp_strip_all_tags( $html );

		// Replace multiple spaces with one space.
		$html = \preg_replace( '!\s+!', ' ', $html );

		return \trim( $html );
	}

	/**
	 * Verifies that the received input is either a string or stringable object.
	 *
	 * @param string $html The original HTML.
	 *
	 * @return bool
	 */
	private function is_non_empty_string_or_stringable( $html ) {
		return ( \is_string( $html ) || \is_object( $html ) && \method_exists( $html, '__toString' ) ) && ! empty( $html );
	}
}
schema/language-helper.php000066600000001462151117651630011572 0ustar00<?php

namespace Yoast\WP\SEO\Helpers\Schema;

/**
 * Class Language_Helper.
 */
class Language_Helper {

	/**
	 * Adds the `inLanguage` property to a Schema piece.
	 *
	 * Must use one of the language codes from the IETF BCP 47 standard. The
	 * language tag syntax is made of one or more subtags separated by a hyphen
	 * e.g. "en", "en-US", "zh-Hant-CN".
	 *
	 * @param array $data The Schema piece data.
	 *
	 * @return array The Schema piece data with added language property
	 */
	public function add_piece_language( $data ) {
		/**
		 * Filter: 'wpseo_schema_piece_language' - Allow changing the Schema piece language.
		 *
		 * @api string $type The Schema piece language.
		 */
		$data['inLanguage'] = \apply_filters( 'wpseo_schema_piece_language', \get_bloginfo( 'language' ), $data );

		return $data;
	}
}
schema/image-helper.php000066600000013566151117651630011101 0ustar00<?php

namespace Yoast\WP\SEO\Helpers\Schema;

use Yoast\WP\SEO\Helpers\Image_Helper as Main_Image_Helper;

/**
 * Class Image_Helper.
 */
class Image_Helper {

	/**
	 * The HTML helper.
	 *
	 * @var HTML_Helper
	 */
	private $html;

	/**
	 * The language helper.
	 *
	 * @var Language_Helper
	 */
	private $language;

	/**
	 * Represents the main image helper.
	 *
	 * @var Main_Image_Helper
	 */
	private $image;

	/**
	 * Image_Helper constructor.
	 *
	 * @codeCoverageIgnore It handles dependencies.
	 *
	 * @param HTML_Helper       $html     The HTML helper.
	 * @param Language_Helper   $language The language helper.
	 * @param Main_Image_Helper $image    The 'main' image helper.
	 */
	public function __construct( HTML_Helper $html, Language_Helper $language, Main_Image_Helper $image ) {
		$this->html     = $html;
		$this->language = $language;
		$this->image    = $image;
	}

	/**
	 * Find an image based on its URL and generate a Schema object for it.
	 *
	 * @param string $schema_id The `@id` to use for the returned image.
	 * @param string $url       The image URL to base our object on.
	 * @param string $caption   An optional caption.
	 * @param bool   $add_hash  Whether a hash will be added as a suffix in the @id.
	 *
	 * @return array Schema ImageObject array.
	 */
	public function generate_from_url( $schema_id, $url, $caption = '', $add_hash = false ) {
		$attachment_id = $this->image->get_attachment_by_url( $url );
		if ( $attachment_id > 0 ) {
			return $this->generate_from_attachment_id( $schema_id, $attachment_id, $caption, $add_hash );
		}

		return $this->simple_image_object( $schema_id, $url, $caption, $add_hash );
	}

	/**
	 * Retrieve data about an image from the database and use it to generate a Schema object.
	 *
	 * @param string $schema_id     The `@id` to use for the returned image.
	 * @param int    $attachment_id The attachment to retrieve data from.
	 * @param string $caption       The caption string, if there is one.
	 * @param bool   $add_hash      Whether a hash will be added as a suffix in the @id.
	 *
	 * @return array Schema ImageObject array.
	 */
	public function generate_from_attachment_id( $schema_id, $attachment_id, $caption = '', $add_hash = false ) {
		$data = $this->generate_object();
		$url  = $this->image->get_attachment_image_url( $attachment_id, 'full' );

		$id_suffix = ( $add_hash ) ? \md5( $url ) : '';

		$data['@id']        = $schema_id . $id_suffix;
		$data['url']        = $url;
		$data['contentUrl'] = $url;
		$data               = $this->add_image_size( $data, $attachment_id );
		$data               = $this->add_caption( $data, $attachment_id, $caption );

		return $data;
	}

	/**
	 * Retrieve data about an image from the database and use it to generate a Schema object.
	 *
	 * @param string $schema_id       The `@id` to use for the returned image.
	 * @param array  $attachment_meta The attachment metadata.
	 * @param string $caption         The caption string, if there is one.
	 * @param bool   $add_hash        Whether a hash will be added as a suffix in the @id.
	 *
	 * @return array Schema ImageObject array.
	 */
	public function generate_from_attachment_meta( $schema_id, $attachment_meta, $caption = '', $add_hash = false ) {
		$data = $this->generate_object();

		$id_suffix = ( $add_hash ) ? \md5( $attachment_meta['url'] ) : '';

		$data['@id']        = $schema_id . $id_suffix;
		$data['url']        = $attachment_meta['url'];
		$data['contentUrl'] = $data['url'];
		$data['width']      = $attachment_meta['width'];
		$data['height']     = $attachment_meta['height'];
		if ( ! empty( $caption ) ) {
			$data['caption'] = $this->html->smart_strip_tags( $caption );
		}

		return $data;
	}

	/**
	 * If we can't find $url in our database, we output a simple ImageObject.
	 *
	 * @param string $schema_id The `@id` to use for the returned image.
	 * @param string $url       The image URL.
	 * @param string $caption   A caption, if set.
	 * @param bool   $add_hash  Whether a hash will be added as a suffix in the @id.
	 *
	 * @return array Schema ImageObject array.
	 */
	public function simple_image_object( $schema_id, $url, $caption = '', $add_hash = false ) {
		$data = $this->generate_object();

		$id_suffix = ( $add_hash ) ? \md5( $url ) : '';

		$data['@id']        = $schema_id . $id_suffix;
		$data['url']        = $url;
		$data['contentUrl'] = $url;

		if ( ! empty( $caption ) ) {
			$data['caption'] = $this->html->smart_strip_tags( $caption );
		}

		return $data;
	}

	/**
	 * Retrieves an image's caption if set, or uses the alt tag if that's set.
	 *
	 * @param array  $data          An ImageObject Schema array.
	 * @param int    $attachment_id Attachment ID.
	 * @param string $caption       The caption string, if there is one.
	 *
	 * @return array An imageObject with width and height set if available.
	 */
	private function add_caption( $data, $attachment_id, $caption = '' ) {
		if ( $caption !== '' ) {
			$data['caption'] = $caption;

			return $data;
		}

		$caption = $this->image->get_caption( $attachment_id );
		if ( ! empty( $caption ) ) {
			$data['caption'] = $this->html->smart_strip_tags( $caption );

			return $data;
		}

		return $data;
	}

	/**
	 * Generates our bare bone ImageObject.
	 *
	 * @return array an empty ImageObject
	 */
	private function generate_object() {
		$data = [
			'@type' => 'ImageObject',
		];

		$data = $this->language->add_piece_language( $data );

		return $data;
	}

	/**
	 * Adds image's width and height.
	 *
	 * @param array $data          An ImageObject Schema array.
	 * @param int   $attachment_id Attachment ID.
	 *
	 * @return array An imageObject with width and height set if available.
	 */
	private function add_image_size( $data, $attachment_id ) {
		$image_meta = $this->image->get_metadata( $attachment_id );
		if ( empty( $image_meta['width'] ) || empty( $image_meta['height'] ) ) {
			return $data;
		}
		$data['width']  = $image_meta['width'];
		$data['height'] = $image_meta['height'];

		return $data;
	}
}
schema/id-helper.php000066600000001263151117651630010402 0ustar00<?php

namespace Yoast\WP\SEO\Helpers\Schema;

use Yoast\WP\SEO\Config\Schema_IDs;
use Yoast\WP\SEO\Context\Meta_Tags_Context;

/**
 * Schema utility functions.
 */
class ID_Helper {

	/**
	 * Retrieve a users Schema ID.
	 *
	 * @param int               $user_id The ID of the User you need a Schema ID for.
	 * @param Meta_Tags_Context $context A value object with context variables.
	 *
	 * @return string The user's schema ID.
	 */
	public function get_user_schema_id( $user_id, $context ) {
		$user = \get_userdata( $user_id );
		if ( \is_a( $user, 'WP_User' ) ) {
			return $context->site_url . Schema_IDs::PERSON_HASH . \wp_hash( $user->user_login . $user_id );
		}

		return '';
	}
}
schema/replace-vars-helper.php000066600000006736151117651630012404 0ustar00<?php

namespace Yoast\WP\SEO\Helpers\Schema;

use Closure;
use WPSEO_Replace_Vars;
use Yoast\WP\SEO\Conditionals\No_Conditionals;
use Yoast\WP\SEO\Config\Schema_IDs;
use Yoast\WP\SEO\Context\Meta_Tags_Context;
use Yoast\WP\SEO\Helpers\Date_Helper;
use Yoast\WP\SEO\Presentations\Indexable_Presentation;

/**
 * Registers the Schema replace variables and exposes a method to replace variables on a Schema graph.
 */
class Replace_Vars_Helper {

	use No_Conditionals;

	/**
	 * The replace vars.
	 *
	 * @var WPSEO_Replace_Vars
	 */
	protected $replace_vars;

	/**
	 * The Schema ID helper.
	 *
	 * @var ID_Helper
	 */
	protected $id_helper;

	/**
	 * The date helper.
	 *
	 * @var Date_Helper
	 */
	protected $date_helper;

	/**
	 * Replace_Vars_Helper constructor.
	 *
	 * @param WPSEO_Replace_Vars $replace_vars The replace vars.
	 * @param ID_Helper          $id_helper    The Schema ID helper.
	 * @param Date_Helper        $date_helper  The date helper.
	 */
	public function __construct(
		WPSEO_Replace_Vars $replace_vars,
		ID_Helper $id_helper,
		Date_Helper $date_helper
	) {
		$this->replace_vars = $replace_vars;
		$this->id_helper    = $id_helper;
		$this->date_helper  = $date_helper;
	}

	/**
	 * Replaces the variables.
	 *
	 * @param array                  $schema_data  The Schema data.
	 * @param Indexable_Presentation $presentation The indexable presentation.
	 *
	 * @return array The array with replaced vars.
	 */
	public function replace( array $schema_data, Indexable_Presentation $presentation ) {
		foreach ( $schema_data as $key => $value ) {
			if ( \is_array( $value ) ) {
				$schema_data[ $key ] = $this->replace( $value, $presentation );

				continue;
			}

			$schema_data[ $key ] = $this->replace_vars->replace( $value, $presentation->source );
		}

		return $schema_data;
	}

	/**
	 * Registers the Schema-related replace vars.
	 *
	 * @param Meta_Tags_Context $context The meta tags context.
	 *
	 * @return void
	 */
	public function register_replace_vars( $context ) {
		$replace_vars = [
			'main_schema_id'   => $context->main_schema_id,
			'author_id'        => $this->id_helper->get_user_schema_id( $context->indexable->author_id, $context ),
			'person_id'        => $context->site_url . Schema_IDs::PERSON_HASH,
			'primary_image_id' => $context->canonical . Schema_IDs::PRIMARY_IMAGE_HASH,
			'webpage_id'       => $context->main_schema_id,
			'website_id'       => $context->site_url . Schema_IDs::WEBSITE_HASH,
			'organization_id'  => $context->site_url . Schema_IDs::ORGANIZATION_HASH,
		];

		if ( $context->post ) {
			// Post does not always exist, e.g. on term pages.
			$replace_vars['post_date'] = $this->date_helper->format( $context->post->post_date, \DATE_ATOM );
		}

		foreach ( $replace_vars as $var => $value ) {
			$this->register_replacement( $var, $value );
		}
	}

	/**
	 * Registers a replace var and its value.
	 *
	 * @param string $variable The replace variable.
	 * @param string $value    The value that the variable should be replaced with.
	 */
	protected function register_replacement( $variable, $value ) {
		$this->replace_vars->safe_register_replacement(
			$variable,
			$this->get_identity_function( $value )
		);
	}

	/**
	 * Returns an anonymous function that in turn just returns the given value.
	 *
	 * @param mixed $value The value that the function should return.
	 *
	 * @return Closure A function that returns the given value.
	 */
	protected function get_identity_function( $value ) {
		return static function() use ( $value ) {
			return $value;
		};
	}
}
schema/article-helper.php000066600000002517151117651630011434 0ustar00<?php

namespace Yoast\WP\SEO\Helpers\Schema;

/**
 * Class Article_Helper.
 */
class Article_Helper {

	/**
	 * Determines whether a given post type should have Article schema.
	 *
	 * @param string|null $post_type Post type to check.
	 *
	 * @return bool True if it has Article schema, false if not.
	 */
	public function is_article_post_type( $post_type = null ) {
		if ( \is_null( $post_type ) ) {
			$post_type = \get_post_type();
		}

		/**
		 * Filter: 'wpseo_schema_article_post_types' - Allow changing for which post types we output Article schema.
		 *
		 * @deprecated 17.6 - Just enable support for authors for the desired post type.
		 *
		 * @api string[] $post_types The post types for which we output Article.
		 */
		\apply_filters_deprecated( 'wpseo_schema_article_post_types', [ [ 'post' ] ], 'WPSEO 17.6', '', 'Every post type supporting authors will automatically have the Article schema enabled.' );

		return $this->is_author_supported( $post_type );
	}

	/**
	 * Checks whether author is supported for the passed object sub type.
	 *
	 * @param string $object_sub_type The sub type of the object to check author support for.
	 *
	 * @return bool True if author is supported for the passed object sub type.
	 */
	public function is_author_supported( $object_sub_type ) {
		return \post_type_supports( $object_sub_type, 'author' );
	}
}
capability-helper.php000066600000004104151117651630010664 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * A helper object for user capabilities.
 */
class Capability_Helper {

	/**
	 * Checks if the user has at least one of the proper capabilities.
	 *
	 * @param string $capability Capability to check.
	 *
	 * @return bool True if the user has at least one of the proper rights.
	 */
	public function current_user_can( $capability ) {
		if ( $capability === 'wpseo_manage_options' ) {
			return \current_user_can( $capability );
		}

		return $this->has_any( [ 'wpseo_manage_options', $capability ] );
	}

	/**
	 * Retrieves the users that have the specified capability.
	 *
	 * @param string $capability The name of the capability.
	 *
	 * @return array The users that have the capability.
	 */
	public function get_applicable_users( $capability ) {
		$applicable_roles = $this->get_applicable_roles( $capability );

		if ( $applicable_roles === [] ) {
			return [];
		}

		return \get_users( [ 'role__in' => $applicable_roles ] );
	}

	/**
	 * Retrieves the roles that have the specified capability.
	 *
	 * @param string $capability The name of the capability.
	 *
	 * @return array The names of the roles that have the capability.
	 */
	public function get_applicable_roles( $capability ) {
		$roles      = \wp_roles();
		$role_names = $roles->get_names();

		$applicable_roles = [];
		foreach ( \array_keys( $role_names ) as $role_name ) {
			$role = $roles->get_role( $role_name );

			if ( ! $role ) {
				continue;
			}

			// Add role if it has the capability.
			if ( \array_key_exists( $capability, $role->capabilities ) && $role->capabilities[ $capability ] === true ) {
				$applicable_roles[] = $role_name;
			}
		}

		return $applicable_roles;
	}

	/**
	 * Checks if the current user has at least one of the supplied capabilities.
	 *
	 * @param array $capabilities Capabilities to check against.
	 *
	 * @return bool True if the user has at least one capability.
	 */
	private function has_any( array $capabilities ) {
		foreach ( $capabilities as $capability ) {
			if ( \current_user_can( $capability ) ) {
				return true;
			}
		}

		return false;
	}
}
curl-helper.php000066600000001217151117651630007512 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

/**
 * Helper class for getting information about the installed cURL version.
 */
class Curl_Helper {

	/**
	 * Checks is cURL is installed.
	 *
	 * @return bool Returns true if cURL is installed.
	 */
	public function is_installed() {
		return \function_exists( 'curl_version' );
	}

	/**
	 * Returns the currently installed cURL version.
	 *
	 * @return string|null Returns a string containing the cURL version, or null if cURL is not installed.
	 */
	public function get_version() {
		$version = \curl_version();

		if ( ! isset( $version['version'] ) ) {
			return null;
		}

		return $version['version'];
	}
}
language-helper.php000066600000004626151117651630010337 0ustar00<?php

namespace Yoast\WP\SEO\Helpers;

use WPSEO_Language_Utils;
use Yoast\WP\SEO\Config\Researcher_Languages;

/**
 * A helper object for language features.
 */
class Language_Helper {

	/**
	 * The languages with inclusive language analysis support.
	 *
	 * @var string[]
	 */
	public static $languages_with_inclusive_language_support = [ 'en' ];

	/**
	 * Checks whether word form recognition is active for the used language.
	 *
	 * @param string $language The used language.
	 *
	 * @return bool Whether word form recognition is active for the used language.
	 */
	public function is_word_form_recognition_active( $language ) {
		$supported_languages = [ 'de', 'en', 'es', 'fr', 'it', 'nl', 'ru', 'id', 'pt', 'pl', 'ar', 'sv', 'he', 'hu', 'nb', 'tr', 'cs', 'sk', 'el', 'ja' ];

		return \in_array( $language, $supported_languages, true );
	}

	/**
	 * Checks whether the given language has function word support.
	 * (E.g. function words are used or filtered out for this language when doing some SEO and readability assessments).
	 *
	 * @param string $language The language to check.
	 *
	 * @return bool Whether the language has function word support.
	 */
	public function has_function_word_support( $language ) {
		$supported_languages = [ 'en', 'de', 'nl', 'fr', 'es', 'it', 'pt', 'ru', 'pl', 'sv', 'id', 'he', 'ar', 'hu', 'nb', 'tr', 'cs', 'sk', 'fa', 'el', 'ja' ];

		return \in_array( $language, $supported_languages, true );
	}

	/**
	 * Checks whether the given language has inclusive language support.
	 *
	 * @param string $language The language to check if inclusive language is supported.
	 *
	 * @return bool Whether the language has inclusive language support.
	 */
	public function has_inclusive_language_support( $language ) {
		return \in_array( $language, self::$languages_with_inclusive_language_support, true );
	}

	/**
	 * Checks whether we have a specific researcher for the current locale and returns that language.
	 * If there is no researcher for the current locale, returns default as the researcher.
	 *
	 * @return string The language to use to select a researcher.
	 */
	public function get_researcher_language() {
		$researcher_language = WPSEO_Language_Utils::get_language( \get_locale() );
		$supported_languages = Researcher_Languages::SUPPORTED_LANGUAGES;

		if ( ! \in_array( $researcher_language, $supported_languages, true ) ) {
			$researcher_language = 'default';
		}
		return $researcher_language;
	}
}
class-orbit-fox-render-helper.php000066600000030227151132640170013033 0ustar00<?php
/**
 * The Helper Class for content rendering.
 *
 * @link       https://themeisle.com
 * @since      1.0.0
 *
 * @package    Orbit_Fox
 * @subpackage Orbit_Fox/app/helpers
 */

/**
 * The class that contains utility methods to render partials, views or elements.
 *
 * @package    Orbit_Fox
 * @subpackage Orbit_Fox/app/helpers
 * @author     Themeisle <friends@themeisle.com>
 */
class Orbit_Fox_Render_Helper {

	/**
	 * Get a partial template and return the output.
	 *
	 * @since   1.0.0
	 * @access  public
	 *
	 * @param   string $name The name of the partial w/o '-tpl.php'.
	 * @param   array  $args Optional. An associative array with name and value to be
	 *                                  passed to the partial.
	 *
	 * @return string
	 */
	public function get_partial( $name = '', $args = array() ) {
		ob_start();
		$file = OBX_PATH . '/core/app/views/partials/' . $name . '-tpl.php';
		if ( ! empty( $args ) ) {
			foreach ( $args as $obfx_rh_name => $obfx_rh_value ) {
				$$obfx_rh_name = $obfx_rh_value;
			}
		}
		if ( file_exists( $file ) ) {
			include $file;
		}

		return ob_get_clean();
	}

	/**
	 * Get a view template and return the output.
	 *
	 * @since   1.0.0
	 * @access  public
	 *
	 * @param   string $name The name of the partial w/o '-page.php'.
	 * @param   array  $args Optional. An associative array with name and value to be
	 *                                  passed to the view.
	 *
	 * @return string
	 */
	public function get_view( $name = '', $args = array() ) {
		ob_start();
		$file = OBX_PATH . '/core/app/views/' . $name . '-page.php';
		if ( ! empty( $args ) ) {
			foreach ( $args as $obfx_rh_name => $obfx_rh_value ) {
				$$obfx_rh_name = $obfx_rh_value;
			}
		}
		if ( file_exists( $file ) ) {
			include $file;
		}

		return ob_get_clean();
	}

	/**
	 * Method to render option to a field.
	 *
	 * @since   1.0.0
	 * @access  public
	 *
	 * @param   array $option The option from the module..
	 *
	 * @return mixed
	 */
	public function render_option( $option = array() ) {

		$option = $this->sanitize_option( $option );
		switch ( $option['type'] ) {
			case 'text':
				return $this->field_text( $option );
				break;
			case 'email':
				return $this->field_text( $option, true );
				break;
			case 'textarea':
				return $this->field_textarea( $option );
				break;
			case 'select':
				return $this->field_select( $option );
				break;
			case 'radio':
				return $this->field_radio( $option );
				break;
			case 'checkbox':
				return $this->field_checkbox( $option );
				break;
			case 'toggle':
				return $this->field_toggle( $option );
				break;
			case 'title':
				return $this->field_title( $option );
				break;
			case 'custom':
				return apply_filters( 'obfx_custom_control_' . $option['id'], '' );
				break;
			case 'link':
				return $this->field_link( $option );
				break;
			case 'password':
				return $this->field_password( $option );
				break;
			default:
				return __( 'No option found for provided type', 'themeisle-companion' );
				break;
		}

	}

	/**
	 * Merges specific defaults with general ones.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The specific defaults array.
	 *
	 * @return array
	 */
	private function sanitize_option( $option ) {
		$general_defaults = array(
			'id'          => null,
			'class'       => null,
			'name'        => null,
			'label'       => 'Module Text Label',
			'title'       => false,
			'description' => false,
			'type'        => null,
			'value'       => '',
			'default'     => '',
			'placeholder' => 'Add some text',
			'disabled'    => false,
			'options'     => array(),
		);

		return wp_parse_args( $option, $general_defaults );
	}

	/**
	 * Render an input text field.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 * @param   bool  $is_email Render an email input instead of text.
	 *
	 * @return mixed
	 */
	private function field_text( $option = array(), $is_email = false ) {
		$input_type = 'text';
		if ( $is_email === true ) {
			$input_type = 'email';
		}

		$field_value = $this->set_field_value( $option );
		$field       = '<input class="form-input ' . $option['class'] . '" type="' . esc_attr( $input_type ) . '" id="' . $option['id'] . '" name="' . $option['name'] . '" placeholder="' . $option['placeholder'] . '" value="' . $field_value . '">';
		$field       = $this->wrap_element( $option, $field );

		return $field;
	}

	/**
	 * Method to set field value.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 *
	 * @return mixed
	 */
	private function set_field_value( $option = array() ) {
		$field_value = $option['default'];
		if ( isset( $option['value'] ) && $option['value'] !== '' ) {
			$field_value = $option['value'];
		}

		return $field_value;
	}

	/**
	 * Utility method to wrap an element with proper blocks.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array  $option The option array.
	 * @param   string $element The element we want to wrap.
	 *
	 * @return string
	 */
	private function wrap_element( $option, $element ) {
		$title       = $this->get_title( $option['id'], $option['title'] );
		$description = $this->get_description( $option['description'] );

		$before_wrap = '';
		if ( isset( $option['before_wrap'] ) ) {
			$before_wrap = wp_kses_post( $option['before_wrap'] ); // @codeCoverageIgnore
		}

		$after_wrap = '';
		if ( isset( $option['after_wrap'] ) ) {
			$after_wrap = wp_kses_post( $option['after_wrap'] ); // @codeCoverageIgnore
		}

		return '
		' . $before_wrap . '
		<div class="form-group ' . $option['class'] . '">
			' . $title . '
			' . $element . '
			' . $description . '
		</div>
		' . $after_wrap . '
		';
	}

	/**
	 * Method to return a title for element if needed.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   string $element_id The option id field.
	 * @param   string $title The option title field.
	 *
	 * @return string
	 */
	private function get_title( $element_id, $title ) {
		$display_title = '';
		if ( $title ) {
			$display_title = '<label class="form-label" for="' . $element_id . '">' . $title . '</label>';
		}

		return $display_title;
	}

	/**
	 * Method to return a description for element if needed.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   string $description The option description field.
	 *
	 * @return string
	 */
	private function get_description( $description ) {
		$display_description = '';
		if ( $description ) {
			$display_description = '<p><small>' . $description . '</small></p>';
		}

		return $display_description;
	}

	/**
	 * Render a textarea field.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 *
	 * @return mixed
	 */
	private function field_textarea( $option = array() ) {
		$field_value = $this->set_field_value( $option );
		$field       = '<textarea class="form-input ' . $option['class'] . '" id="' . $option['id'] . '" name="' . $option['name'] . '" placeholder="' . $option['placeholder'] . '" rows="3">' . $field_value . '</textarea>';
		$field       = $this->wrap_element( $option, $field );

		return $field;
	}

	/**
	 * Render a select field.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 *
	 * @return mixed
	 */
	private function field_select( $option = array() ) {
		$field_value    = $this->set_field_value( $option );
		$select_options = '';
		foreach ( $option['options'] as $value => $label ) {
			$is_selected = '';
			// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
			if ( $field_value == $value ) {
				$is_selected = 'selected';
			}
			$select_options .= '<option value="' . $value . '" ' . $is_selected . '>' . $label . '</option>';
		}
		$field = '
			<select class="form-select ' . $option['class'] . '" id="' . $option['id'] . '" name="' . $option['name'] . '" placeholder="' . $option['placeholder'] . '">
				' . $select_options . '
			</select>';
		$field = $this->wrap_element( $option, $field );

		return $field;
	}

	/**
	 * Render a radio field.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 *
	 * @return mixed
	 */
	private function field_radio( $option = array() ) {
		$field_value    = $this->set_field_value( $option );
		$select_options = '';
		foreach ( $option['options'] as $value => $label ) {
			$checked = '';
			if ( $value === $field_value ) {
				$checked = 'checked';
			}
			$select_options .= $this->generate_check_type( 'radio', $value, $checked, $label, $option );
		}
		$field = $this->wrap_element( $option, $select_options );

		return $field;
	}

	/**
	 * DRY method to generate checkbox or radio field types
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   string $type The field type ( checkbox | radio ).
	 * @param   string $field_value The field value.
	 * @param   string $checked The checked flag.
	 * @param   string $label The option label.
	 * @param   array  $option The option from the module.
	 *
	 * @return string
	 */
	private function generate_check_type( $type = 'radio', $field_value, $checked, $label, $option = array() ) {
		return '
		<label class="form-' . $type . ' ' . $option['class'] . '">
			<input type="' . $type . '" name="' . $option['name'] . '" value="' . $field_value . '" ' . $checked . ' />
			<i class="form-icon"></i> ' . $label . '
		</label>
		';
	}

	/**
	 * Render a checkbox field.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 *
	 * @return mixed
	 */
	private function field_checkbox( $option = array() ) {
		$field_value = $this->set_field_value( $option );
		$checked     = '';
		if ( $field_value ) {
			$checked = 'checked';
		}
		$select_options = $this->generate_check_type( 'checkbox', 1, $checked, $option['label'], $option );
		$field          = $this->wrap_element( $option, $select_options );

		return $field;
	}

	/**
	 * Render a toggle field.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 *
	 * @return mixed
	 */
	private function field_toggle( $option = array() ) {
		$field_value = $this->set_field_value( $option );
		$checked     = '';
		if ( $field_value ) {
			$checked = 'checked';
		}
		$field = '
			<label class="form-switch ' . $option['class'] . '">
				<input type="checkbox" name="' . $option['name'] . '" value="1" ' . $checked . ' />
				<i class="form-icon"></i> ' . $option['label'] . '
			</label>';
		$field = $this->wrap_element( $option, $field );

		return $field;
	}

	/**
	 * Render a title field.
	 *
	 * @since   2.5.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 *
	 * @return mixed
	 */
	private function field_title( $option = array() ) {

		$field = $this->wrap_element( $option, '' );

		return $field;
	}

	/**
	 * Render a toggle field.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 *
	 * @return mixed
	 */
	private function field_link( $option = array() ) {
		if ( ! isset( $option['link-id'] ) ) {
			$option['link-id'] = $option['id'];
		}
		if ( ! isset( $option['target'] ) ) {
			$option['target'] = '';
		}
		$field = '
			<a id="' . esc_attr( $option['link-id'] ) . '" target="' . esc_attr( $option['target'] ) . '" class="' . esc_attr( isset( $option['link-class'] ) ? $option['link-class'] : '' ) . '" href="' . esc_url( $option['url'] ) . '">' .
				 wp_kses_post( $option['text'] )
				 . '</a>';

		$field = $this->wrap_element( $option, $field );

		return $field;
	}

	/**
	 * Render an input password field.
	 *
	 * @since   1.0.0
	 * @access  private
	 *
	 * @param   array $option The option from the module.
	 * @param   bool  $is_email Render an email input instead of text.
	 *
	 * @return mixed
	 */
	private function field_password( $option = array(), $is_email = false ) {
		$input_type = 'password';

		$field_value = $this->set_field_value( $option );
		$field       = '<input class="form-input ' . $option['class'] . '" type="' . esc_attr( $input_type ) . '" id="' . $option['id'] . '" name="' . $option['name'] . '" placeholder="' . $option['placeholder'] . '" value="' . $field_value . '">';
		$field       = $this->wrap_element( $option, $field );

		return $field;
	}


}
sanitize-functions.php000066600000012730151132703330011116 0ustar00<?php
/**
 * Sanitize functions.
 *
 * @package Hestia
 */

/**
 * Sanitize alignment control.
 *
 * @since 1.1.34
 *
 * @param string $value Control output.
 *
 * @return string
 */
function hestia_sanitize_alignment_options( $value ) {
	$valid_values = array(
		'video',
		'parallax',
		'left',
		'center',
		'right',
		'true',
		'false',
		'slider',
		'extra',
	);

	if ( ! in_array( $value, $valid_values ) ) {
		wp_die( 'Invalid value, go back and try again.' );
	}

	return $value;
}

/**
 * Sanitize Footer Layout control.
 *
 * @since 1.1.59
 *
 * @param string $value Control output.
 *
 * @return string
 */
function hestia_sanitize_footer_layout_control( $value ) {
	$value        = sanitize_text_field( $value );
	$valid_values = array(
		'white_footer',
		'black_footer',
	);

	if ( ! in_array( $value, $valid_values ) ) {
		wp_die( 'Invalid value, go back and try again.' );
	}

	return $value;
}

/**
 * Sanitize Blog Layout control.
 *
 * @since 1.1.59
 *
 * @param string $value Control output.
 *
 * @return string
 */
function hestia_sanitize_blog_layout_control( $value ) {
	$value        = sanitize_text_field( $value );
	$valid_values = array(
		'blog_alternative_layout',
		'blog_normal_layout',
	);

	if ( ! in_array( $value, $valid_values ) ) {
		wp_die( 'Invalid value, go back and try again.' );
	}

	return $value;
}

/**
 * Sanitize arrays.
 *
 * @since 1.1.40
 *
 * @param mixed $value Control output.
 *
 * @return array
 */
function hestia_sanitize_array( $value ) {
	$output = $value;

	if ( ! is_array( $value ) ) {
		$output = explode( ',', $value );
	}

	if ( ! empty( $output ) ) {
		return array_map( 'sanitize_text_field', $output );
	}

	return array();
}

/**
 * Function to sanitize alpha color.
 *
 * @param string $value Hex or RGBA color.
 *
 * @return string
 */
function hestia_sanitize_colors( $value ) {
	// Is this an rgba color or a hex?
	$mode = ( false === strpos( $value, 'rgba' ) ) ? 'hex' : 'rgba';

	if ( 'rgba' === $mode ) {
		return hestia_sanitize_rgba( $value );
	} else {
		return sanitize_hex_color( $value );
	}
}

/**
 * Sanitize big title type
 */
function hestia_sanitize_big_title_type( $input ) {
	$options = array( 'image', 'parallax', 'video' );
	if ( in_array( $input, $options ) ) {
		return $input;
	}
	return 'image';
}

/**
 * Sanitize rgba color.
 *
 * @param string $value Color in rgba format.
 *
 * @return string
 */
function hestia_sanitize_rgba( $value ) {
	$red   = 'rgba(0,0,0,0)';
	$green = 'rgba(0,0,0,0)';
	$blue  = 'rgba(0,0,0,0)';
	$alpha = 'rgba(0,0,0,0)';   // If empty or an array return transparent
	if ( empty( $value ) || is_array( $value ) ) {
		return '';
	}

	// By now we know the string is formatted as an rgba color so we need to further sanitize it.
	$value = str_replace( ' ', '', $value );
	sscanf( $value, 'rgba(%d,%d,%d,%f)', $red, $green, $blue, $alpha );

	return 'rgba(' . $red . ',' . $green . ',' . $blue . ',' . $alpha . ')';
}

/**
 * Sanitize repeater control.
 *
 * @param object $value Control output.
 *
 * @return object
 */
function hestia_repeater_sanitize( $value ) {
	$value_decoded = json_decode( $value, true );

	if ( ! empty( $value_decoded ) ) {
		foreach ( $value_decoded as $boxk => $box ) {
			foreach ( $box as $key => $value ) {

				$value_decoded[ $boxk ][ $key ] = wp_kses_post( force_balance_tags( $value ) );

			}
		}

		return json_encode( $value_decoded );
	}

	return $value;
}

/**
 * Allowed HTML tags for text controls
 *
 * @param string $value the string to be sanitized.
 *
 * @return string
 */
function hestia_sanitize_string( $value ) {

	$allowed_html = apply_filters(
		'hestia_sanitize_html_tags', array(
			'a'      => array(
				'href'  => array(),
				'title' => array(),
				'class' => array(),
			),
			'br'     => array(),
			'em'     => array(),
			'strong' => array(),
			'i'      => array(
				'class' => array(),
			),
			'b'      => array(),
			'p'      => array(),
		)
	);

	$value = force_balance_tags( $value );

	return wp_kses( $value, $allowed_html );
}

/**
 * Sanitize checkbox output.
 *
 * @param bool $value value to be sanitized.
 *
 * @return string
 * @since Hestia 1.0
 */
function hestia_sanitize_checkbox( $value ) {
	return isset( $value ) && true === (bool) $value;
}

/**
 * Sanitize multi select output.
 *
 * @param string $value value to be sanitized.
 *
 * @return array
 * @since Hestia 1.0
 */
function hestia_sanitize_multiselect( $value ) {
	if ( ! is_array( $value ) ) {
		$value = explode( ',', $value );
	}

	return ! empty( $value ) ? array_map( 'sanitize_text_field', $value ) : array();
}

/**
 * Check if a string is in json format
 *
 * @param  string $string Input.
 *
 * @since 1.1.38
 * @return bool
 */
function hestia_is_json( $string ) {
	return is_string( $string ) && is_array( json_decode( $string, true ) ) ? true : false;
}

/**
 * Sanitize values for range inputs.
 *
 * @param string $input Control input.
 *
 * @since 1.1.38
 * @return float
 */
function hestia_sanitize_range_value( $input ) {
	if ( ! hestia_is_json( $input ) ) {
		return floatval( $input );
	}
	$range_value            = json_decode( $input, true );
	$range_value['desktop'] = ! empty( $range_value['desktop'] ) || $range_value['desktop'] === '0' ? floatval( $range_value['desktop'] ) : '';
	$range_value['tablet']  = ! empty( $range_value['tablet'] ) || $range_value['tablet'] === '0' ? floatval( $range_value['tablet'] ) : '';
	$range_value['mobile']  = ! empty( $range_value['mobile'] ) || $range_value['mobile'] === '0' ? floatval( $range_value['mobile'] ) : '';

	return json_encode( $range_value );
}
layout-functions.php000066600000044131151132703330010605 0ustar00<?php
/**
 * Layout functions needed in global scope.
 *
 * @package Hestia
 */

if ( ! function_exists( 'hestia_wp_link_pages' ) ) {
	/**
	 * Display a custom wp_link_pages for singular view.
	 *
	 * @param array $args arguments.
	 *
	 * @since Hestia 1.0
	 * @return string
	 */
	function hestia_wp_link_pages( $args = array() ) {
		$defaults = array(
			'before'           => '<ul class="nav pagination pagination-primary">',
			'after'            => '</ul>',
			'link_before'      => '',
			'link_after'       => '',
			'next_or_number'   => 'number',
			'nextpagelink'     => esc_html__( 'Next page', 'hestia' ),
			'previouspagelink' => esc_html__( 'Previous page', 'hestia' ),
			'pagelink'         => '%',
			'echo'             => 1,
		);

		$r = wp_parse_args( $args, $defaults );
		$r = apply_filters( 'wp_link_pages_args', $r );

		global $page, $numpages, $multipage, $more, $pagenow;

		$output = '';
		if ( $multipage ) {
			if ( 'number' == $r['next_or_number'] ) {
				$output .= $r['before'];
				for ( $i = 1; $i < ( $numpages + 1 ); $i = $i + 1 ) {
					$j       = str_replace( '%', $i, $r['pagelink'] );
					$output .= ' ';
					$output .= $r['link_before'];
					if ( $i != $page || ( ( ! $more ) && ( $page == 1 ) ) ) {
						$output .= _wp_link_page( $i );
					} else {
						$output .= '<span class="page-numbers current">';
					}
					$output .= $j;
					if ( $i != $page || ( ( ! $more ) && ( $page == 1 ) ) ) {
						$output .= '</a>';
					} else {
						$output .= '</span>';
					}
					$output .= $r['link_after'];
				}
				$output .= $r['after'];
			} else {
				if ( $more ) {
					$output .= $r['before'];
					$i       = $page - 1;
					if ( $i && $more ) {
						$output .= _wp_link_page( $i );
						$output .= $r['link_before'] . $r['previouspagelink'] . $r['link_after'] . '</a>';
					}
					$i = $page + 1;
					if ( $i <= $numpages && $more ) {
						$output .= _wp_link_page( $i );
						$output .= $r['link_before'] . $r['nextpagelink'] . $r['link_after'] . '</a>';
					}
					$output .= $r['after'];
				}
			}// End if().
		}// End if().

		if ( $r['echo'] ) {
			echo wp_kses(
				$output, array(
					'div'  => array(
						'class' => array(),
						'id'    => array(),
					),
					'ul'   => array(
						'class' => array(),
					),
					'a'    => array(
						'href' => array(),
					),
					'li'   => array(),
					'span' => array(
						'class' => array(),
					),
				)
			);
		}

		return $output;
	}
}

if ( ! function_exists( 'hestia_comments_template' ) ) {
	/**
	 * Custom list of comments for the theme.
	 *
	 * @since Hestia 1.0
	 */
	function hestia_comments_template() {
		if ( is_user_logged_in() ) {
			$current_user = get_avatar( wp_get_current_user(), 64 );
		} else {
			$current_user = '<img src="' . get_template_directory_uri() . '/assets/img/placeholder.jpg" height="64" width="64"/>';
		}

		$args = array(
			'class_form'         => 'form media-body',
			'class_submit'       => 'btn btn-primary pull-right',
			'title_reply_before' => '<h3 class="hestia-title text-center">',
			'title_reply_after'  => '</h3> <span class="pull-left author"> <div class="avatar">' . $current_user . '</div> </span>',
			'must_log_in'        => '<p class="must-log-in">' .
									sprintf(
										wp_kses(
											/* translators: %s is Link to login */
											__( 'You must be <a href="%s">logged in</a> to post a comment.', 'hestia' ), array(
												'a' => array(
													'href' => array(),
												),
											)
										), esc_url( wp_login_url( apply_filters( 'the_permalink', get_permalink() ) ) )
									) . '</p>',
			'comment_field'      => '<div class="form-group label-floating is-empty"> <label class="control-label">' . esc_html__( 'What\'s on your mind?', 'hestia' ) . '</label><textarea id="comment" name="comment" class="form-control" rows="6" aria-required="true"></textarea><span class="hestia-input"></span> </div>',
		);

		return $args;
	}
}

if ( ! function_exists( 'hestia_comments_list' ) ) {
	/**
	 * Custom list of comments for the theme.
	 *
	 * @since Hestia 1.0
	 *
	 * @param string  $comment comment.
	 * @param array   $args    arguments.
	 * @param integer $depth   depth.
	 */
	function hestia_comments_list( $comment, $args, $depth ) {
		?>
		<div <?php comment_class( empty( $args['has_children'] ) ? 'media' : 'parent media' ); ?>
				id="comment-<?php comment_ID(); ?>">
			<?php if ( $args['type'] != 'pings' ) : ?>
				<a class="pull-left" href="<?php echo esc_url( get_comment_author_url( $comment ) ); ?> ">
					<div class="comment-author avatar vcard">
						<?php
						if ( $args['avatar_size'] != 0 ) {
							echo get_avatar( $comment, 64 );
						}
						?>
					</div>
				</a>
			<?php endif; ?>
			<div class="media-body">
				<h4 class="media-heading">
					<?php echo get_comment_author_link(); ?>
					<small>
						<?php
						printf(
							/* translators: %1$s is Date, %2$s is Time */
							esc_html__( '&#183; %1$s at %2$s', 'hestia' ),
							get_comment_date(),
							get_comment_time()
						);
						edit_comment_link( esc_html__( '(Edit)', 'hestia' ), '  ', '' );
						?>
					</small>
				</h4>
				<?php comment_text(); ?>
				<div class="media-footer">
					<?php
					echo get_comment_reply_link(
						array(
							'depth'      => $depth,
							'max_depth'  => $args['max_depth'],
							'reply_text' => sprintf( '<i class="fa fa-mail-reply"></i> %s', esc_html__( 'Reply', 'hestia' ) ),
						),
						$comment->comment_ID,
						$comment->comment_post_ID
					);
					?>
				</div>
			</div>
		</div>
		<?php
	}
}

if ( ! function_exists( 'hestia_single_pagination' ) ) {
	/**
	 * Display pagination on single page and single portfolio.
	 */
	function hestia_single_pagination() {
		?>
		<div class="section section-blog-info">
			<div class="row">
				<div class="col-md-8 col-md-offset-2">
					<div class="row">
						<div class="col-md-12">
							<?php
							hestia_wp_link_pages(
								array(
									'before'      => '<div class="text-center"> <ul class="nav pagination pagination-primary">',
									'after'       => '</ul> </div>',
									'link_before' => '<li>',
									'link_after'  => '</li>',
								)
							);
							?>
						</div>
					</div>
				</div>
			</div>
		</div>
		<?php
	}
}

if ( ! function_exists( 'hestia_get_image_sizes' ) ) {
	/**
	 * Output image sizes for attachment single page.
	 *
	 * @since Hestia 1.0
	 */
	function hestia_get_image_sizes() {

		/* If not viewing an image attachment page, return. */
		if ( ! wp_attachment_is_image( get_the_ID() ) ) {
			return '';
		}

		/* Set up an empty array for the links. */
		$links = array();

		/* Get the intermediate image sizes and add the full size to the array. */
		$sizes   = get_intermediate_image_sizes();
		$sizes[] = 'full';

		/* Loop through each of the image sizes. */
		foreach ( $sizes as $size ) {

			/* Get the image source, width, height, and whether it's intermediate. */
			$image = wp_get_attachment_image_src( get_the_ID(), $size );

			/* Add the link to the array if there's an image and if $is_intermediate (4th array value) is true or full size. */
			if ( ! empty( $image ) && ( ( ! empty( $image[3] ) && true === $image[3] ) || 'full' === $size ) ) {
				$links[] = '<a target="_blank" class="image-size-link" href="' . esc_url( $image[0] ) . '">' . $image[1] . ' &times; ' . $image[2] . '</a>';
			}
		}

		/* Join the links in a string and return. */

		return join( ' <span class="sep">|</span> ', $links );
	}
}

if ( ! function_exists( 'hestia_sidebar_placeholder' ) ) {
	/**
	 * Display sidebar placeholder.
	 *
	 * @param string $class_to_add Classes to add on container.
	 * @param string $sidebar_id   Id of the sidebar used as a class to differentiate hestia-widget-placeholder for blog and shop pages.
	 * @param string $classes      Classes to add to placeholder.
	 *
	 * @access public
	 * @since  1.1.24
	 */
	function hestia_sidebar_placeholder( $class_to_add, $sidebar_id, $classes = 'col-md-3 blog-sidebar-wrapper' ) {
		$content = apply_filters( 'hestia_sidebar_placeholder_content', esc_html__( 'This sidebar is active but empty. In order to use this layout, please add widgets in the sidebar', 'hestia' ) );
		?>
		<div class="<?php echo esc_attr( $classes ); ?>">
			<aside id="secondary" class="blog-sidebar <?php echo esc_attr( $class_to_add ); ?>" role="complementary">
				<div class="hestia-widget-placeholder
				<?php
				if ( ! empty( $sidebar_id ) ) {
					echo esc_attr( $sidebar_id );
				}
				?>
				">
					<?php
					the_widget( 'WP_Widget_Text', 'text=' . $content );
					?>
				</div>
			</aside><!-- .sidebar .widget-area -->
		</div>
		<?php
	}
}

if ( ! function_exists( 'hestia_display_customizer_shortcut' ) ) {
	/**
	 * This function display a shortcut to a customizer control.
	 *
	 * @param string $class_name        The name of control we want to link this shortcut with.
	 * @param bool   $is_section_toggle Tells function to display eye icon if it's true.
	 */
	function hestia_display_customizer_shortcut( $class_name, $is_section_toggle = false ) {
		if ( ! is_customize_preview() ) {
			return;
		}
		$icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
				<path d="M13.89 3.39l2.71 2.72c.46.46.42 1.24.03 1.64l-8.01 8.02-5.56 1.16 1.16-5.58s7.6-7.63 7.99-8.03c.39-.39 1.22-.39 1.68.07zm-2.73 2.79l-5.59 5.61 1.11 1.11 5.54-5.65zm-2.97 8.23l5.58-5.6-1.07-1.08-5.59 5.6z"></path>
			</svg>';
		if ( $is_section_toggle ) {
			$icon = '<i class="fa fa-eye"></i>';
		}
		echo
			'<span class="hestia-hide-section-shortcut customize-partial-edit-shortcut customize-partial-edit-shortcut-' . esc_attr( $class_name ) . '">
		<button class="customize-partial-edit-shortcut-button">
			' . $icon . '
		</button>
	</span>';
	}
}

if ( ! function_exists( 'hestia_no_content_get_header' ) ) {
	/**
	 * Header for page builder blank template
	 *
	 * @since  1.1.24
	 * @access public
	 */
	function hestia_no_content_get_header() {

		?>
		<!DOCTYPE html>
		<html <?php language_attributes(); ?> class="no-js">
		<head>
			<meta charset="<?php bloginfo( 'charset' ); ?>">
			<meta name="viewport" content="width=device-width, initial-scale=1">
			<link rel="profile" href="http://gmpg.org/xfn/11">
			<?php wp_head(); ?>
		</head>

		<body <?php body_class(); ?>>
		<?php
		do_action( 'hestia_page_builder_content_body_before' );
	}
}

if ( ! function_exists( 'hestia_no_content_get_footer' ) ) {
	/**
	 * Footer for page builder blank template
	 *
	 * @since  1.1.24
	 * @access public
	 */
	function hestia_no_content_get_footer() {
		do_action( 'hestia_page_builder_content_body_after' );
		wp_footer();
		?>
		</body>
		</html>
		<?php
	}
}

if ( ! function_exists( 'hestia_is_external_url' ) ) {
	/**
	 * Utility to check if URL is external
	 *
	 * @param string $url Url to check.
	 *
	 * @return string
	 */
	function hestia_is_external_url( $url ) {
		$link_url = parse_url( $url );
		$home_url = parse_url( home_url() );

		if ( ! empty( $link_url['host'] ) ) {
			if ( $link_url['host'] !== $home_url['host'] ) {
				return ' target="_blank"';
			}
		} else {
			return '';
		}
	}
}

if ( ! function_exists( 'hestia_hex_rgb' ) ) {
	/**
	 * HEX colors conversion to RGB.
	 *
	 * @param string $input Color in hex format.
	 *
	 * @return array|string RGB string.
	 * @since Hestia 1.0
	 */
	function hestia_hex_rgb( $input ) {

		$default = 'rgb(0,0,0)';

		// Return default if no color provided
		if ( empty( $input ) ) {
			return $default;
		}

		// Sanitize $color if "#" is provided
		if ( $input[0] == '#' ) {
			$input = substr( $input, 1 );
		}

		// Check if color has 6 or 3 characters and get values
		if ( strlen( $input ) == 6 ) {
			$hex = array( $input[0] . $input[1], $input[2] . $input[3], $input[4] . $input[5] );
		} elseif ( strlen( $input ) == 3 ) {
			$hex = array( $input[0] . $input[0], $input[1] . $input[1], $input[2] . $input[2] );
		} else {
			return $default;
		}

		// Convert hexadeciomal color to rgb(a)
		$rgb = array_map( 'hexdec', $hex );

		return $rgb;
	}
}

if ( ! function_exists( 'hestia_rgb_to_rgba' ) ) {
	/**
	 * Add opacity to rgb.
	 *
	 * @param array $rgb     RGB color.
	 * @param int   $opacity Opacity value.
	 *
	 * @return string
	 */
	function hestia_rgb_to_rgba( $rgb, $opacity ) {

		if ( ! is_array( $rgb ) ) {
			return '';
		}
		// Check for opacity
		if ( $opacity ) {
			if ( abs( $opacity ) > 1 ) {
				$opacity = 1.0;
			}
			$output = 'rgba(' . implode( ',', $rgb ) . ',' . $opacity . ')';
		} else {
			$output = 'rgb(' . implode( ',', $rgb ) . ')';
		}

		return esc_html( $output );
	}
}

if ( ! function_exists( 'hestia_hex_rgba' ) ) {
	/**
	 * HEX colors conversion to RGBA.
	 *
	 * @param array|string $input   RGB color.
	 * @param int          $opacity Opacity value.
	 *
	 * @return string
	 */
	function hestia_hex_rgba( $input, $opacity = 1 ) {
		$rgb = hestia_hex_rgb( $input );

		return hestia_rgb_to_rgba( $rgb, $opacity );
	}
}

if ( ! function_exists( 'hestia_add_animationation' ) ) {
	/**
	 * Add animation attribute for animate-on-scroll.
	 *
	 * @param string $animation_type the type of animation.
	 *
	 * @return string
	 */
	function hestia_add_animationation( $animation_type ) {
		if ( ! defined( 'HESTIA_PRO_FLAG' ) ) {
			return '';
		}
		$enable_animations = apply_filters( 'hestia_enable_animations', true );
		$output            = '';
		if ( $enable_animations && ! empty( $animation_type ) ) {
			$output .= ' data-aos="';
			$output .= $animation_type;
			$output .= '" ';
		}

		return $output;
	}
}

if ( ! function_exists( 'hestia_layout' ) ) {
	/**
	 * Returns class names used for the main page/post content div
	 * Based on the Boxed Layout and Header Layout customizer options
	 *
	 * @since    Hestia 1.0
	 * @modified 1.1.64
	 */
	function hestia_layout() {

		/**
		 * For the Page Builder Full Width template don't add any extra classes (except main)
		 */
		if ( is_page_template( 'page-templates/template-pagebuilder-full-width.php' ) ) {
			return 'main';
		}

		$layout_class = 'main ';

		$hestia_general_layout = get_theme_mod( 'hestia_general_layout', 1 );

		/**
		 * Add main-raised class when the Boxed Layout option is enabled
		 */
		if ( isset( $hestia_general_layout ) && $hestia_general_layout == 1 ) {
			$layout_class .= ' main-raised ';
		}

		/**
		 * For WooCommerce pages don't add any extra classes (except main or main-raised)
		 */
		if ( class_exists( 'WooCommerce' ) && ( is_product() || is_cart() || is_checkout() ) ) {
			return $layout_class;
		}

		$hestia_header_layout = get_theme_mod( 'hestia_header_layout', 'default' );

		/**
		 * For other internal posts/pages or static frontpage add extra clsses based on the header layout
		 * Possible cases: default, no-content or classic-blog
		 */
		$layout_class .= ! is_singular() || ( is_front_page() && ! is_page_template() ) ? '' : $hestia_header_layout;

		return $layout_class;
	}
}

if ( ! function_exists( 'hestia_limit_content' ) ) {
	/**
	 * Function that limits a text to $limit words, words that are separated by $separator
	 *
	 * @param array  $input     Content to limit.
	 * @param int    $limit     Max size.
	 * @param string $separator Separator.
	 * @param bool   $show_more Flag to decide if '...' should be added at the end of result.
	 *
	 * @return string
	 */
	function hestia_limit_content( $input, $limit, $separator = ',', $show_more = true ) {
		if ( $limit === 0 ) {
			return '';
		}
		$length = sizeof( $input );
		$more   = $length > $limit ? apply_filters( 'hestia_text_more', ' ...' ) : '';
		$result = '';
		$index  = 0;
		foreach ( $input as $word ) {
			if ( $index < $limit || $limit < 0 ) {
				$result .= $word;
				if ( $length > 1 && $index !== $length - 1 && $index !== $limit - 1 ) {
					$result .= $separator;
					if ( $separator === ',' ) {
						$result .= ' ';
					}
				}
			}
			$index ++;
		}
		if ( $show_more === true ) {
			$result .= $more;
		}

		return $result;
	}
}

if ( ! function_exists( 'hestia_edited_with_pagebuilder' ) ) {
	/**
	 * This function returns whether the theme use or not one of the following page builders:
	 * SiteOrigin, WP Bakery, Elementor, Divi Builder or Beaver Builder.
	 *
	 * @since 1.1.63
	 * @return bool
	 */
	function hestia_edited_with_pagebuilder() {
		$frontpage_id = get_option( 'page_on_front' );
		/**
		 * Exit with false if there is no page set as frontpage.
		 */
		if ( intval( $frontpage_id ) === 0 ) {
			return false;
		}
		/**
		 * Elementor, Beaver Builder, Divi and Siteorigin mark if the page was edited with its editors in post meta
		 * so we'll have to check if plugins exists and the page was edited with page builder.
		 */
		$post_meta            = ! empty( $frontpage_id ) ? get_post_meta( $frontpage_id ) : '';
		$page_builders_values = array(
			'elementor'  => ! empty( $post_meta['_elementor_edit_mode'] ) && $post_meta['_elementor_edit_mode'][0] === 'builder' && class_exists( 'Elementor\Plugin' ),
			'beaver'     => ! empty( $post_meta['_fl_builder_enabled'] ) && $post_meta['_fl_builder_enabled'][0] === '1' && class_exists( 'FLBuilder' ),
			'siteorigin' => ! empty( $post_meta['panels_data'] ) && class_exists( 'SiteOrigin_Panels' ),
			'divi'       => ! empty( $post_meta['_et_pb_use_builder'] ) && $post_meta['_et_pb_use_builder'][0] === 'on' && class_exists( 'ET_Builder_Plugin' ),
		);
		/**
		 * WP Bakery (former Visual Composer) doesn't store a flag in meta data to say whether or not the page
		 * is edited with it so we have to check post content if it contains shortcodes from plugin.
		 */
		$post_content = get_post_field( 'post_content', $frontpage_id );
		if ( ! empty( $post_content ) ) {
			$page_builders_values['wpbakery'] = class_exists( 'Vc_Manager' ) && strpos( $post_content, '[vc_' ) !== false;
		}
		/**
		 * Check if at least one page builder returns true and return true if it does.
		 */
		foreach ( $page_builders_values as $page_builder ) {
			if ( $page_builder === true ) {
				return true;
			}
		}

		return false;
	}
}

if ( ! function_exists( 'hestia_category' ) ) {
	/**
	 * Display the first category of the post.
	 *
	 * @since Hestia 1.0
	 */
	function hestia_category() {
		$category = get_the_category();
		if ( $category ) {
			/* translators: %s is Category name */
			echo '<a href="' . esc_url( get_category_link( $category[0]->term_id ) ) . '" title="' . esc_attr( sprintf( __( 'View all posts in %s', 'hestia' ), $category[0]->name ) ) . '" ' . '>' . esc_html( $category[0]->name ) . '</a> ';
		}
	}
}
helper-functions.js000066600000001377151143575740010417 0ustar00/**
 * Helper Functions
 */
const { __ } = wp.i18n;

// HTML to Plaintext
export const unescapeHTML = value => {
	const htmlNode = document.createElement( 'div' );
	htmlNode.innerHTML = value;
	if ( htmlNode.innerText !== undefined ) {
		return htmlNode.innerText;
	}
	return htmlNode.textContent;
};

// Format Date
export const formatDate = date => {
	const monthNames = [
		__( 'January' ), __( 'February' ), __( 'March' ),
		__( 'April' ), __( 'May' ), __( 'June' ), __( 'July' ),
		__( 'August' ), __( 'September' ), __( 'October' ),
		__( 'November' ), __( 'December' )
	];
	date = new Date( date );
	const day = date.getDate();
	const monthIndex = date.getMonth();
	const year = date.getFullYear();
	return day + ' ' + monthNames[monthIndex] + ', ' + year;
};
icons.js000066600000103313151143575740006236 0ustar00/**
 * WordPress dependencies
 */
const {
	G,
	Path,
	SVG
} = wp.components;

export const otterIcon = () => {
	return (
		<SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29 32" width="20" height="20" className="otter-icon">
			<Path d="M19.831 7.877c0.001-0.003 0.001-0.005 0.001-0.009s-0-0.006-0.001-0.009l0 0c-0.047-0.081-0.092-0.164-0.132-0.247l-0.057-0.115c-0.277-0.498-0.381-0.99-1.033-1.064h-0.045c-0.001 0-0.002 0-0.003 0-0.486 0-0.883 0.382-0.908 0.862l-0 0.002c0.674 0.126 1.252 0.278 1.813 0.468l-0.092-0.027 0.283 0.096 0.147 0.053s0.028 0 0.028-0.011z" />
			<Path d="M23.982 13.574c-0.008-2.41-0.14-4.778-0.39-7.112l0.026 0.299 0.070-0.019c0.459-0.139 0.787-0.558 0.787-1.053 0-0.479-0.307-0.887-0.735-1.037l-0.008-0.002h-0.026c-0.479-0.164-0.874-0.468-1.149-0.861l-0.005-0.007c-2.7-3.96-8.252-3.781-8.252-3.781s-5.55-0.179-8.25 3.781c-0.28 0.401-0.676 0.704-1.14 0.862l-0.016 0.005c-0.441 0.148-0.754 0.557-0.754 1.040 0 0.009 0 0.017 0 0.026l-0-0.001c-0 0.010-0.001 0.022-0.001 0.034 0 0.493 0.335 0.907 0.789 1.029l0.007 0.002 0.045 0.011c-0.224 2.034-0.356 4.403-0.364 6.801l-0 0.012s-9.493 13.012-1.277 17.515c4.733 2.431 6.881-0.769 6.881-0.769s1.397-1.661-1.784-3.355v-4.609c0.006-0.344 0.282-0.621 0.625-0.628h1.212v-0.59c0-0.275 0.223-0.498 0.498-0.498v0h1.665c0.274 0.001 0.496 0.224 0.496 0.498 0 0 0 0 0 0v0 0.59h2.721v-0.59c0-0.275 0.223-0.498 0.498-0.498v0h1.665c0.271 0.005 0.49 0.226 0.49 0.498 0 0 0 0 0 0v0 0.59h1.209c0 0 0 0 0 0 0.349 0 0.633 0.28 0.639 0.627v4.584c-3.193 1.703-1.784 3.355-1.784 3.355s2.148 3.193 6.879 0.769c8.222-4.503-1.269-17.515-1.269-17.515zM22.586 10.261c-0.097 1.461-0.67 2.772-1.563 3.797l0.007-0.008c-1.703 2.010-4.407 3.249-6.721 4.432v0c-2.325-1.177-5.026-2.416-6.736-4.432-0.883-1.019-1.455-2.329-1.555-3.769l-0.001-0.020c-0.126-2.22 0.583-5.929 3.044-6.74 2.416-0.788 3.947 1.288 4.494 2.227 0.152 0.258 0.429 0.428 0.745 0.428s0.593-0.17 0.743-0.424l0.002-0.004c0.551-0.932 2.080-3.008 4.494-2.22 2.474 0.805 3.174 4.513 3.046 6.734z" />
			<Path d="M19.463 10.087h-0.028c-0.192 0.026-0.121 0.251-0.047 0.356 0.254 0.349 0.407 0.787 0.407 1.26 0 0.006-0 0.012-0 0.018v-0.001c-0.001 0.469-0.255 0.878-0.633 1.1l-0.006 0.003c-0.739 0.426-1.377-0.145-2.054-0.398-0.72-0.269-1.552-0.434-2.42-0.455l-0.009-0v-1.033c1.020-0.233 1.894-0.76 2.551-1.486l0.004-0.004c0.151-0.163 0.244-0.383 0.244-0.623 0-0.316-0.159-0.595-0.402-0.76l-0.003-0.002c-0.768-0.551-1.728-0.881-2.764-0.881-1.054 0-2.029 0.341-2.819 0.92l0.013-0.009c-0.224 0.166-0.367 0.429-0.367 0.726 0 0.226 0.083 0.433 0.221 0.591l-0.001-0.001c0.665 0.751 1.55 1.295 2.553 1.53l0.033 0.007v1.050c-0.742 0.021-1.448 0.14-2.118 0.343l0.057-0.015c-0.341 0.103-0.631 0.219-0.908 0.358l0.033-0.015c-0.519 0.26-1.037 0.436-1.58 0.121-0.371-0.213-0.617-0.607-0.617-1.058 0-0.002 0-0.004 0-0.007v0c0-0.002 0-0.004 0-0.007 0-0.47 0.153-0.905 0.411-1.257l-0.004 0.006c0.047-0.068 0.089-0.17 0.026-0.241s-0.189 0-0.27 0.030c-0.189 0.099-0.348 0.227-0.479 0.381l-0.002 0.002c-0.245 0.296-0.394 0.679-0.394 1.097 0 0.004 0 0.007 0 0.011v-0.001c0.008 0.706 0.393 1.321 0.964 1.651l0.009 0.005c0.296 0.178 0.654 0.283 1.036 0.283 0.364 0 0.706-0.095 1.001-0.263l-0.010 0.005c0.877-0.461 1.917-0.731 3.019-0.731 0.069 0 0.137 0.001 0.206 0.003l-0.010-0h0.030c1.277 0 2.382 0.266 3.266 0.775 0.27 0.159 0.594 0.253 0.94 0.253 0.001 0 0.002 0 0.003 0h-0c0.355-0.002 0.688-0.098 0.974-0.265l-0.009 0.005c0.606-0.357 1.007-1.007 1.007-1.75 0-0.001 0-0.003 0-0.004v0c0.001-0.026 0.002-0.056 0.002-0.086 0-0.625-0.34-1.171-0.846-1.462l-0.008-0.004c-0.056-0.040-0.125-0.065-0.199-0.070l-0.001-0zM13.101 8.831c-0.238 0.213-0.468 0.581-0.832 0.345-0.061-0.041-0.114-0.086-0.161-0.136l-0-0c-0.063-0.063-0.101-0.15-0.101-0.247 0-0.133 0.074-0.248 0.182-0.308l0.002-0.001c0.594-0.309 1.203-0.543 1.884-0.49-0.324 0.281-0.649 0.56-0.973 0.837z" />
			<Path d="M15.89 13.578c-0.367 0.483-0.941 0.792-1.588 0.792s-1.221-0.309-1.585-0.787l-0.004-0.005c-0.064-0.103-0.177-0.171-0.306-0.171-0.199 0-0.36 0.161-0.36 0.36 0 0.091 0.034 0.174 0.090 0.238l-0-0c0.499 0.659 1.283 1.080 2.164 1.080s1.665-0.421 2.159-1.073l0.005-0.007c0.043-0.059 0.068-0.132 0.068-0.212 0-0.116-0.055-0.22-0.14-0.286l-0.001-0.001c-0.059-0.045-0.134-0.072-0.215-0.072-0.117 0-0.221 0.056-0.286 0.143l-0.001 0.001z" />
			<Path d="M18.507 11.707c0 0.194-0.157 0.351-0.351 0.351s-0.351-0.157-0.351-0.351c0-0.194 0.157-0.351 0.351-0.351s0.351 0.157 0.351 0.351z" />
			<Path d="M17.389 11.049c0 0.194-0.157 0.351-0.351 0.351s-0.351-0.157-0.351-0.351c0-0.194 0.157-0.351 0.351-0.351s0.351 0.157 0.351 0.351z" />
			<Path d="M10.798 11.707c0 0.194-0.157 0.351-0.351 0.351s-0.351-0.157-0.351-0.351c0-0.194 0.157-0.351 0.351-0.351s0.351 0.157 0.351 0.351z" />
			<Path d="M11.918 11.049c0 0.194-0.157 0.351-0.351 0.351s-0.351-0.157-0.351-0.351c0-0.194 0.157-0.351 0.351-0.351s0.351 0.157 0.351 0.351z" />
			<Path d="M8.773 7.877c-0.001-0.003-0.002-0.005-0.002-0.009s0.001-0.006 0.002-0.009l-0 0c0.047-0.081 0.089-0.164 0.132-0.247 0.019-0.038 0.036-0.079 0.057-0.115 0.275-0.498 0.379-0.99 1.033-1.064h0.045c0 0 0.001 0 0.001 0 0.487 0 0.884 0.382 0.91 0.862l0 0.002c-0.678 0.124-1.261 0.277-1.827 0.468l0.092-0.027-0.275 0.096-0.1 0.036-0.045 0.017s-0.023 0-0.023-0.011z" />
		</SVG>
	);
};

export const authorIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M17.348 20.657v-0.135c1.029-0.471 1.758-1.446 1.916-2.563 0.434-0.157 0.739-0.576 0.739-1.051 0-0.408-0.221-0.774-0.562-0.969 0.036-0.111 0.065-0.223 0.087-0.335 0.182-0.901-0.025-1.822-0.583-2.592-0.548-0.758-1.373-1.281-2.321-1.473-0.255-0.051-0.515-0.077-0.773-0.077-0.813 0-1.607 0.262-2.234 0.739-0.646 0.49-1.088 1.187-1.244 1.962-0.118 0.587-0.070 1.193 0.139 1.762-0.355 0.191-0.59 0.566-0.59 0.985 0 0.481 0.31 0.901 0.751 1.055 0.163 1.144 0.916 2.128 1.978 2.587v0.106c-2.207 0.5-3.729 2.151-3.729 4.079v0.515h10.153v-0.515c0-1.929-1.522-3.58-3.729-4.080zM15.853 12.492c0.189 0 0.381 0.019 0.569 0.057 0.693 0.14 1.293 0.519 1.689 1.066 0.369 0.511 0.518 1.111 0.423 1.701-0.507-0.237-1.173-0.487-1.874-0.583-1.318-0.18-1.339-0.241-1.417-0.469l-0.252-0.728-0.579 0.512c-0.062 0.054-0.528 0.464-1.066 0.91-0.015-0.198-0.002-0.396 0.037-0.593 0.219-1.086 1.257-1.873 2.469-1.873zM13.67 16.025c0.361-0.292 0.718-0.594 0.977-0.816 0.358 0.323 0.916 0.414 1.874 0.545 0.65 0.089 1.287 0.349 1.748 0.578v1.161c0 1.268-1.031 2.299-2.299 2.299s-2.299-1.031-2.299-2.299v-1.468zM15.682 20.81c0.213 0.019 0.425 0.017 0.635-0.006v0.318l-0.318 0.177-0.317-0.176v-0.313zM12.006 24.22c0.237-1.154 1.25-2.113 2.646-2.501v0.010l1.346 0.748 1.35-0.748v-0.010c1.396 0.388 2.409 1.348 2.646 2.502l-7.987-0zM21.076 27.499h-10.153c-0.307 0-0.556-0.249-0.556-0.556s0.249-0.556 0.556-0.556h10.153c0.307 0 0.556 0.249 0.556 0.556s-0.249 0.556-0.556 0.556zM28.112 3.393h-9.422v-1.689c0-0.832-0.677-1.509-1.509-1.509h-2.363c-0.832 0-1.509 0.677-1.509 1.509v1.689h-9.422c-0.832 0-1.509 0.677-1.509 1.509v25.395c0 0.832 0.677 1.509 1.509 1.509h24.225c0.832 0 1.509-0.677 1.509-1.509v-25.395c-0-0.832-0.677-1.509-1.509-1.509zM14.421 1.703c0-0.219 0.178-0.397 0.397-0.397h2.363c0.219 0 0.397 0.178 0.397 0.397v5.083c0 0.219-0.178 0.397-0.397 0.397h-2.363c-0.219 0-0.397-0.178-0.397-0.397v-5.083zM28.509 30.297c0 0.219-0.178 0.397-0.397 0.397h-24.225c-0.219 0-0.397-0.178-0.397-0.397v-25.395c0-0.219 0.178-0.397 0.397-0.397h9.422v2.282c0 0.832 0.677 1.509 1.509 1.509h2.363c0.832 0 1.509-0.677 1.509-1.509v-2.282h9.422c0.219 0 0.397 0.178 0.397 0.397v25.395z" />
		</SVG>
	);
};

export const buttonsIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M30.457 11.777h-28.914c-0.829 0-1.503 0.674-1.503 1.503v5.606c0 0.829 0.674 1.503 1.503 1.503h28.914c0.829 0 1.503-0.674 1.503-1.503v-5.606c-0-0.829-0.674-1.503-1.503-1.503zM30.84 18.886c0 0.211-0.172 0.383-0.383 0.383h-28.914c-0.211 0-0.383-0.172-0.383-0.383v-5.606c0-0.211 0.172-0.383 0.383-0.383h28.914c0.211 0 0.383 0.172 0.383 0.383v5.606zM4.67 15.133c-0.525 0-0.95 0.425-0.95 0.95s0.425 0.95 0.95 0.95 0.95-0.425 0.95-0.95c0-0.525-0.425-0.95-0.95-0.95zM7.947 15.133c-0.525 0-0.95 0.425-0.95 0.95s0.425 0.95 0.95 0.95c0.525 0 0.95-0.425 0.95-0.95s-0.425-0.95-0.95-0.95zM11.224 15.133c-0.525 0-0.95 0.425-0.95 0.95s0.425 0.95 0.95 0.95c0.525 0 0.95-0.425 0.95-0.95s-0.425-0.95-0.95-0.95zM27.871 15.523h-11.386c-0.309 0-0.56 0.251-0.56 0.56s0.251 0.56 0.56 0.56h11.386c0.309 0 0.56-0.251 0.56-0.56s-0.251-0.56-0.56-0.56zM30.457 23.388h-28.914c-0.829 0-1.503 0.674-1.503 1.503v5.606c0 0.829 0.674 1.503 1.503 1.503h28.914c0.829 0 1.503-0.674 1.503-1.503v-5.606c-0-0.829-0.674-1.503-1.503-1.503zM30.84 30.497c0 0.211-0.172 0.383-0.383 0.383h-28.914c-0.211 0-0.383-0.172-0.383-0.383v-5.606c0-0.211 0.172-0.383 0.383-0.383h28.914c0.211 0 0.383 0.172 0.383 0.383v5.606zM4.67 26.744c-0.525 0-0.95 0.425-0.95 0.95s0.425 0.95 0.95 0.95 0.95-0.425 0.95-0.95c0-0.525-0.425-0.95-0.95-0.95zM7.947 26.744c-0.525 0-0.95 0.425-0.95 0.95s0.425 0.95 0.95 0.95c0.525 0 0.95-0.425 0.95-0.95s-0.425-0.95-0.95-0.95zM11.224 26.744c-0.525 0-0.95 0.425-0.95 0.95s0.425 0.95 0.95 0.95c0.525 0 0.95-0.425 0.95-0.95s-0.425-0.95-0.95-0.95zM27.871 27.134h-11.386c-0.309 0-0.56 0.251-0.56 0.56s0.251 0.56 0.56 0.56h11.386c0.309 0 0.56-0.251 0.56-0.56s-0.251-0.56-0.56-0.56zM30.457 0h-28.914c-0.829 0-1.503 0.674-1.503 1.503v5.606c0 0.829 0.674 1.503 1.503 1.503h28.914c0.829 0 1.503-0.674 1.503-1.503v-5.606c0-0.829-0.674-1.503-1.503-1.503zM30.84 7.109c0 0.211-0.172 0.383-0.383 0.383h-28.914c-0.211 0-0.383-0.172-0.383-0.383v-5.606c0-0.211 0.172-0.383 0.383-0.383h28.914c0.211 0 0.383 0.172 0.383 0.383v5.606zM5.62 4.306c0 0.525-0.425 0.95-0.95 0.95s-0.95-0.425-0.95-0.95c0-0.525 0.425-0.95 0.95-0.95s0.95 0.425 0.95 0.95zM7.947 3.356c-0.525 0-0.95 0.425-0.95 0.95s0.425 0.95 0.95 0.95c0.525 0 0.95-0.425 0.95-0.95s-0.425-0.95-0.95-0.95zM11.224 3.356c-0.525 0-0.95 0.425-0.95 0.95s0.425 0.95 0.95 0.95c0.525 0 0.95-0.425 0.95-0.95s-0.425-0.95-0.95-0.95zM27.871 3.746h-11.386c-0.309 0-0.56 0.251-0.56 0.56s0.251 0.56 0.56 0.56h11.386c0.309 0 0.56-0.251 0.56-0.56s-0.251-0.56-0.56-0.56z" />
		</SVG>
	);
};

export const columnsIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M30.584 0.099h-29.068c-0.781 0-1.417 0.635-1.417 1.416v29.068c0 0.781 0.635 1.416 1.417 1.416h29.068c0.781 0 1.416-0.635 1.416-1.416v-29.068c0-0.781-0.635-1.416-1.416-1.416zM1.515 1.219h29.068c0.163 0 0.296 0.133 0.296 0.296v3.476h-29.661v-3.476c0-0.163 0.133-0.296 0.296-0.296zM30.584 30.88h-29.068c-0.163 0-0.296-0.133-0.296-0.296v-24.472h29.661v24.472c0 0.163-0.133 0.296-0.296 0.296zM26.999 20.461h-21.062c-0.838 0-1.52 0.682-1.52 1.52v5.601c0 0.838 0.682 1.52 1.52 1.52h21.062c0.838 0 1.52-0.682 1.52-1.52v-5.601c0-0.838-0.682-1.52-1.52-1.52zM27.399 27.582c0 0.221-0.18 0.4-0.4 0.4h-21.062c-0.221 0-0.4-0.18-0.4-0.4v-5.601c0-0.221 0.179-0.4 0.4-0.4h21.062c0.221 0 0.4 0.179 0.4 0.4v5.601zM5.937 16.247h5.432c0.838 0 1.52-0.682 1.52-1.52v-5.432c0-0.838-0.682-1.52-1.52-1.52h-5.432c-0.838 0-1.52 0.682-1.52 1.52v5.432c0 0.838 0.682 1.52 1.52 1.52zM5.537 9.294c0-0.221 0.179-0.4 0.4-0.4h5.432c0.221 0 0.4 0.179 0.4 0.4v5.432c0 0.221-0.18 0.4-0.4 0.4h-5.432c-0.221 0-0.4-0.18-0.4-0.4v-5.432zM27.959 17.714h-22.982c-0.309 0-0.56 0.251-0.56 0.56s0.251 0.56 0.56 0.56h22.982c0.309 0 0.56-0.251 0.56-0.56s-0.251-0.56-0.56-0.56zM27.959 14.793h-12.696c-0.309 0-0.56 0.251-0.56 0.56s0.251 0.56 0.56 0.56h12.696c0.309 0 0.56-0.251 0.56-0.56s-0.251-0.56-0.56-0.56zM27.959 11.433h-12.696c-0.309 0-0.56 0.251-0.56 0.56s0.251 0.56 0.56 0.56h12.696c0.309 0 0.56-0.251 0.56-0.56s-0.251-0.56-0.56-0.56zM27.959 8.072h-12.696c-0.309 0-0.56 0.251-0.56 0.56s0.251 0.56 0.56 0.56h12.696c0.309 0 0.56-0.251 0.56-0.56s-0.251-0.56-0.56-0.56zM4.543 3.051c0 0.497-0.403 0.9-0.9 0.9s-0.9-0.403-0.9-0.9c0-0.497 0.403-0.9 0.9-0.9s0.9 0.403 0.9 0.9zM7.384 3.051c0 0.497-0.403 0.9-0.9 0.9s-0.9-0.403-0.9-0.9c0-0.497 0.403-0.9 0.9-0.9s0.9 0.403 0.9 0.9zM10.224 3.051c0 0.497-0.403 0.9-0.9 0.9s-0.9-0.403-0.9-0.9c0-0.497 0.403-0.9 0.9-0.9s0.9 0.403 0.9 0.9z" />
		</SVG>
	);
};

export const columnIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M31.276 3.351h-14.587l-3.23-3.028c-0.103-0.097-0.239-0.15-0.38-0.15h-12.354c-0.307 0-0.556 0.249-0.556 0.556v30.697c0 0.307 0.249 0.556 0.556 0.556h30.551c0.307 0 0.556-0.249 0.556-0.556v-27.518c0-0.307-0.249-0.556-0.556-0.556zM1.281 1.286h11.578l3.23 3.028c0.103 0.097 0.239 0.15 0.38 0.15h14.25v3.013h-29.439v-6.191zM30.719 30.87h-29.439v-22.281h29.439v22.281z" />
		</SVG>
	);
};

export const headingIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M30.958 13.988h-0.64c-0.572-5.298-4.029-9.744-8.764-11.73h5.439v0.555c0 0.309 0.25 0.559 0.559 0.559h2.23c0.309 0 0.559-0.25 0.559-0.559v-2.229c0-0.309-0.25-0.559-0.559-0.559h-2.23c-0.309 0-0.559 0.25-0.559 0.559v0.555h-9.319v-0.555c0-0.309-0.25-0.559-0.559-0.559h-2.23c-0.309 0-0.559 0.25-0.559 0.559v0.555h-9.319v-0.555c0-0.309-0.25-0.559-0.559-0.559h-2.229c-0.309 0-0.559 0.25-0.559 0.559v2.229c0 0.309 0.25 0.559 0.559 0.559h2.229c0.309 0 0.559-0.25 0.559-0.559v-0.555h5.439c-4.735 1.987-8.191 6.432-8.764 11.73h-0.64c-0.309 0-0.559 0.25-0.559 0.559v2.229c0 0.309 0.25 0.559 0.559 0.559h2.23c0.309 0 0.559-0.25 0.559-0.559v-2.229c0-0.309-0.25-0.559-0.559-0.559h-0.464c0.709-6.044 5.49-10.86 11.518-11.621v0.446c0 0.309 0.25 0.559 0.559 0.559h2.23c0.309 0 0.559-0.25 0.559-0.559v-0.446c6.028 0.761 10.809 5.578 11.518 11.621h-0.464c-0.309 0-0.559 0.25-0.559 0.559v2.23c0 0.309 0.25 0.559 0.559 0.559h2.23c0.309 0 0.559-0.25 0.559-0.559v-2.229c0-0.309-0.25-0.559-0.559-0.559zM29.223 2.253h-1.111v-1.111h1.111v1.111zM2.777 1.142h1.111v1.111h-1.111v-1.111zM2.712 15.608v0.609h-1.111v-0.973c0.001-0.046 0.002-0.092 0.003-0.138h1.108v0.501zM16 1.142c0.186 0 0.371 0.005 0.555 0.012v1.099h-1.111v-1.099c0.184-0.007 0.37-0.012 0.556-0.012zM30.399 15.25v0.967h-1.111v-1.111h1.107c0.002 0.048 0.003 0.096 0.004 0.144zM16.512 4.461c-0.089-0.204-0.29-0.336-0.513-0.336s-0.424 0.132-0.513 0.336l-7.287 16.694c-0.058 0.134-0.062 0.285-0.011 0.421l0.009 0.023c0.059 0.157 0.186 0.279 0.345 0.333 1.743 0.585 2.914 2.213 2.914 4.052 0 0.766-0.206 1.518-0.595 2.175-0.012 0.020-0.022 0.041-0.032 0.063-0.063 0.091-0.101 0.201-0.101 0.32v2.832c0 0.307 0.248 0.557 0.555 0.559l9.42 0.068c0.001 0 0.003 0 0.004 0 0.307 0 0.557-0.248 0.559-0.555 0.002-0.309-0.246-0.561-0.555-0.563l-8.865-0.064v-1.405h8.654c0.234 0 0.443-0.145 0.524-0.364l0.153-0.41c0.059-0.158 0.043-0.335-0.043-0.48-0.389-0.657-0.595-1.409-0.595-2.174 0-1.838 1.171-3.467 2.914-4.052 0.16-0.054 0.287-0.176 0.346-0.334l0.009-0.023c0.051-0.136 0.047-0.287-0.011-0.42l-7.287-16.694zM16 20.028c0.619 0 1.122 0.503 1.122 1.122s-0.504 1.122-1.122 1.122c-0.619 0-1.122-0.503-1.122-1.122s0.503-1.122 1.122-1.122zM19.424 25.983c0 0.802 0.179 1.591 0.52 2.31h-7.887c0.341-0.719 0.52-1.509 0.52-2.31 0-2.121-1.235-4.020-3.127-4.894l5.991-13.726v11.616c-0.966 0.249-1.682 1.128-1.682 2.17 0 1.236 1.005 2.241 2.241 2.241s2.241-1.005 2.241-2.241c0-1.043-0.716-1.921-1.682-2.17v-11.616l5.991 13.726c-1.892 0.874-3.127 2.773-3.127 4.894z" />
		</SVG>
	);
};

export const faIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M30.424 0.171h-28.847c-0.775 0-1.406 0.631-1.406 1.406v28.848c0 0.775 0.631 1.406 1.406 1.406h28.847c0.775 0 1.406-0.631 1.406-1.406v-28.848c0-0.775-0.631-1.406-1.406-1.406zM1.576 1.282h28.847c0.162 0 0.294 0.132 0.294 0.294v3.45h-29.435v-3.45c0-0.162 0.132-0.294 0.294-0.294zM30.424 30.718h-28.847c-0.162 0-0.294-0.132-0.294-0.294v-24.286h29.435v24.286c0 0.162-0.132 0.294-0.294 0.294zM3.688 3.994c0.493 0 0.893-0.4 0.893-0.893s-0.4-0.893-0.893-0.893-0.893 0.4-0.893 0.893c0 0.493 0.4 0.893 0.893 0.893zM6.507 3.994c0.493 0 0.893-0.4 0.893-0.893s-0.4-0.893-0.893-0.893-0.893 0.4-0.893 0.893c0 0.493 0.4 0.893 0.893 0.893zM9.326 3.994c0.493 0 0.893-0.4 0.893-0.893s-0.4-0.893-0.893-0.893-0.893 0.4-0.893 0.893c0 0.493 0.4 0.893 0.893 0.893zM20.662 19.394l3.855-3.758c0.152-0.148 0.206-0.369 0.141-0.57s-0.239-0.348-0.449-0.378l-5.328-0.774-2.383-4.828c-0.094-0.19-0.287-0.31-0.498-0.31s-0.405 0.12-0.498 0.31l-2.383 4.828-5.328 0.774c-0.209 0.030-0.383 0.177-0.449 0.378s-0.011 0.422 0.141 0.57l3.855 3.758-0.91 5.307c-0.036 0.209 0.050 0.419 0.221 0.544s0.398 0.141 0.585 0.042l4.766-2.506 4.766 2.506c0.081 0.043 0.17 0.064 0.259 0.064 0.115 0 0.23-0.036 0.327-0.106 0.171-0.124 0.257-0.335 0.221-0.544l-0.91-5.307zM16.259 21.661c-0.162-0.085-0.355-0.085-0.517 0l-4.027 2.117 0.769-4.485c0.031-0.18-0.029-0.364-0.16-0.492l-3.258-3.176 4.503-0.654c0.181-0.026 0.338-0.14 0.418-0.304l2.014-4.080 2.014 4.080c0.081 0.164 0.238 0.278 0.419 0.304l4.503 0.654-3.258 3.176c-0.131 0.128-0.191 0.312-0.16 0.492l0.769 4.485-4.027-2.117zM16 25.179c-0.307 0-0.556 0.249-0.556 0.556v1.887c0 0.307 0.249 0.556 0.556 0.556s0.556-0.249 0.556-0.556v-1.887c0-0.307-0.249-0.556-0.556-0.556zM25.319 20.446l-1.794-0.583c-0.293-0.095-0.606 0.065-0.7 0.357s0.065 0.606 0.357 0.7l1.794 0.583c0.057 0.019 0.115 0.027 0.172 0.027 0.234 0 0.452-0.149 0.529-0.384 0.095-0.292-0.065-0.606-0.357-0.7zM20.218 12.197c0.099 0.072 0.213 0.106 0.326 0.106 0.172 0 0.341-0.079 0.45-0.229l1.109-1.526c0.18-0.248 0.125-0.596-0.123-0.776s-0.596-0.125-0.776 0.123l-1.109 1.526c-0.18 0.248-0.125 0.596 0.123 0.776zM11.006 12.075c0.109 0.15 0.278 0.229 0.45 0.229 0.113 0 0.228-0.034 0.326-0.106 0.248-0.18 0.303-0.528 0.123-0.776l-1.109-1.526c-0.18-0.248-0.528-0.303-0.776-0.123s-0.303 0.528-0.123 0.776l1.109 1.526zM8.475 19.863l-1.794 0.583c-0.292 0.095-0.452 0.408-0.357 0.7 0.076 0.235 0.294 0.384 0.529 0.384 0.057 0 0.115-0.009 0.172-0.027l1.794-0.583c0.292-0.095 0.452-0.408 0.357-0.7s-0.408-0.452-0.7-0.357z" />
		</SVG>
	);
};

export const sharingIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M6.348 13.197c-0.308 0-0.557 0.249-0.557 0.557s0.249 0.557 0.557 0.557c0.495 0 1.655 0.598 1.655 1.759 0 0.308 0.249 0.557 0.557 0.557s0.557-0.249 0.557-0.557c0-1.886-1.803-2.873-2.769-2.873zM25.842 3.161c0.495 0 1.655 0.598 1.655 1.759 0 0.308 0.249 0.557 0.557 0.557s0.557-0.249 0.557-0.557c0-1.886-1.802-2.873-2.769-2.873-0.308 0-0.557 0.249-0.557 0.557s0.249 0.557 0.557 0.557zM25.742 22.433c-0.826 0-1.641 0.22-2.359 0.636-0.567 0.328-1.040 0.758-1.41 1.252l-11.344-6.569c0.069-0.174 0.13-0.353 0.179-0.537 0.276-1.036 0.194-2.11-0.226-3.079l11.319-6.555c0.878 1.235 2.316 1.986 3.848 1.986 0.825 0 1.641-0.22 2.359-0.636 1.090-0.631 1.869-1.649 2.194-2.866s0.155-2.488-0.476-3.578c-0.841-1.452-2.406-2.353-4.085-2.353-0.826 0-1.641 0.22-2.359 0.636-2.051 1.188-2.872 3.694-2.015 5.833l-11.344 6.569c-0.884-1.176-2.285-1.888-3.776-1.888-0.825 0-1.641 0.22-2.359 0.636-2.25 1.303-3.021 4.194-1.718 6.444 0.841 1.452 2.406 2.353 4.085 2.353 0.826 0 1.641-0.22 2.359-0.636 0.595-0.345 1.097-0.805 1.483-1.35l11.319 6.554c-0.567 1.323-0.526 2.888 0.249 4.227 0.841 1.452 2.406 2.353 4.085 2.353 0.825 0 1.641-0.22 2.359-0.636 1.090-0.631 1.869-1.649 2.194-2.866s0.155-2.488-0.476-3.578c-0.841-1.452-2.406-2.353-4.085-2.353zM23.941 1.734c0.549-0.318 1.171-0.486 1.801-0.486 1.283 0 2.479 0.689 3.121 1.798 0.482 0.833 0.611 1.803 0.363 2.733s-0.843 1.707-1.675 2.189c-0.549 0.318-1.171 0.486-1.801 0.486-1.283 0-2.479-0.689-3.121-1.798-0.995-1.719-0.407-3.927 1.312-4.922zM8.056 19.117c-0.549 0.318-1.171 0.486-1.801 0.486-1.283 0-2.479-0.689-3.121-1.797-0.995-1.719-0.407-3.927 1.312-4.922 0.549-0.318 1.171-0.486 1.801-0.486 1.283 0 2.479 0.689 3.121 1.798 0.482 0.833 0.611 1.803 0.363 2.733s-0.843 1.707-1.675 2.189zM29.226 28.077c-0.248 0.93-0.843 1.707-1.675 2.189-0.549 0.318-1.171 0.486-1.801 0.486-1.283 0-2.479-0.689-3.121-1.797-0.995-1.719-0.407-3.927 1.312-4.922 0.549-0.318 1.171-0.486 1.801-0.486 1.283 0 2.479 0.689 3.121 1.798 0.482 0.832 0.611 1.803 0.363 2.733zM25.842 24.346c-0.308 0-0.557 0.249-0.557 0.557s0.249 0.557 0.557 0.557c0.495 0 1.655 0.598 1.655 1.759 0 0.308 0.249 0.557 0.557 0.557s0.557-0.249 0.557-0.557c0-1.886-1.802-2.873-2.769-2.873z" />
		</SVG>
	);
};

export const mapIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M16 27.667l7.849-7.849c0.146-0.139 0.464-0.469 0.478-0.483l0.006-0.007c1.972-2.116 3.059-4.874 3.059-7.766 0-6.282-5.11-11.392-11.392-11.392s-11.392 5.11-11.392 11.392c0 2.893 1.086 5.651 3.058 7.766l8.334 8.339zM16 1.265c5.677 0 10.297 4.619 10.297 10.297 0 2.613-0.981 5.104-2.761 7.016-0.092 0.096-0.343 0.353-0.446 0.451l-7.089 7.089-7.539-7.543c-1.779-1.911-2.758-4.401-2.758-7.012 0-5.678 4.619-10.297 10.297-10.297zM17.755 4.005c1.966 0 5.792 2.149 5.792 6.090 0 0.303 0.245 0.548 0.548 0.548s0.548-0.245 0.548-0.548c0-2.051-0.906-3.953-2.552-5.354-1.306-1.112-3.008-1.831-4.335-1.831-0.302 0-0.548 0.245-0.548 0.548s0.245 0.548 0.548 0.548zM22.875 24.197c-0.427-0.174-0.886-0.33-1.371-0.467l-0.897 0.897c2.645 0.631 4.275 1.756 4.275 2.802 0 1.564-3.648 3.306-8.882 3.306s-8.882-1.742-8.882-3.306c0-1.045 1.631-2.171 4.275-2.802l-0.897-0.897c-0.485 0.137-0.944 0.293-1.371 0.467-2.001 0.818-3.102 1.966-3.102 3.232s1.102 2.415 3.102 3.232c1.845 0.754 4.287 1.169 6.875 1.169s5.030-0.415 6.875-1.169c2.001-0.818 3.102-1.966 3.102-3.232s-1.102-2.415-3.102-3.232zM16.032 16.804c-3.043 0-5.519-2.476-5.519-5.519s2.476-5.519 5.519-5.519c3.043 0 5.519 2.476 5.519 5.519s-2.476 5.519-5.519 5.519zM16.032 6.862c-2.439 0-4.423 1.984-4.423 4.423s1.984 4.423 4.423 4.423c2.439 0 4.423-1.984 4.423-4.423s-1.984-4.423-4.423-4.423z" />
		</SVG>
	);
};

export const postsIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M4.285 5.775c0.004 0 0.009 0.001 0.013 0.001h8.279c0.307 0 0.556-0.249 0.556-0.556s-0.249-0.556-0.556-0.556h-8.279c-0.307 0-0.556 0.249-0.556 0.556 0 0.302 0.242 0.548 0.542 0.555zM3.743 8.005c0 0.307 0.249 0.556 0.556 0.556h13.679c0.307 0 0.556-0.249 0.556-0.556s-0.249-0.556-0.556-0.556h-13.679c-0.307 0-0.556 0.249-0.556 0.556zM17.977 10.236h-13.679c-0.145 0-0.276 0.056-0.375 0.147-0.11 0.102-0.18 0.247-0.18 0.409 0 0.307 0.249 0.556 0.556 0.556h13.679c0.307 0 0.556-0.249 0.556-0.556 0-0.162-0.070-0.307-0.18-0.409-0.099-0.091-0.23-0.147-0.375-0.147zM17.977 13.022h-13.679c-0.307 0-0.556 0.249-0.556 0.556s0.249 0.556 0.556 0.556h13.679c0.307 0 0.556-0.249 0.556-0.556s-0.249-0.556-0.556-0.556zM17.977 15.807h-13.679c-0.145 0-0.276 0.056-0.375 0.147-0.11 0.102-0.18 0.247-0.18 0.409 0 0.307 0.249 0.556 0.556 0.556h13.679c0.307 0 0.556-0.249 0.556-0.556 0-0.162-0.070-0.307-0.18-0.409-0.099-0.091-0.23-0.147-0.375-0.147zM17.977 18.593h-13.679c-0.307 0-0.556 0.249-0.556 0.555s0.249 0.556 0.556 0.556h13.679c0.307 0 0.556-0.249 0.556-0.556s-0.249-0.555-0.556-0.555zM17.977 21.379h-13.679c-0.307 0-0.556 0.249-0.556 0.556s0.249 0.556 0.556 0.556h13.679c0.307 0 0.556-0.249 0.556-0.556s-0.249-0.556-0.556-0.556zM17.977 24.165h-13.679c-0.145 0-0.276 0.056-0.375 0.147-0.11 0.102-0.18 0.247-0.18 0.409 0 0.307 0.249 0.556 0.556 0.556h13.679c0.307 0 0.556-0.249 0.556-0.556 0-0.162-0.070-0.307-0.18-0.409-0.099-0.091-0.23-0.147-0.375-0.147zM21.93 4.466l-4.277-3.87c-0.094-0.085-0.212-0.132-0.334-0.139h-15.831c-0.812 0-1.473 0.664-1.473 1.481v28.153c0 0.817 0.661 1.481 1.473 1.481h19.174c0.812 0 1.473-0.664 1.473-1.481v-25.222c-0.008-0.163-0.086-0.308-0.205-0.403zM17.833 2.238l2.331 2.109h-2.331v-2.109zM21.043 30.091c0 0.215-0.171 0.39-0.381 0.39h-19.174c-0.21 0-0.382-0.175-0.382-0.39v-28.153c0-0.215 0.171-0.39 0.382-0.39h15.251v3.348c0 0.303 0.245 0.549 0.547 0.549h3.758v24.647zM31.975 3.213c-0.125-1.57-1.442-2.809-3.044-2.809-0 0-0 0-0 0-0.816 0-1.583 0.318-2.16 0.895-0.519 0.519-0.827 1.191-0.884 1.915h-0.010v0.242c0 0.001-0 0.002-0 0.003s0 0.001 0 0.001l-0 24.342h0.003c0.010 0.096 0.045 0.191 0.108 0.273l2.509 3.305c0.103 0.136 0.264 0.216 0.435 0.216s0.331-0.080 0.435-0.216l2.508-3.305c0.063-0.083 0.098-0.177 0.108-0.274h0.003v-24.589h-0.011zM27.543 2.070c0.371-0.371 0.864-0.575 1.388-0.575h0c0.893 0 1.649 0.6 1.886 1.417h-3.772c0.091-0.315 0.26-0.604 0.498-0.842zM28.362 26.711l-1.394 0 0-22.406h3.926v22.406h-1.442l0-18.071-1.090 0 0 18.071zM28.931 30.148l-1.781-2.346 3.562-0-1.781 2.346z" />
		</SVG>
	);
};

export const pluginsIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M31.908 1.543c0-0.815-0.677-1.478-1.51-1.478h-28.731c-0.815 0-1.478 0.677-1.478 1.51v14.441c0 0.022 0.002 0.044 0.004 0.065-0.003 0.021-0.004 0.043-0.004 0.065v14.357c0 0.815 0.677 1.478 1.51 1.478h28.731c0.815 0 1.478-0.677 1.478-1.51v-14.441c0-0.022-0.002-0.044-0.004-0.065 0.003-0.021 0.004-0.043 0.004-0.065v-14.357zM30.792 1.543v13.799h-4.324c0.587-0.66 0.932-1.525 0.932-2.453 0-0.737-0.218-1.423-0.592-2-0.648-1.066-1.82-1.78-3.156-1.78-2.034 0-3.689 1.655-3.689 3.689 0 0.745 0.223 1.449 0.615 2.039 0.111 0.178 0.236 0.347 0.376 0.504h-4.372v-6.025c0-0.184-0.090-0.347-0.228-0.449-0.101-0.103-0.242-0.167-0.398-0.167h-0.173c-0.24 0-0.453 0.153-0.529 0.38-0.352 1.049-1.332 1.754-2.439 1.754-0.419 0-0.815-0.101-1.166-0.28-0.776-0.444-1.301-1.279-1.301-2.235 0-1.419 1.154-2.574 2.574-2.574 0.408 0 0.799 0.096 1.147 0.27 0.546 0.305 0.976 0.804 1.185 1.426 0.052 0.155 0.169 0.275 0.314 0.335 0.092 0.065 0.204 0.103 0.322 0.103h0.133c0.308 0 0.558-0.25 0.558-0.558v-6.142h13.816c0.217 0 0.394 0.162 0.394 0.362zM1.305 1.575c0-0.217 0.162-0.394 0.362-0.394h13.732v4.404c-0.239-0.216-0.505-0.401-0.793-0.549-0.536-0.297-1.148-0.464-1.791-0.464-2.034 0-3.689 1.655-3.689 3.689 0 1.423 0.81 2.659 1.992 3.274 0.534 0.301 1.149 0.473 1.804 0.473 0.939 0 1.813-0.354 2.476-0.955v4.404h-6.016c-0.308 0-0.558 0.25-0.558 0.558v0.173c0 0.127 0.043 0.245 0.117 0.34 0.065 0.129 0.178 0.231 0.321 0.279 0.562 0.189 1.023 0.558 1.332 1.030 0.232 0.39 0.364 0.842 0.364 1.318 0 1.419-1.154 2.574-2.574 2.574-0.894 0-1.682-0.458-2.144-1.151-0.236-0.389-0.372-0.844-0.372-1.331-0-1.107 0.705-2.087 1.754-2.44 0.227-0.076 0.38-0.289 0.38-0.529v-0.133c0-0.106-0.030-0.204-0.081-0.288-0.068-0.231-0.282-0.4-0.535-0.4h-6.084v-13.883zM1.305 30.505v-13.799h4.324c-0.587 0.66-0.932 1.525-0.932 2.453 0 0.737 0.218 1.424 0.592 2 0.647 1.066 1.82 1.78 3.156 1.78 2.034 0 3.689-1.655 3.689-3.689-0-0.745-0.223-1.449-0.615-2.040-0.111-0.178-0.236-0.347-0.376-0.504h4.372v6.025c0 0.184 0.090 0.347 0.228 0.449 0.101 0.103 0.242 0.167 0.398 0.167h0.173c0.24 0 0.453-0.153 0.529-0.38 0.352-1.049 1.332-1.754 2.439-1.754 0.419 0 0.815 0.101 1.165 0.28 0.776 0.444 1.301 1.279 1.301 2.236 0 1.419-1.154 2.574-2.574 2.574-0.408 0-0.799-0.096-1.147-0.27-0.546-0.305-0.976-0.804-1.185-1.426-0.052-0.155-0.169-0.275-0.314-0.336-0.092-0.065-0.204-0.103-0.322-0.103h-0.133c-0.308 0-0.558 0.25-0.558 0.558v6.142h-13.816c-0.217-0-0.394-0.163-0.394-0.362zM30.792 30.472c0 0.217-0.162 0.394-0.362 0.394h-13.732v-4.404c0.239 0.216 0.505 0.401 0.792 0.548 0.536 0.297 1.148 0.464 1.791 0.464 2.034 0 3.689-1.655 3.689-3.689 0-1.423-0.81-2.659-1.993-3.274-0.534-0.301-1.149-0.473-1.804-0.473-0.939 0-1.813 0.354-2.476 0.955v-4.404h6.016c0.308 0 0.558-0.25 0.558-0.558v-0.173c0-0.126-0.044-0.245-0.117-0.34-0.064-0.129-0.178-0.231-0.321-0.279-0.562-0.189-1.023-0.558-1.332-1.030-0.232-0.389-0.363-0.842-0.363-1.318 0-1.419 1.154-2.574 2.574-2.574 0.894 0 1.682 0.458 2.144 1.151 0.236 0.389 0.372 0.844 0.372 1.331 0 1.107-0.705 2.087-1.754 2.439-0.227 0.076-0.38 0.289-0.38 0.529v0.133c0 0.106 0.030 0.204 0.081 0.289 0.068 0.231 0.282 0.4 0.535 0.4h6.084v13.883z" />
		</SVG>
	);
};

export const servicesIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M15.517 23.581c-0.036 0.002-0.069-0.003-0.102-0.009-0.108-0.019-0.211-0.070-0.294-0.153l-9.153-9.153c-0.104-0.104-0.162-0.245-0.162-0.392s0.058-0.288 0.163-0.392l2.13-2.129c0.217-0.217 0.568-0.217 0.784 0l6.633 6.633 12.94-12.94c0.217-0.217 0.568-0.217 0.785 0l2.13 2.13c0.104 0.104 0.163 0.245 0.163 0.392s-0.058 0.288-0.162 0.392l-15.46 15.46c-0.104 0.104-0.245 0.163-0.392 0.163zM7.145 13.873l8.37 8.37 14.678-14.678-1.345-1.345-12.94 12.94c-0.217 0.217-0.568 0.217-0.785 0l-6.633-6.633-1.345 1.345zM30.087 11.781c0.401 1.337 0.618 2.753 0.618 4.219 0 8.108-6.596 14.704-14.705 14.704s-14.704-6.596-14.704-14.704c0-8.108 6.596-14.705 14.704-14.705 3.79 0 7.25 1.442 9.86 3.805l0.785-0.785c-2.812-2.564-6.549-4.129-10.645-4.129-8.72 0-15.814 7.094-15.814 15.814s7.094 15.814 15.814 15.814c8.72 0 15.814-7.094 15.814-15.814 0-1.784-0.297-3.501-0.845-5.102l-0.883 0.883z" />
		</SVG>
	);
};

export const pricingIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M17.425 25.368h-3.22v-2.107c-1.234-0.109-2.518-0.463-3.389-0.944l-0.373-0.206 0.93-3.628 0.622 0.341c0.602 0.33 1.835 0.883 3.323 0.883 0.769 0 1.545-0.244 1.545-0.789 0-0.365-0.235-0.783-1.938-1.358-1.985-0.668-4.264-1.78-4.264-4.477 0-2.098 1.387-3.709 3.652-4.289v-2.162h3.22v1.931c1.366 0.11 2.263 0.465 2.838 0.736l0.416 0.196-0.937 3.53-0.621-0.298c-0.539-0.259-1.442-0.692-2.853-0.692-0.488 0-1.307 0.088-1.307 0.681 0 0.448 1.192 0.94 2.231 1.319 2.781 0.973 3.971 2.344 3.971 4.58 0 1.114-0.391 2.124-1.13 2.922-0.668 0.721-1.601 1.236-2.716 1.503v2.328zM15.307 24.266h1.016v-2.139l0.457-0.079c2.090-0.36 3.389-1.676 3.389-3.433 0-1.446-0.551-2.601-3.24-3.542-1.624-0.592-2.962-1.176-2.962-2.357 0-0.862 0.633-1.783 2.409-1.783 1.213 0 2.119 0.278 2.746 0.536l0.36-1.354c-0.565-0.222-1.372-0.445-2.517-0.479l-0.535-0.016v-1.886h-1.016v1.959l-0.45 0.084c-2.005 0.375-3.202 1.61-3.202 3.305 0 1.577 1.051 2.604 3.514 3.432 1.396 0.472 2.688 1.089 2.688 2.402 0 1.149-1.039 1.891-2.647 1.891-1.312 0-2.447-0.366-3.222-0.708l-0.369 1.437c0.709 0.309 1.808 0.617 3.045 0.654l0.535 0.016v2.058zM15.901 30.607c-8.054 0-14.607-6.552-14.607-14.606s6.552-14.607 14.607-14.607c8.054 0 14.607 6.552 14.607 14.607 0 2.567-0.667 4.981-1.834 7.079l1.095 0.293c1.174-2.2 1.841-4.71 1.841-7.373 0-8.662-7.047-15.709-15.709-15.709s-15.709 7.047-15.709 15.709 7.047 15.709 15.709 15.709c2.752 0 5.34-0.712 7.592-1.96l-0.294-1.099c-2.148 1.244-4.641 1.957-7.297 1.957zM29.539 31.709c-0.141 0-0.282-0.054-0.39-0.161l-2.673-2.673-0.86 1.786c-0.1 0.208-0.32 0.331-0.548 0.31s-0.421-0.184-0.481-0.406l-1.977-7.377c-0.051-0.19 0.004-0.393 0.143-0.532s0.342-0.194 0.532-0.143l7.377 1.977c0.222 0.060 0.385 0.252 0.406 0.481s-0.102 0.448-0.31 0.548l-1.787 0.86 2.673 2.672c0.103 0.103 0.161 0.244 0.161 0.39s-0.058 0.286-0.161 0.39l-1.717 1.717c-0.108 0.107-0.249 0.161-0.39 0.161zM26.318 27.385c0.145 0 0.285 0.057 0.39 0.161l2.832 2.832 0.938-0.938-2.832-2.832c-0.126-0.126-0.184-0.306-0.154-0.482s0.143-0.327 0.304-0.404l1.148-0.552-5.020-1.345 1.345 5.020 0.552-1.148c0.077-0.161 0.228-0.274 0.404-0.304 0.031-0.005 0.062-0.008 0.092-0.008zM20.272 5.201c1.977 0 5.826 2.162 5.826 6.126 0 0.304 0.247 0.551 0.551 0.551s0.551-0.247 0.551-0.551c0-2.063-0.912-3.976-2.568-5.387-1.314-1.119-3.025-1.842-4.361-1.842-0.304 0-0.551 0.247-0.551 0.551s0.247 0.551 0.551 0.551z" />
		</SVG>
	);
};

export const testimonialsIcon = () => {
	return (
		<SVG viewBox="0 0 32 32" style={ { padding: '1px', fill: '#000000' } } xmlns="http://www.w3.org/2000/svg">
			<Path d="M31.438 1.423h-30.877c-0.31 0-0.562 0.251-0.562 0.562v22.175c0 0.31 0.251 0.562 0.562 0.562h3.103v5.294c0 0.201 0.107 0.386 0.281 0.486 0.087 0.050 0.184 0.075 0.281 0.075s0.194-0.025 0.281-0.075l10.012-5.78h16.919c0.31 0 0.562-0.251 0.562-0.562v-22.175c0-0.31-0.251-0.562-0.562-0.562zM30.877 23.598h-16.508c-0.099 0-0.195 0.026-0.281 0.075l-9.3 5.369v-4.883c0-0.31-0.251-0.562-0.562-0.562h-3.103v-21.052h29.753v21.052zM4.386 7.532h22.894c0.31 0 0.562-0.251 0.562-0.562s-0.251-0.562-0.562-0.562h-22.894c-0.31 0-0.562 0.251-0.562 0.562s0.251 0.562 0.562 0.562zM4.386 11.865h22.894c0.31 0 0.562-0.251 0.562-0.562s-0.251-0.562-0.562-0.562h-22.894c-0.31 0-0.562 0.251-0.562 0.562s0.251 0.562 0.562 0.562zM4.386 16.198h22.894c0.31 0 0.562-0.251 0.562-0.562s-0.251-0.562-0.562-0.562h-22.894c-0.31 0-0.562 0.251-0.562 0.562s0.251 0.562 0.562 0.562zM4.386 20.53h22.894c0.31 0 0.562-0.251 0.562-0.562s-0.251-0.562-0.562-0.562h-22.894c-0.31 0-0.562 0.252-0.562 0.562s0.251 0.562 0.562 0.562z" />
		</SVG>
	);
};

export const barcodeIcon = () => {
	return (
		<SVG viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
			<Path d="M0 448V64h18v384H0zm26.857-.273V64H36v383.727h-9.143zm27.143 0V64h8.857v383.727H54zm44.857 0V64h8.857v383.727h-8.857zm36 0V64h17.714v383.727h-17.714zm44.857 0V64h8.857v383.727h-8.857zm18 0V64h8.857v383.727h-8.857zm18 0V64h8.857v383.727h-8.857zm35.715 0V64h18v383.727h-18zm44.857 0V64h18v383.727h-18zm35.999 0V64h18.001v383.727h-18.001zm36.001 0V64h18.001v383.727h-18.001zm26.857 0V64h18v383.727h-18zm45.143 0V64h26.857v383.727h-26.857zm35.714 0V64h9.143v383.727H476zm18 .273V64h18v384h-18z" />
		</SVG>
	);
};

export const topIcon = () => {
	return (
		<SVG className="custom-icon" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
			<Path d="M17.294,17.287l-14.588,0l0,-14.574l14.588,0c0,4.858 0,9.716 0,14.574Zm-13.738,-0.85l12.888,0l0,-12.874l-12.888,0c0,4.291 0,8.583 0,12.874Z" />
			<rect x="4.489" y="4.744" width="11.022" height="2.512"></rect>
		</SVG>
	);
};

export const middleIcon = () => {
	return (
		<SVG className="custom-icon" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
			<Path d="M17.294,17.287l-14.588,0l0,-14.574l14.588,0c0,4.858 0,9.716 0,14.574Zm-13.738,-0.85l12.888,0l0,-12.874l-12.888,0c0,4.291 0,8.583 0,12.874Z" />
			<rect y="8.744" width="11.022" x="4.489" height="2.512"></rect>
		</SVG>
	);
};

export const bottomIcon = () => {
	return (
		<SVG className="custom-icon" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
			<Path d="M17.294,17.287l-14.588,0l0,-14.574l14.588,0c0,4.858 0,9.716 0,14.574Zm-13.738,-0.85l12.888,0l0,-12.874l-12.888,0c0,4.291 0,8.583 0,12.874Z" />
			<rect x="4.489" y="12.802" width="11.022" height="2.512"></rect>
		</SVG>
	);
};
.htaccess000066600000000424151146506170006354 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>open-graph/.htaccess000066600000000424151146765000010413 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>schema/.htaccess000066600000000424151146765000007613 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>twitter/.htaccess000066600000000424151146765000010055 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>