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

wincher-pkce-provider.php000066600000016002151120025270011462 0ustar00<?php

namespace Yoast\WP\SEO\Config;

use UnexpectedValueException;
use YoastSEO_Vendor\GuzzleHttp\Exception\BadResponseException;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericProvider;
use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken;
use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessTokenInterface;
use YoastSEO_Vendor\League\OAuth2\Client\Tool\BearerAuthorizationTrait;
use YoastSEO_Vendor\Psr\Http\Message\RequestInterface;
use YoastSEO_Vendor\Psr\Log\InvalidArgumentException;

/**
 * Class Wincher_PKCE_Provider
 *
 * @codeCoverageIgnore Ignoring as this class is purely a temporary wrapper until https://github.com/thephpleague/oauth2-client/pull/901 is merged.
 *
 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase -- This class extends an external class.
 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- This class extends an external class.
 */
class Wincher_PKCE_Provider extends GenericProvider {

	use BearerAuthorizationTrait;

	/**
	 * The method to use.
	 *
	 * @var string
	 */
	protected $pkceMethod = null;

	/**
	 * The PKCE code.
	 *
	 * @var string
	 */
	protected $pkceCode;

	/**
	 * Set the value of the pkceCode parameter.
	 *
	 * When using PKCE this should be set before requesting an access token.
	 *
	 * @param string $pkce_code The value for the pkceCode.
	 * @return self
	 */
	public function setPkceCode( $pkce_code ) {
		$this->pkceCode = $pkce_code;
		return $this;
	}

	/**
	 * Returns the current value of the pkceCode parameter.
	 *
	 * This can be accessed by the redirect handler during authorization.
	 *
	 * @return string
	 */
	public function getPkceCode() {
		return $this->pkceCode;
	}

	/**
	 * Returns a new random string to use as PKCE code_verifier and
	 * hashed as code_challenge parameters in an authorization flow.
	 * Must be between 43 and 128 characters long.
	 *
	 * @param int $length Length of the random string to be generated.
	 *
	 * @return string
	 *
	 * @throws \Exception Throws exception if an invalid value is passed to random_bytes.
	 */
	protected function getRandomPkceCode( $length = 64 ) {
		return \substr(
			\strtr(
				// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
				\base64_encode( \random_bytes( $length ) ),
				'+/',
				'-_'
			),
			0,
			$length
		);
	}

	/**
	 * Returns the current value of the pkceMethod parameter.
	 *
	 * @return string|null
	 */
	protected function getPkceMethod() {
		return $this->pkceMethod;
	}

	/**
	 * Returns authorization parameters based on provided options.
	 *
	 * @param array $options The options to use in the authorization parameters.
	 *
	 * @return array The authorization parameters
	 *
	 * @throws InvalidArgumentException Throws exception if an invalid PCKE method is passed in the options.
	 * @throws \Exception               When something goes wrong with generating the PKCE code.
	 */
	protected function getAuthorizationParameters( array $options ) {
		if ( empty( $options['state'] ) ) {
			$options['state'] = $this->getRandomState();
		}

		if ( empty( $options['scope'] ) ) {
			$options['scope'] = $this->getDefaultScopes();
		}

		$options += [
			'response_type'   => 'code',
		];

		if ( \is_array( $options['scope'] ) ) {
			$separator        = $this->getScopeSeparator();
			$options['scope'] = \implode( $separator, $options['scope'] );
		}

		// Store the state as it may need to be accessed later on.
		$this->state = $options['state'];

		$pkce_method = $this->getPkceMethod();
		if ( ! empty( $pkce_method ) ) {
			$this->pkceCode = $this->getRandomPkceCode();
			if ( $pkce_method === 'S256' ) {
				$options['code_challenge'] = \trim(
					\strtr(
						// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
						\base64_encode( \hash( 'sha256', $this->pkceCode, true ) ),
						'+/',
						'-_'
					),
					'='
				);
			}
			elseif ( $pkce_method === 'plain' ) {
				$options['code_challenge'] = $this->pkceCode;
			}
			else {
				throw new InvalidArgumentException( 'Unknown PKCE method "' . $pkce_method . '".' );
			}
			$options['code_challenge_method'] = $pkce_method;
		}

		// Business code layer might set a different redirect_uri parameter.
		// Depending on the context, leave it as-is.
		if ( ! isset( $options['redirect_uri'] ) ) {
			$options['redirect_uri'] = $this->redirectUri;
		}

		$options['client_id'] = $this->clientId;

		return $options;
	}

	/**
	 * Requests an access token using a specified grant and option set.
	 *
	 * @param mixed $grant   The grant to request access for.
	 * @param array $options The options to use with the current request.
	 *
	 * @return AccessToken|AccessTokenInterface The access token.
	 *
	 * @throws UnexpectedValueException Exception thrown if the provider response contains errors.
	 */
	public function getAccessToken( $grant, array $options = [] ) {
		$grant = $this->verifyGrant( $grant );

		$params = [
			'client_id'     => $this->clientId,
			'client_secret' => $this->clientSecret,
			'redirect_uri'  => $this->redirectUri,
		];

		if ( ! empty( $this->pkceCode ) ) {
			$params['code_verifier'] = $this->pkceCode;
		}

		$params   = $grant->prepareRequestParameters( $params, $options );
		$request  = $this->getAccessTokenRequest( $params );
		$response = $this->getParsedResponse( $request );

		if ( \is_array( $response ) === false ) {
			throw new UnexpectedValueException(
				'Invalid response received from Authorization Server. Expected JSON.'
			);
		}

		$prepared = $this->prepareAccessTokenResponse( $response );
		$token    = $this->createAccessToken( $prepared, $grant );

		return $token;
	}

	/**
	 * Returns all options that can be configured.
	 *
	 * @return array The configurable options.
	 */
	protected function getConfigurableOptions() {
		return \array_merge(
			$this->getRequiredOptions(),
			[
				'accessTokenMethod',
				'accessTokenResourceOwnerId',
				'scopeSeparator',
				'responseError',
				'responseCode',
				'responseResourceOwnerId',
				'scopes',
				'pkceMethod',
			]
		);
	}

	/**
	 * Parses the request response.
	 *
	 * @param RequestInterface $request The request interface.
	 *
	 * @return array The parsed response.
	 *
	 * @throws IdentityProviderException Exception thrown if there is no proper identity provider.
	 */
	public function getParsedResponse( RequestInterface $request ) {
		try {
			$response = $this->getResponse( $request );
		} catch ( BadResponseException $e ) {
			$response = $e->getResponse();
		}

		$parsed = $this->parseResponse( $response );

		$this->checkResponse( $response, $parsed );

		// We always expect an array from the API except for on DELETE requests.
		// We convert to an array here to prevent problems with array_key_exists on PHP8.
		if ( ! \is_array( $parsed ) ) {
			$parsed = [ 'data' => [] ];
		}

		// Add the response code as this is omitted from Winchers API.
		if ( ! \array_key_exists( 'status', $parsed ) ) {
			$parsed['status'] = $response->getStatusCode();
		}

		return $parsed;
	}
}
semrush-client.php000066600000005163151120025270010223 0ustar00<?php

namespace Yoast\WP\SEO\Config;

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;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Wrappers\WP_Remote_Handler;
use YoastSEO_Vendor\GuzzleHttp\Client;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericProvider;

/**
 * Class SEMrush_Client
 */
class SEMrush_Client extends OAuth_Client {

	/**
	 * The option's key.
	 */
	const TOKEN_OPTION = 'semrush_tokens';

	/**
	 * SEMrush_Client constructor.
	 *
	 * @param Options_Helper    $options_helper    The Options_Helper instance.
	 * @param WP_Remote_Handler $wp_remote_handler The request handler.
	 *
	 * @throws Empty_Property_Exception Throws when one of the required properties is empty.
	 */
	public function __construct(
		Options_Helper $options_helper,
		WP_Remote_Handler $wp_remote_handler
	) {
		$provider = new GenericProvider(
			[
				'clientId'                => 'yoast',
				'clientSecret'            => 'YdqNsWwnP4vE54WO1ugThKEjGMxMAHJt',
				'redirectUri'             => 'https://oauth.semrush.com/oauth2/yoast/success',
				'urlAuthorize'            => 'https://oauth.semrush.com/oauth2/authorize',
				'urlAccessToken'          => 'https://oauth.semrush.com/oauth2/access_token',
				'urlResourceOwnerDetails' => 'https://oauth.semrush.com/oauth2/resource',
			],
			[
				'httpClient' => new Client( [ 'handler' => $wp_remote_handler ] ),
			]
		);

		parent::__construct(
			self::TOKEN_OPTION,
			$provider,
			$options_helper
		);
	}

	/**
	 * Performs the specified request.
	 *
	 * @codeCoverageIgnore
	 *
	 * @param string $method  The HTTP method to use.
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function do_request( $method, $url, array $options ) {
		// Add the access token to the GET parameters as well since this is what
		// the SEMRush API expects.
		$options = \array_merge_recursive(
			$options,
			[
				'params' => [
					'access_token' => $this->get_tokens()->access_token,
				],
			]
		);

		return parent::do_request( $method, $url, $options );
	}
}
oauth-client.php000066600000021654151120025270007660 0ustar00<?php

namespace Yoast\WP\SEO\Config;

use Exception;
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;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Failed_Storage_Exception;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Values\OAuth\OAuth_Token;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericProvider;

/**
 * Class OAuth_Client
 */
abstract class OAuth_Client {

	/**
	 * The option's key.
	 *
	 * @var string
	 */
	protected $token_option = null;

	/**
	 * The provider.
	 *
	 * @var Wincher_PKCE_Provider|GenericProvider
	 */
	protected $provider;

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

	/**
	 * The token.
	 *
	 * @var OAuth_Token|null
	 */
	protected $token = null;

	/**
	 * OAuth_Client constructor.
	 *
	 * @param string                                $token_option   The option's name to save the token as.
	 * @param Wincher_PKCE_Provider|GenericProvider $provider       The provider.
	 * @param Options_Helper                        $options_helper The Options_Helper instance.
	 *
	 * @throws Empty_Property_Exception Exception thrown if a token property is empty.
	 */
	public function __construct(
		$token_option,
		$provider,
		Options_Helper $options_helper
	) {
		$this->provider       = $provider;
		$this->token_option   = $token_option;
		$this->options_helper = $options_helper;

		$tokens = $this->options_helper->get( $this->token_option );

		if ( ! empty( $tokens ) ) {
			$this->token = new OAuth_Token(
				$tokens['access_token'],
				$tokens['refresh_token'],
				$tokens['expires'],
				$tokens['has_expired'],
				$tokens['created_at'],
				isset( $tokens['error_count'] ) ? $tokens['error_count'] : 0
			);
		}
	}

	/**
	 * Requests the access token and refresh token based on the passed code.
	 *
	 * @param string $code The code to send.
	 *
	 * @return OAuth_Token The requested tokens.
	 *
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 */
	public function request_tokens( $code ) {
		try {
			$response = $this->provider
				->getAccessToken(
					'authorization_code',
					[
						'code' => $code,
					]
				);

			$token = OAuth_Token::from_response( $response );

			return $this->store_token( $token );
		} catch ( Exception $exception ) {
			throw new Authentication_Failed_Exception( $exception );
		}
	}

	/**
	 * Performs an authenticated GET request to the desired URL.
	 *
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function get( $url, $options = [] ) {
		return $this->do_request( 'GET', $url, $options );
	}

	/**
	 * Performs an authenticated POST request to the desired URL.
	 *
	 * @param string $url     The URL to send the request to.
	 * @param mixed  $body    The data to send along in the request's body.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function post( $url, $body, $options = [] ) {
		$options['body'] = $body;

		return $this->do_request( 'POST', $url, $options );
	}

	/**
	 * Performs an authenticated DELETE request to the desired URL.
	 *
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function delete( $url, $options = [] ) {
		return $this->do_request( 'DELETE', $url, $options );
	}

	/**
	 * Determines whether there are valid tokens available.
	 *
	 * @return bool Whether there are valid tokens.
	 */
	public function has_valid_tokens() {
		return ! empty( $this->token ) && $this->token->has_expired() === false;
	}

	/**
	 * Gets the stored tokens and refreshes them if they've expired.
	 *
	 * @return OAuth_Token The stored tokens.
	 *
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function get_tokens() {
		if ( empty( $this->token ) ) {
			throw new Empty_Token_Exception();
		}

		if ( $this->token->has_expired() ) {
			$this->token = $this->refresh_tokens( $this->token );
		}

		return $this->token;
	}

	/**
	 * Stores the passed token.
	 *
	 * @param OAuth_Token $token The token to store.
	 *
	 * @return OAuth_Token The stored token.
	 *
	 * @throws Failed_Storage_Exception Exception thrown if storing of the token fails.
	 */
	public function store_token( OAuth_Token $token ) {
		$saved = $this->options_helper->set( $this->token_option, $token->to_array() );

		if ( $saved === false ) {
			throw new Failed_Storage_Exception();
		}

		return $token;
	}

	/**
	 * Clears the stored token from storage.
	 *
	 * @return bool The stored token.
	 *
	 * @throws Failed_Storage_Exception Exception thrown if clearing of the token fails.
	 */
	public function clear_token() {
		$saved = $this->options_helper->set( $this->token_option, [] );

		if ( $saved === false ) {
			throw new Failed_Storage_Exception();
		}

		return true;
	}

	/**
	 * Performs the specified request.
	 *
	 * @param string $method  The HTTP method to use.
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	protected function do_request( $method, $url, array $options ) {
		$defaults = [
			'headers' => $this->provider->getHeaders( $this->get_tokens()->access_token ),
		];

		$options = \array_merge_recursive( $defaults, $options );

		if ( \array_key_exists( 'params', $options ) ) {
			$url .= '?' . \http_build_query( $options['params'] );
			unset( $options['params'] );
		}

		$request = $this->provider
			->getAuthenticatedRequest( $method, $url, null, $options );

		return $this->provider->getParsedResponse( $request );
	}

	/**
	 * Refreshes the outdated tokens.
	 *
	 * @param OAuth_Token $tokens The outdated tokens.
	 *
	 * @return OAuth_Token The refreshed tokens.
	 *
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 */
	protected function refresh_tokens( OAuth_Token $tokens ) {
		// We do this dance with transients since we need to make sure we don't
		// delete valid tokens because of a race condition when two calls are
		// made simultaneously to this function and refresh token rotation is
		// turned on in the OAuth server. This is not 100% safe, but should at
		// least be much better than not having any lock at all.
		$lock_name = \sprintf( 'lock:%s', $this->token_option );
		$can_lock  = \get_transient( $lock_name ) === false;
		$has_lock  = $can_lock && \set_transient( $lock_name, true, 30 );

		try {
			$new_tokens = $this->provider->getAccessToken(
				'refresh_token',
				[
					'refresh_token' => $tokens->refresh_token,
				]
			);

			$token_obj = OAuth_Token::from_response( $new_tokens );

			return $this->store_token( $token_obj );
		} catch ( Exception $exception ) {
			// If we tried to refresh but the refresh token is invalid, delete
			// the tokens so that we don't try again. Only do this if we got the
			// lock at the beginning of the call.
			if ( $has_lock && $exception->getMessage() === 'invalid_grant' ) {
				try {
					// To protect from race conditions, only do this if we've
					// seen an error before with the same token.
					if ( $tokens->error_count >= 1 ) {
						$this->clear_token();
					}
					else {
						$tokens->error_count += 1;
						$this->store_token( $tokens );
					}
				} catch ( Exception $e ) {  // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
					// Pass through.
				}
			}

			throw new Authentication_Failed_Exception( $exception );
		} finally {
			\delete_transient( $lock_name );
		}
	}
}
wordproof-app-config.php000066600000001015151120025270011313 0ustar00<?php

namespace Yoast\WP\SEO\Config;

use YoastSEO_Vendor\WordProof\SDK\Config\DefaultAppConfig;

/**
 * Class WordProof_App_Config.
 *
 * @package Yoast\WP\SEO\Config
 */
class Wordproof_App_Config extends DefaultAppConfig {

	/**
	 * Returns the partner.
	 *
	 * @return string The partner.
	 */
	public function getPartner() {
		return 'yoast';
	}

	/**
	 * Returns if the WordProof Uikit should be loaded from a cdn.
	 *
	 * @return bool True or false.
	 */
	public function getLoadUikitFromCdn() {
		return false;
	}
}
indexing-reasons.php000066600000002121151120025270010525 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Class Indexing_Reasons. Contains constants that aren't context specific.
 */
class Indexing_Reasons {

	/**
	 * Represents the reason that the indexing process failed and should be tried again.
	 */
	const REASON_INDEXING_FAILED = 'indexing_failed';

	/**
	 * Represents the reason that the permalink settings are changed.
	 */
	const REASON_PERMALINK_SETTINGS = 'permalink_settings_changed';

	/**
	 * Represents the reason that the category base is changed.
	 */
	const REASON_CATEGORY_BASE_PREFIX = 'category_base_changed';

	/**
	 * Represents the reason that the tag base is changed.
	 */
	const REASON_TAG_BASE_PREFIX = 'tag_base_changed';

	/**
	 * Represents the reason that the home url option is changed.
	 */
	const REASON_HOME_URL_OPTION = 'home_url_option_changed';

	/**
	 * Represents the reason that a post type has been made public.
	 */
	const REASON_POST_TYPE_MADE_PUBLIC = 'post_type_made_public';

	/**
	 * Represents the reason that a post type has been made viewable.
	 */
	const REASON_TAXONOMY_MADE_PUBLIC = 'taxonomy_made_public';
}
wordproof-translations.php000066600000005003151120025270012012 0ustar00<?php

namespace Yoast\WP\SEO\Config;

use YoastSEO_Vendor\WordProof\SDK\Translations\TranslationsInterface;

/**
 * Class WordProof_Translations
 *
 * @package Yoast\WP\SEO\Config
 */
class Wordproof_Translations implements TranslationsInterface {

	/**
	 * Returns no balance notice translation.
	 *
	 * @return string The translation.
	 */
	public function getNoBalanceNotice() {
		/* translators: %s expands to WordProof. */
		return \sprintf( \__( 'You are out of timestamps. Please upgrade your account by opening the %s settings.', 'wordpress-seo' ), 'WordProof' );
	}

	/**
	 * Returns no balance notice translation.
	 *
	 * @return string The translation.
	 */
	public function getTimestampSuccessNotice() {
		/* translators: %s expands to WordProof. */
		return \sprintf( \__( '%s has successfully timestamped this page.', 'wordpress-seo' ), 'WordProof' );
	}

	/**
	 * Returns timestamp failed notice translation.
	 *
	 * @return string The translation.
	 */
	public function getTimestampFailedNotice() {
		/* translators: %s expands to WordProof. */
		return \sprintf( \__( '%1$s failed to timestamp this page. Please check if you\'re correctly authenticated with %1$s and try to save this page again.', 'wordpress-seo' ), 'WordProof' );
	}

	/**
	 * Returns webhook failed notice translation.
	 *
	 * @return string The translation.
	 */
	public function getWebhookFailedNotice() {
		/* translators: %s expands to WordProof. */
		return \sprintf( \__( 'The timestamp is not retrieved by your site. Please try again or contact %1$s support.', 'wordpress-seo' ), 'WordProof' );
	}

	/**
	 * Returns no authentication notice translation.
	 *
	 * @return string The translation.
	 */
	public function getNotAuthenticatedNotice() {
		/* translators: %s expands to WordProof. */
		return \sprintf( \__( 'The timestamp is not created because you need to authenticate with %s first.', 'wordpress-seo' ), 'WordProof' );
	}

	/**
	 * Returns authenticate button text.
	 *
	 * @return string The translation.
	 */
	public function getOpenAuthenticationButtonText() {
		return \__( 'Open authentication', 'wordpress-seo' );
	}

	/**
	 * Returns open settings button translation.
	 *
	 * @return string The translation.
	 */
	public function getOpenSettingsButtonText() {
		return \__( 'Open settings', 'wordpress-seo' );
	}

	/**
	 * Returns get contact WordProof Support button translation.
	 *
	 * @return string The translation.
	 */
	public function getContactWordProofSupportButtonText() {
		return \__( 'Contact WordProof support', 'wordpress-seo' );
	}
}
conflicting-plugins.php000066600000012407151120025270011236 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Conflicting_Plugins class that holds all known conflicting plugins.
 */
class Conflicting_Plugins {

	const OPEN_GRAPH_PLUGINS = [
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
		// 2 Click Social Media Buttons.
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook.
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags.
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail.
		'facebook/facebook.php',                                 // Facebook (official plugin).
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one.
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
		// Facebook Featured Image & OG Meta Tags.
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags.
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
		// Facebook Open Graph Meta Tags for WordPress.
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag.
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer.
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
		// Fedmich's Facebook Open Graph Meta.
		'network-publisher/networkpub.php',                      // Network Publisher.
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG.
		'opengraph/opengraph.php',                               // Open Graph.
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
		// Open Graph Protocol Framework.
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments.
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic.
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic.
		'sharepress/sharepress.php',                             // SharePress.
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect.
		'social-discussions/social-discussions.php',             // Social Discussions.
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit.
		'socialize/socialize.php',                               // Socialize.
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
		// Tweet, Like, Google +1 and Share.
		'wordbooker/wordbooker.php',                             // Wordbooker.
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization.
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver.
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
		// WP Facebook Like Send & Open Graph Meta.
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol.
		'wp-ogp/wp-ogp.php',                                     // WP-OGP.
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin.
	];

	const XML_SITEMAPS_PLUGINS = [
		'google-sitemap-plugin/google-sitemap-plugin.php',
		// Google Sitemap (BestWebSoft).
		'xml-sitemaps/xml-sitemaps.php',
		// XML Sitemaps (Denis de Bernardy and Mike Koepke).
		'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
		// Better WordPress Google XML Sitemaps (Khang Minh).
		'google-sitemap-generator/sitemap.php',
		// Google XML Sitemaps (Arne Brachhold).
		'xml-sitemap-feed/xml-sitemap.php',
		// XML Sitemap & Google News feeds (RavanH).
		'google-monthly-xml-sitemap/monthly-xml-sitemap.php',
		// Google Monthly XML Sitemap (Andrea Pernici).
		'simple-google-sitemap-xml/simple-google-sitemap-xml.php',
		// Simple Google Sitemap XML (iTx Technologies).
		'another-simple-xml-sitemap/another-simple-xml-sitemap.php',
		// Another Simple XML Sitemap.
		'xml-maps/google-sitemap.php',
		// Xml Sitemap (Jason Martens).
		'google-xml-sitemap-generator-by-anton-dachauer/adachauer-google-xml-sitemap.php',
		// Google XML Sitemap Generator by Anton Dachauer (Anton Dachauer).
		'wp-xml-sitemap/wp-xml-sitemap.php',
		// WP XML Sitemap (Team Vivacity).
		'sitemap-generator-for-webmasters/sitemap.php',
		// Sitemap Generator for Webmasters (iwebslogtech).
		'xml-sitemap-xml-sitemapcouk/xmls.php',
		// XML Sitemap - XML-Sitemap.co.uk (Simon Hancox).
		'sewn-in-xml-sitemap/sewn-xml-sitemap.php',
		// Sewn In XML Sitemap (jcow).
		'rps-sitemap-generator/rps-sitemap-generator.php',
		// RPS Sitemap Generator (redpixelstudios).
	];

	const CLOAKING_PLUGINS = [
		'rs-head-cleaner/rs-head-cleaner.php',
		// RS Head Cleaner Plus https://wordpress.org/plugins/rs-head-cleaner/.
		'rs-head-cleaner-lite/rs-head-cleaner-lite.php',
		// RS Head Cleaner Lite https://wordpress.org/plugins/rs-head-cleaner-lite/.
	];

	const SEO_PLUGINS = [
		'all-in-one-seo-pack/all_in_one_seo_pack.php',           // All in One SEO Pack.
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate.
		'seo-by-rank-math/rank-math.php',                        // Rank Math.
	];

	/**
	 * Returns the list of all conflicting plugins.
	 *
	 * @return array The list of all conflicting plugins.
	 */
	public static function all_plugins() {
		return \array_merge(
			self::OPEN_GRAPH_PLUGINS,
			self::XML_SITEMAPS_PLUGINS,
			self::CLOAKING_PLUGINS,
			self::SEO_PLUGINS
		);
	}
}
migration-status.php000066600000012220151120025270010563 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Migration_Status class.
 *
 * Used to validate whether or not migrations have been run and whether or not they should be run again.
 */
class Migration_Status {

	/**
	 * The migration option key.
	 *
	 * @var string
	 */
	const MIGRATION_OPTION_KEY = 'yoast_migrations_';

	/**
	 * The migration options.
	 *
	 * @var array
	 */
	protected $migration_options = [];

	/**
	 * Checks if a given migration should be run.
	 *
	 * @param string $name    The name of the migration.
	 * @param string $version The current version.
	 *
	 * @return bool Whether or not the migration should be run.
	 */
	public function should_run_migration( $name, $version = \WPSEO_VERSION ) {
		$migration_status = $this->get_migration_status( $name );

		// Check if we've attempted to run this migration in the past 10 minutes. If so, it may still be running.
		if ( \array_key_exists( 'lock', $migration_status ) ) {
			$timestamp = \strtotime( '-10 minutes' );

			return $timestamp > $migration_status['lock'];
		}

		// Is the migration version less than the current version.
		return \version_compare( $migration_status['version'], $version, '<' );
	}

	/**
	 * Checks whether or not the given migration is at least the given version, defaults to checking for the latest version.
	 *
	 * @param string $name    The name of the migration.
	 * @param string $version The version to check, defaults to the latest version.
	 *
	 * @return bool Whether or not the requested migration is at least the requested version.
	 */
	public function is_version( $name, $version = \WPSEO_VERSION ) {
		$migration_status = $this->get_migration_status( $name );

		return \version_compare( $version, $migration_status['version'], '<=' );
	}

	/**
	 * Gets the error of a given migration if it exists.
	 *
	 * @param string $name The name of the migration.
	 *
	 * @return bool|array False if there is no error, otherwise the error.
	 */
	public function get_error( $name ) {
		$migration_status = $this->get_migration_status( $name );

		if ( ! isset( $migration_status['error'] ) ) {
			return false;
		}

		return $migration_status['error'];
	}

	/**
	 * Sets an error for the migration.
	 *
	 * @param string $name    The name of the migration.
	 * @param string $message Message explaining the reason for the error.
	 * @param string $version The current version.
	 *
	 * @return void
	 */
	public function set_error( $name, $message, $version = \WPSEO_VERSION ) {
		$migration_status = $this->get_migration_status( $name );

		$migration_status['error'] = [
			'time'    => \strtotime( 'now' ),
			'version' => $version,
			'message' => $message,
		];

		$this->set_migration_status( $name, $migration_status );
	}

	/**
	 * Updates the migration version to the latest version.
	 *
	 * @param string $name    The name of the migration.
	 * @param string $version The current version.
	 *
	 * @return void
	 */
	public function set_success( $name, $version = \WPSEO_VERSION ) {
		$migration_status = $this->get_migration_status( $name );
		unset( $migration_status['lock'] );
		unset( $migration_status['error'] );
		$migration_status['version'] = $version;
		$this->set_migration_status( $name, $migration_status );
	}

	/**
	 * Locks the migration status.
	 *
	 * @param string $name The name of the migration.
	 *
	 * @return bool Whether or not the migration was succesfully locked.
	 */
	public function lock_migration( $name ) {
		$migration_status         = $this->get_migration_status( $name );
		$migration_status['lock'] = \strtotime( 'now' );

		return $this->set_migration_status( $name, $migration_status );
	}

	/**
	 * Retrieves the migration option.
	 *
	 * @param string $name The name of the migration.
	 *
	 * @return bool|array The status of the migration, false if no status exists.
	 */
	protected function get_migration_status( $name ) {
		$current_blog_id = \get_current_blog_id();
		if ( ! isset( $this->migration_options[ $current_blog_id ][ $name ] ) ) {
			$migration_status = \get_option( self::MIGRATION_OPTION_KEY . $name );

			if ( ! \is_array( $migration_status ) || ! isset( $migration_status['version'] ) ) {
				$migration_status = [ 'version' => '0.0' ];
			}

			if ( ! isset( $this->migration_options[ $current_blog_id ] ) ) {
				$this->migration_options[ $current_blog_id ] = [];
			}
			$this->migration_options[ $current_blog_id ][ $name ] = $migration_status;
		}

		return $this->migration_options[ $current_blog_id ][ $name ];
	}

	/**
	 * Retrieves the migration option.
	 *
	 * @param string $name             The name of the migration.
	 * @param array  $migration_status The migration status.
	 *
	 * @return bool True if the status was succesfully updated, false otherwise.
	 */
	protected function set_migration_status( $name, $migration_status ) {
		if ( ! \is_array( $migration_status ) || ! isset( $migration_status['version'] ) ) {
			return false;
		}
		$current_blog_id = \get_current_blog_id();

		if ( ! isset( $this->migration_options[ $current_blog_id ] ) ) {
			$this->migration_options[ $current_blog_id ] = [];
		}
		$this->migration_options[ $current_blog_id ][ $name ] = $migration_status;

		return \update_option( self::MIGRATION_OPTION_KEY . $name, $migration_status );
	}
}
badge-group-names.php000066600000002753151120025270010560 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Class Badge_Group_Names.
 *
 * This class defines groups for "new" badges, with the version in which those groups are no longer considered
 * to be "new".
 */
class Badge_Group_Names {

	const GROUP_GLOBAL_TEMPLATES = 'global-templates';

	/**
	 * Constant describing when certain groups of new badges will no longer be shown.
	 */
	const GROUP_NAMES = [
		self::GROUP_GLOBAL_TEMPLATES => '16.7-beta0',
	];

	/**
	 * The current plugin version.
	 *
	 * @var string
	 */
	protected $version;

	/**
	 * Badge_Group_Names constructor.
	 *
	 * @param string|null $version Optional: the current plugin version.
	 */
	public function __construct( $version = null ) {
		if ( ! $version ) {
			$version = \WPSEO_VERSION;
		}
		$this->version = $version;
	}

	/**
	 * Check whether a group of badges is still eligible for a "new" badge.
	 *
	 * @param string      $group           One of the GROUP_* constants.
	 * @param string|null $current_version The current version of the plugin that's being checked.
	 *
	 * @return bool Whether a group of badges is still eligible for a "new" badge.
	 */
	public function is_still_eligible_for_new_badge( $group, $current_version = null ) {
		if ( ! \array_key_exists( $group, $this::GROUP_NAMES ) ) {
			return false;
		}

		$group_version = $this::GROUP_NAMES[ $group ];

		if ( \is_null( $current_version ) ) {
			$current_version = $this->version;
		}

		return (bool) \version_compare( $group_version, $current_version, '>' );
	}
}
researcher-languages.php000066600000000514151120025270011343 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Holds all languages supported with specific researches for our readability analysis.
 */
class Researcher_Languages {

	const SUPPORTED_LANGUAGES = [ 'ar', 'ca', 'de', 'en', 'es', 'fa', 'fr', 'he', 'hu', 'id', 'it', 'nb', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'cs', 'sk', 'el', 'ja' ];
}
wincher-client.php000066600000007513151120025270010175 0ustar00<?php

namespace Yoast\WP\SEO\Config;

use WPSEO_Utils;
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;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Values\OAuth\OAuth_Token;
use Yoast\WP\SEO\Wrappers\WP_Remote_Handler;
use YoastSEO_Vendor\GuzzleHttp\Client;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;

/**
 * Class Wincher_Client
 */
class Wincher_Client extends OAuth_Client {

	/**
	 * The option's key.
	 */
	const TOKEN_OPTION = 'wincher_tokens';

	/**
	 * Name of the temporary PKCE cookie.
	 */
	const PKCE_TRANSIENT_NAME = 'yoast_wincher_pkce';

	/**
	 * The WP_Remote_Handler instance.
	 *
	 * @var WP_Remote_Handler
	 */
	protected $wp_remote_handler;

	/**
	 * Wincher_Client constructor.
	 *
	 * @param Options_Helper    $options_helper    The Options_Helper instance.
	 * @param WP_Remote_Handler $wp_remote_handler The request handler.
	 *
	 * @throws Empty_Property_Exception Exception thrown if a token property is empty.
	 */
	public function __construct(
		Options_Helper $options_helper,
		WP_Remote_Handler $wp_remote_handler
	) {

		$provider = new Wincher_PKCE_Provider(
			[
				'clientId'                => 'yoast',
				'redirectUri'             => 'https://auth.wincher.com/yoast/setup',
				'urlAuthorize'            => 'https://auth.wincher.com/connect/authorize',
				'urlAccessToken'          => 'https://auth.wincher.com/connect/token',
				'urlResourceOwnerDetails' => 'https://api.wincher.com/beta/user',
				'scopes'                  => [ 'profile', 'account', 'websites:read', 'websites:write', 'offline_access' ],
				'scopeSeparator'          => ' ',
				'pkceMethod'              => 'S256',
			],
			[
				'httpClient' => new Client( [ 'handler' => $wp_remote_handler ] ),
			]
		);

		parent::__construct(
			self::TOKEN_OPTION,
			$provider,
			$options_helper
		);
	}

	/**
	 * Return the authorization URL.
	 *
	 * @return string The authentication URL.
	 */
	public function get_authorization_url() {
		$parsed_site_url = \wp_parse_url( \get_site_url() );

		$url = $this->provider->getAuthorizationUrl(
			[
				'state' => WPSEO_Utils::format_json_encode( [ 'domain' => $parsed_site_url['host'] ] ),
			]
		);

		$pkce_code = $this->provider->getPkceCode();

		// Store a transient value with the PKCE code that we need in order to
		// exchange the returned code for a token after authorization.
		\set_transient( self::PKCE_TRANSIENT_NAME, $pkce_code, \DAY_IN_SECONDS );

		return $url;
	}

	/**
	 * Requests the access token and refresh token based on the passed code.
	 *
	 * @param string $code The code to send.
	 *
	 * @return OAuth_Token The requested tokens.
	 *
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 */
	public function request_tokens( $code ) {
		$pkce_code = \get_transient( self::PKCE_TRANSIENT_NAME );
		if ( $pkce_code ) {
			$this->provider->setPkceCode( $pkce_code );
		}
		return parent::request_tokens( $code );
	}

	/**
	 * Performs the specified request.
	 *
	 * @codeCoverageIgnore
	 *
	 * @param string $method  The HTTP method to use.
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	protected function do_request( $method, $url, array $options ) {
		$options['headers'] = [ 'Content-Type' => 'application/json' ];
		return parent::do_request( $method, $url, $options );
	}
}
schema-types.php000066600000010577151120025270007670 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Class Schema_Types.
 */
class Schema_Types {

	/**
	 * Holds the possible schema page types.
	 *
	 * Capitalized in this way so the value can be directly used in the schema output.
	 *
	 * @var string[]
	 */
	const PAGE_TYPES = [
		'WebPage'           => '',
		'ItemPage'          => '',
		'AboutPage'         => '',
		'FAQPage'           => '',
		'QAPage'            => '',
		'ProfilePage'       => '',
		'ContactPage'       => '',
		'MedicalWebPage'    => '',
		'CollectionPage'    => '',
		'CheckoutPage'      => '',
		'RealEstateListing' => '',
		'SearchResultsPage' => '',
	];

	/**
	 * Holds the possible schema article types.
	 *
	 * Capitalized in this way so the value can be directly used in the schema output.
	 *
	 * @var string[]
	 */
	const ARTICLE_TYPES = [
		'Article'                  => '',
		'BlogPosting'              => '',
		'SocialMediaPosting'       => '',
		'NewsArticle'              => '',
		'AdvertiserContentArticle' => '',
		'SatiricalArticle'         => '',
		'ScholarlyArticle'         => '',
		'TechArticle'              => '',
		'Report'                   => '',
		'None'                     => '',
	];

	/**
	 * Gets the page type options.
	 *
	 * @return array[] The schema page type options.
	 */
	public function get_page_type_options() {
		return [
			[
				'name'  => \__( 'Web Page', 'wordpress-seo' ),
				'value' => 'WebPage',
			],
			[
				'name'  => \__( 'Item Page', 'wordpress-seo' ),
				'value' => 'ItemPage',
			],
			[
				'name'  => \__( 'About Page', 'wordpress-seo' ),
				'value' => 'AboutPage',
			],
			[
				'name'  => \__( 'FAQ Page', 'wordpress-seo' ),
				'value' => 'FAQPage',
			],
			[
				'name'  => \__( 'QA Page', 'wordpress-seo' ),
				'value' => 'QAPage',
			],
			[
				'name'  => \__( 'Profile Page', 'wordpress-seo' ),
				'value' => 'ProfilePage',
			],
			[
				'name'  => \__( 'Contact Page', 'wordpress-seo' ),
				'value' => 'ContactPage',
			],
			[
				'name'  => \__( 'Medical Web Page', 'wordpress-seo' ),
				'value' => 'MedicalWebPage',
			],
			[
				'name'  => \__( 'Collection Page', 'wordpress-seo' ),
				'value' => 'CollectionPage',
			],
			[
				'name'  => \__( 'Checkout Page', 'wordpress-seo' ),
				'value' => 'CheckoutPage',
			],
			[
				'name'  => \__( 'Real Estate Listing', 'wordpress-seo' ),
				'value' => 'RealEstateListing',
			],
			[
				'name'  => \__( 'Search Results Page', 'wordpress-seo' ),
				'value' => 'SearchResultsPage',
			],
		];
	}

	/**
	 * Gets the article type options.
	 *
	 * @return array[] The schema article type options.
	 */
	public function get_article_type_options() {
		/**
		 * Filter: 'wpseo_schema_article_types_labels' - Allow developers to filter the available article types and their labels.
		 *
		 * Make sure when you filter this to also filter `wpseo_schema_article_types`.
		 *
		 * @api array $schema_article_types_labels The available schema article types and their labels.
		 */
		return \apply_filters(
			'wpseo_schema_article_types_labels',
			[
				[
					'name'  => \__( 'Article', 'wordpress-seo' ),
					'value' => 'Article',
				],
				[
					'name'  => \__( 'Blog Post', 'wordpress-seo' ),
					'value' => 'BlogPosting',
				],
				[
					'name'  => \__( 'Social Media Posting', 'wordpress-seo' ),
					'value' => 'SocialMediaPosting',
				],
				[
					'name'  => \__( 'News Article', 'wordpress-seo' ),
					'value' => 'NewsArticle',
				],
				[
					'name'  => \__( 'Advertiser Content Article', 'wordpress-seo' ),
					'value' => 'AdvertiserContentArticle',
				],
				[
					'name'  => \__( 'Satirical Article', 'wordpress-seo' ),
					'value' => 'SatiricalArticle',
				],
				[
					'name'  => \__( 'Scholarly Article', 'wordpress-seo' ),
					'value' => 'ScholarlyArticle',
				],
				[
					'name'  => \__( 'Tech Article', 'wordpress-seo' ),
					'value' => 'TechArticle',
				],
				[
					'name'  => \__( 'Report', 'wordpress-seo' ),
					'value' => 'Report',
				],
				[
					'name'  => \__( 'None', 'wordpress-seo' ),
					'value' => 'None',
				],
			]
		);
	}

	/**
	 * Gets the values of the article type options.
	 *
	 * @return array[] The values of the Schema article type options.
	 */
	public function get_article_type_options_values() {
		$article_types       = $this->get_article_type_options();
		$article_type_values = [];

		foreach ( $article_types as $type ) {
			$article_type_values[] = $type['value'];
		}

		return $article_type_values;
	}
}
schema-ids.php000066600000002142151120025270007270 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Class Schema_IDs.
 */
class Schema_IDs {

	/**
	 * Hash used for the Author `@id`.
	 */
	const AUTHOR_HASH = '#author';

	/**
	 * Hash used for the Author Logo's `@id`.
	 */
	const AUTHOR_LOGO_HASH = '#authorlogo';

	/**
	 * Hash used for the Breadcrumb's `@id`.
	 */
	const BREADCRUMB_HASH = '#breadcrumb';

	/**
	 * Hash used for the Person `@id`.
	 */
	const PERSON_HASH = '#/schema/person/';

	/**
	 * Hash used for the Article `@id`.
	 */
	const ARTICLE_HASH = '#article';

	/**
	 * Hash used for the Organization `@id`.
	 */
	const ORGANIZATION_HASH = '#organization';

	/**
	 * Hash used for the Organization `@id`.
	 */
	const ORGANIZATION_LOGO_HASH = '#/schema/logo/image/';

	/**
	 * Hash used for the logo `@id`.
	 */
	const PERSON_LOGO_HASH = '#/schema/person/image/';

	/**
	 * Hash used for an Article's primary image `@id`.
	 */
	const PRIMARY_IMAGE_HASH = '#primaryimage';

	/**
	 * Hash used for the WebPage's `@id`.
	 *
	 * @deprecated 19.3
	 */
	const WEBPAGE_HASH = '';

	/**
	 * Hash used for the Website's `@id`.
	 */
	const WEBSITE_HASH = '#website';
}
migrations/20200617122511_CreateSEOLinksTable.php000066600000004735151120025270014621 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * CreateSEOLinksTable class.
 */
class CreateSEOLinksTable extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();
		$adapter    = $this->get_adapter();

		// The table may already have been created by legacy code.
		// If not, create it exactly as it was.
		if ( ! $adapter->table_exists( $table_name ) ) {
			$table = $this->create_table( $table_name, [ 'id' => false ] );
			$table->column(
				'id',
				'biginteger',
				[
					'primary_key'    => true,
					'limit'          => 20,
					'unsigned'       => true,
					'auto_increment' => true,
				]
			);
			$table->column( 'url', 'string', [ 'limit' => 255 ] );
			$table->column(
				'post_id',
				'biginteger',
				[
					'limit'    => 20,
					'unsigned' => true,
				]
			);
			$table->column(
				'target_post_id',
				'biginteger',
				[
					'limit'    => 20,
					'unsigned' => true,
				]
			);
			$table->column( 'type', 'string', [ 'limit' => 8 ] );
			$table->finish();
		}
		if ( ! $adapter->has_index( $table_name, [ 'post_id', 'type' ], [ 'name' => 'link_direction' ] ) ) {
			$this->add_index( $table_name, [ 'post_id', 'type' ], [ 'name' => 'link_direction' ] );
		}

		// Add these columns outside of the initial table creation as these did not exist on the legacy table.
		$this->add_column( $table_name, 'indexable_id', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'target_indexable_id', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'height', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'width', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'size', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'language', 'string', [ 'limit' => 32 ] );
		$this->add_column( $table_name, 'region', 'string', [ 'limit' => 32 ] );

		$this->add_index( $table_name, [ 'indexable_id', 'type' ], [ 'name' => 'indexable_link_direction' ] );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->drop_table( $this->get_table_name() );
	}

	/**
	 * Returns the SEO Links table name.
	 *
	 * @return string
	 */
	private function get_table_name() {
		return Model::get_table_name( 'SEO_Links' );
	}
}
migrations/20201216141134_ExpandPrimaryTermIDColumnLengths.php000066600000002005151120025270017413 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * ExpandPrimaryTermIDColumnLengths class.
 */
class ExpandPrimaryTermIDColumnLengths extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * The columns to change the column type and length of.
	 *
	 * @var string[]
	 */
	protected static $columns_to_change = [
		'post_id',
		'term_id',
	];

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		foreach ( self::$columns_to_change as $column ) {
			$this->change_column(
				$this->get_table_name(),
				$column,
				'biginteger',
				[ 'limit' => 20 ]
			);
		}
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Primary_Term' );
	}
}
migrations/20171228151841_WpYoastPrimaryTerm.php000066600000002775151120025270014717 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Migration for the Primary Term.
 */
class WpYoastPrimaryTerm extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();

		$indexable_table = $this->create_table( $table_name );

		$indexable_table->column(
			'post_id',
			'integer',
			[
				'unsigned' => true,
				'null'     => false,
				'limit'    => 11,
			]
		);
		$indexable_table->column(
			'term_id',
			'integer',
			[
				'unsigned' => true,
				'null'     => false,
				'limit'    => 11,
			]
		);
		$indexable_table->column(
			'taxonomy',
			'string',
			[
				'null'  => false,
				'limit' => 32,
			]
		);

		// Executes the SQL to create the table.
		$indexable_table->finish();

		$this->add_index(
			$table_name,
			[
				'post_id',
				'taxonomy',
			],
			[
				'name' => 'post_taxonomy',
			]
		);

		$this->add_index(
			$table_name,
			[
				'post_id',
				'term_id',
			],
			[
				'name' => 'post_term',
			]
		);

		$this->add_timestamps( $table_name );
	}

	/**
	 * Migration down.
	 */
	public function down() {
		$this->drop_table( $this->get_table_name() );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string Table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Primary_Term' );
	}
}
migrations/20200430075614_AddIndexableObjectIdAndTypeIndex.php000066600000001665151120025270017266 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class AddIndexableObjectIdAndTypeIndex.
 */
class AddIndexableObjectIdAndTypeIndex extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$this->add_index(
			$this->get_table_name(),
			[
				'object_id',
				'object_type',
			],
			[
				'name' => 'object_id_and_type',
			]
		);
	}

	/**
	 * Migration down.
	 */
	public function down() {
		$this->remove_index(
			$this->get_table_name(),
			[
				'object_id',
				'object_type',
			],
			[
				'name' => 'object_id_and_type',
			]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200616130143_ReplacePermalinkHashIndex.php000066600000004365151120025270016106 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * ReplacePermalinkHashIndex class.
 */
class ReplacePermalinkHashIndex extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();
		$adapter    = $this->get_adapter();

		if ( ! $adapter->has_table( $table_name ) ) {
			return;
		}

		$this->change_column(
			$table_name,
			'permalink_hash',
			'string',
			[
				'null'  => true,
				'limit' => 40,
			]
		);

		if ( $adapter->has_index( $table_name, [ 'permalink_hash' ], [ 'name' => 'permalink_hash' ] ) ) {
			$this->remove_index(
				$table_name,
				[
					'permalink_hash',
				],
				[
					'name' => 'permalink_hash',
				]
			);
		}

		if ( ! $adapter->has_index( $table_name, [ 'permalink_hash', 'object_type' ], [ 'name' => 'permalink_hash_and_object_type' ] ) ) {
			$this->add_index(
				$table_name,
				[
					'permalink_hash',
					'object_type',
				],
				[
					'name' => 'permalink_hash_and_object_type',
				]
			);
		}
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$table_name = $this->get_table_name();
		$adapter    = $this->get_adapter();

		if ( ! $adapter->has_table( $table_name ) ) {
			return;
		}

		if ( $adapter->has_index( $table_name, [ 'permalink_hash', 'object_type' ], [ 'name' => 'permalink_hash_and_object_type' ] ) ) {
			$this->remove_index(
				$table_name,
				[
					'permalink_hash',
					'object_type',
				],
				[
					'name' => 'permalink_hash_and_object_type',
				]
			);
		}

		$this->change_column(
			$table_name,
			'permalink_hash',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);

		if ( ! $adapter->has_index( $table_name, [ 'permalink_hash' ], [ 'name' => 'permalink_hash' ] ) ) {
			$this->add_index(
				$table_name,
				[
					'permalink_hash',
				],
				[
					'name' => 'permalink_hash',
				]
			);
		}
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200428194858_ExpandIndexableColumnLengths.php000066600000003341151120025270016653 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class ExpandIndexableColumnLengths.
 */
class ExpandIndexableColumnLengths extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$this->change_column( $this->get_table_name(), 'title', 'text', [ 'null' => true ] );
		$this->change_column( $this->get_table_name(), 'open_graph_title', 'text', [ 'null' => true ] );
		$this->change_column( $this->get_table_name(), 'twitter_title', 'text', [ 'null' => true ] );
		$this->change_column( $this->get_table_name(), 'open_graph_image_source', 'text', [ 'null' => true ] );
		$this->change_column( $this->get_table_name(), 'twitter_image_source', 'text', [ 'null' => true ] );
	}

	/**
	 * Migration down.
	 */
	public function down() {
		$attr_limit_191 = [
			'null'  => true,
			'limit' => 191,
		];

		$this->change_column(
			$this->get_table_name(),
			'title',
			'string',
			$attr_limit_191
		);
		$this->change_column(
			$this->get_table_name(),
			'opengraph_title',
			'string',
			$attr_limit_191
		);
		$this->change_column(
			$this->get_table_name(),
			'twitter_title',
			'string',
			$attr_limit_191
		);
		$this->change_column(
			$this->get_table_name(),
			'open_graph_image_source',
			'string',
			$attr_limit_191
		);
		$this->change_column(
			$this->get_table_name(),
			'twitter_image_source',
			'string',
			$attr_limit_191
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200428123747_BreadcrumbTitleAndHierarchyReset.php000066600000002304151120025270017435 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class BreadcrumbTitleAndHierarchyReset.
 */
class BreadcrumbTitleAndHierarchyReset extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$this->change_column( $this->get_indexable_table_name(), 'breadcrumb_title', 'text', [ 'null' => true ] );
		$this->query( 'DELETE FROM ' . $this->get_indexable_hierarchy_table_name() );
	}

	/**
	 * Migration down.
	 */
	public function down() {
		$this->change_column(
			$this->get_indexable_table_name(),
			'breadcrumb_title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_table_name() {
		return Model::get_table_name( 'Indexable' );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_hierarchy_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20200507054848_DeleteDuplicateIndexables.php000066600000002041151120025270016135 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class DeleteDuplicateIndexables.
 */
class DeleteDuplicateIndexables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$table_name = $this->get_table_name();

		/*
		 * Deletes duplicate indexables that have the same object_id and object_type.
		 * The rows with a higher ID are deleted as those should be unused and could be outdated.
		 */
		$this->query( 'DELETE wyi FROM ' . $table_name . ' wyi INNER JOIN ' . $table_name . ' wyi2 WHERE wyi2.object_id = wyi.object_id AND wyi2.object_type = wyi.object_type AND wyi2.id < wyi.id;' );
	}

	/**
	 * Migration down.
	 */
	public function down() {
		// Nothing to do.
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20191011111109_WpYoastIndexableHierarchy.php000066600000003020151120025270016137 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class WpYoastIndexableHierarchy.
 */
class WpYoastIndexableHierarchy extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$table_name = $this->get_table_name();

		$indexable_table = $this->create_table( $table_name, [ 'id' => false ] );

		$indexable_table->column(
			'indexable_id',
			'integer',
			[
				'primary_key' => true,
				'unsigned'    => true,
				'null'        => true,
				'limit'       => 11,
			]
		);
		$indexable_table->column(
			'ancestor_id',
			'integer',
			[
				'primary_key' => true,
				'unsigned'    => true,
				'null'        => true,
				'limit'       => 11,
			]
		);
		$indexable_table->column(
			'depth',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'limit'    => 11,
			]
		);
		$indexable_table->finish();

		$this->add_index( $table_name, 'indexable_id', [ 'name' => 'indexable_id' ] );
		$this->add_index( $table_name, 'ancestor_id', [ 'name' => 'ancestor_id' ] );
		$this->add_index( $table_name, 'depth', [ 'name' => 'depth' ] );
	}

	/**
	 * Migration up.
	 */
	public function down() {
		$this->drop_table( $this->get_table_name() );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20211020091404_AddObjectTimestamps.php000066600000003007151120025270014747 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * AddObjectTimestamps class.
 */
class AddObjectTimestamps extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->add_column(
			$this->get_table_name(),
			'object_last_modified',
			'datetime',
			[
				'null'    => true,
				'default' => null,
			]
		);
		$this->add_column(
			$this->get_table_name(),
			'object_published_at',
			'datetime',
			[
				'null'    => true,
				'default' => null,
			]
		);
		$this->add_index(
			$this->get_table_name(),
			[
				'object_published_at',
				'is_robots_noindex',
				'object_type',
				'object_sub_type',
			],
			[
				'name' => 'published_sitemap_index',
			]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->remove_column( $this->get_table_name(), 'object_last_modified' );
		$this->remove_column( $this->get_table_name(), 'object_published_at' );
		$this->remove_index(
			$this->get_table_name(),
			[
				'object_published_at',
				'is_robots_noindex',
				'object_type',
				'object_sub_type',
			],
			[
				'name' => 'published_sitemap_index',
			]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200430150130_ClearIndexableTables.php000066600000002005151120025270015046 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class ClearIndexableTables.
 */
class ClearIndexableTables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_table_name() );
		$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_hierarchy_table_name() );
	}

	/**
	 * Migration down.
	 */
	public function down() {
		// Nothing to do.
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_table_name() {
		return Model::get_table_name( 'Indexable' );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_hierarchy_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20200702141921_CreateIndexableSubpagesIndex.php000066600000002354151120025270016573 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * CreateIndexableSubpagesIndex class.
 */
class CreateIndexableSubpagesIndex extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->change_column(
			$this->get_table_name(),
			'post_status',
			'string',
			[
				'null'  => true,
				'limit' => 20,
			]
		);
		$this->add_index(
			$this->get_table_name(),
			[ 'post_parent', 'object_type', 'post_status', 'object_id' ],
			[ 'name' => 'subpages' ]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->change_column(
			$this->get_table_name(),
			'post_status',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$this->remove_index(
			$this->get_table_name(),
			[ 'post_parent', 'object_type', 'post_status', 'object_id' ],
			[ 'name' => 'subpages' ]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200408101900_AddCollationToTables.php000066600000001624151120025270015060 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class AddCollationToTables.
 */
class AddCollationToTables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		global $wpdb;

		$charset_collate = $wpdb->get_charset_collate();
		if ( empty( $charset_collate ) ) {
			return;
		}

		$tables = [
			Model::get_table_name( 'migrations' ),
			Model::get_table_name( 'Indexable' ),
			Model::get_table_name( 'Indexable_Hierarchy' ),
			Model::get_table_name( 'Primary_Term' ),
		];

		foreach ( $tables as $table ) {
			$this->query( 'ALTER TABLE ' . $table . ' CONVERT TO ' . \str_replace( 'DEFAULT ', '', $charset_collate ) );
		}
	}

	/**
	 * Migration down.
	 */
	public function down() {
		// No down required.
	}
}
migrations/20200429105310_TruncateIndexableTables.php000066600000002013151120025270015614 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class TruncateIndexableTables.
 */
class TruncateIndexableTables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_table_name() );
		$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_hierarchy_table_name() );
	}

	/**
	 * Migration down.
	 */
	public function down() {
		// Nothing to do.
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_table_name() {
		return Model::get_table_name( 'Indexable' );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_hierarchy_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20201216124002_ExpandIndexableIDColumnLengths.php000066600000002023151120025270017026 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * ExpandIndexableIDColumnLengths class.
 */
class ExpandIndexableIDColumnLengths extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * The columns to change the column type and length of.
	 *
	 * @var string[]
	 */
	protected static $columns_to_change = [
		'object_id',
		'author_id',
		'post_parent',
	];

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		foreach ( self::$columns_to_change as $column ) {
			$this->change_column(
				$this->get_table_name(),
				$column,
				'biginteger',
				[ 'limit' => 20 ]
			);
		}
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20210817092415_AddVersionColumnToIndexables.php000066600000001547151120025270016626 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * AddVersionColumnToIndexables class.
 */
class AddVersionColumnToIndexables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->add_column(
			$this->get_table_name(),
			'version',
			'integer',
			[
				'default'  => 1,
			]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->remove_column(
			$this->get_table_name(),
			'version'
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200513133401_ResetIndexableHierarchyTable.php000066600000001314151120025270016564 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class ResetIndexableHierarchyTable.
 */
class ResetIndexableHierarchyTable extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$this->query( 'TRUNCATE TABLE ' . $this->get_table_name() );
	}

	/**
	 * Migration down.
	 */
	public function down() {
		// Nothing to do.
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20171228151840_WpYoastIndexable.php000066600000014204151120025270014324 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Indexable migration.
 */
class WpYoastIndexable extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->add_table();
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->drop_table( $this->get_table_name() );
	}

	/**
	 * Creates the indexable table.
	 */
	private function add_table() {
		$table_name = $this->get_table_name();

		$indexable_table = $this->create_table( $table_name );

		// Permalink.
		$indexable_table->column( 'permalink', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column(
			'permalink_hash',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);

		// Object information.
		$indexable_table->column(
			'object_id',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'limit'    => 11,
			]
		);
		$indexable_table->column(
			'object_type',
			'string',
			[
				'null'  => false,
				'limit' => 32,
			]
		);
		$indexable_table->column(
			'object_sub_type',
			'string',
			[
				'null'  => true,
				'limit' => 32,
			]
		);

		// Ownership.
		$indexable_table->column(
			'author_id',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'limit'    => 11,
			]
		);
		$indexable_table->column(
			'post_parent',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'limit'    => 11,
			]
		);

		// Title and description.
		$indexable_table->column(
			'title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column( 'description', 'text', [ 'null' => true ] );
		$indexable_table->column(
			'breadcrumb_title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);

		// Post metadata: status, public, protected.
		$indexable_table->column(
			'post_status',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column(
			'is_public',
			'boolean',
			[
				'null'    => true,
				'default' => null,
			]
		);
		$indexable_table->column( 'is_protected', 'boolean', [ 'default' => false ] );
		$indexable_table->column(
			'has_public_posts',
			'boolean',
			[
				'null'    => true,
				'default' => null,
			]
		);

		$indexable_table->column(
			'number_of_pages',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'default'  => null,
				'limit'    => 11,
			]
		);

		$indexable_table->column( 'canonical', 'mediumtext', [ 'null' => true ] );

		// SEO and readability analysis.
		$indexable_table->column(
			'primary_focus_keyword',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column(
			'primary_focus_keyword_score',
			'integer',
			[
				'null'  => true,
				'limit' => 3,
			]
		);
		$indexable_table->column(
			'readability_score',
			'integer',
			[
				'null'  => true,
				'limit' => 3,
			]
		);
		$indexable_table->column( 'is_cornerstone', 'boolean', [ 'default' => false ] );

		// Robots.
		$indexable_table->column(
			'is_robots_noindex',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);
		$indexable_table->column(
			'is_robots_nofollow',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);
		$indexable_table->column(
			'is_robots_noarchive',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);
		$indexable_table->column(
			'is_robots_noimageindex',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);
		$indexable_table->column(
			'is_robots_nosnippet',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);

		// Twitter.
		$indexable_table->column(
			'twitter_title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column( 'twitter_image', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column( 'twitter_description', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column(
			'twitter_image_id',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column(
			'twitter_image_source',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);

		// Open-Graph.
		$indexable_table->column(
			'open_graph_title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column( 'open_graph_description', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column( 'open_graph_image', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column(
			'open_graph_image_id',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column(
			'open_graph_image_source',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column( 'open_graph_image_meta', 'text', [ 'null' => true ] );

		// Link count.
		$indexable_table->column(
			'link_count',
			'integer',
			[
				'null'  => true,
				'limit' => 11,
			]
		);
		$indexable_table->column(
			'incoming_link_count',
			'integer',
			[
				'null'  => true,
				'limit' => 11,
			]
		);

		// Prominent words.
		$indexable_table->column(
			'prominent_words_version',
			'integer',
			[
				'null'     => true,
				'limit'    => 11,
				'unsigned' => true,
				'default'  => null,
			]
		);

		$indexable_table->finish();

		$this->add_indexes( $table_name );

		$this->add_timestamps( $table_name );
	}

	/**
	 * Adds indexes to the indexable table.
	 *
	 * @param string $indexable_table_name The name of the indexable table.
	 */
	private function add_indexes( $indexable_table_name ) {
		$this->add_index(
			$indexable_table_name,
			[
				'object_type',
				'object_sub_type',
			],
			[
				'name' => 'object_type_and_sub_type',
			]
		);

		$this->add_index(
			$indexable_table_name,
			'permalink_hash',
			[
				'name' => 'permalink_hash',
			]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20190529075038_WpYoastDropIndexableMetaTableIfExists.php000066600000001521151120025270020416 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class WpYoastDropIndexableMetaTableIfExists.
 */
class WpYoastDropIndexableMetaTableIfExists extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$table_name = $this->get_table_name();

		// This can be done safely as it executes a DROP IF EXISTS.
		$this->drop_table( $table_name );
	}

	/**
	 * Migration down.
	 */
	public function down() {
		// No down required. This specific table should never exist.
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable_Meta' );
	}
}
migrations/20200728095334_AddIndexesForProminentWordsOnIndexables.php000066600000002265151120025270020777 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * AddIndexesForProminentWordsOnIndexables class.
 */
class AddIndexesForProminentWordsOnIndexables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * The columns on which an index should be added.
	 *
	 * @var string[]
	 */
	private $columns_with_index = [
		'prominent_words_version',
		'object_type',
		'object_sub_type',
		'post_status',
	];

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();
		$adapter    = $this->get_adapter();

		if ( ! $adapter->has_index( $table_name, $this->columns_with_index, [ 'name' => 'prominent_words' ] ) ) {
			$this->add_index(
				$table_name,
				$this->columns_with_index,
				[
					'name' => 'prominent_words',
				]
			);
		}
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200609154515_AddHasAncestorsColumn.php000066600000001607151120025270015267 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;
use Yoast\WP\SEO\WordPress\Wrapper;

/**
 * Class AddHasAncestorsColumn.
 */
class AddHasAncestorsColumn extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$this->add_column(
			Model::get_table_name( 'Indexable' ),
			'has_ancestors',
			'boolean',
			[
				'default' => false,
			]
		);

		Wrapper::get_wpdb()->query(
			'
			UPDATE ' . Model::get_table_name( 'Indexable' ) . '
			SET has_ancestors = 1
			WHERE id IN ( SELECT indexable_id FROM ' . Model::get_table_name( 'Indexable_Hierarchy' ) . ' )
			'
		);
	}

	/**
	 * Migration down.
	 */
	public function down() {
		$this->remove_column( Model::get_table_name( 'Indexable' ), 'has_ancestors' );
	}
}
migrations/20201202144329_AddEstimatedReadingTime.php000066600000001703151120025270015531 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * AddEstimatedReadingTime class.
 */
class AddEstimatedReadingTime extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();

		$this->add_column(
			$table_name,
			'estimated_reading_time_minutes',
			'integer',
			[
				'null'     => true,
				'default'  => null,
			]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$table_name = $this->get_table_name();

		$this->remove_column( $table_name, 'estimated_reading_time_minutes' );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200420073606_AddColumnsToIndexables.php000066600000004052151120025270015423 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class AddColumnsToIndexables.
 */
class AddColumnsToIndexables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 */
	public function up() {
		$tables  = $this->get_tables();
		$blog_id = \get_current_blog_id();
		foreach ( $tables as $table ) {
			$this->add_column(
				$table,
				'blog_id',
				'biginteger',
				[
					'null'    => false,
					'limit'   => 20,
					'default' => $blog_id,
				]
			);
		}

		$attr_limit_32 = [
			'null'  => true,
			'limit' => 32,
		];
		$attr_limit_64 = [
			'null'  => true,
			'limit' => 64,
		];

		$indexable_table = $this->get_indexable_table();
		$this->add_column( $indexable_table, 'language', 'string', $attr_limit_32 );
		$this->add_column( $indexable_table, 'region', 'string', $attr_limit_32 );
		$this->add_column( $indexable_table, 'schema_page_type', 'string', $attr_limit_64 );
		$this->add_column( $indexable_table, 'schema_article_type', 'string', $attr_limit_64 );
	}

	/**
	 * Migration down.
	 */
	public function down() {
		$tables = $this->get_tables();
		foreach ( $tables as $table ) {
			$this->remove_column( $table, 'blog_id' );
		}

		$indexable_table = $this->get_indexable_table();
		$this->remove_column( $indexable_table, 'language' );
		$this->remove_column( $indexable_table, 'region' );
		$this->remove_column( $indexable_table, 'schema_page_type' );
		$this->remove_column( $indexable_table, 'schema_article_type' );
	}

	/**
	 * Retrieves the Indexable table.
	 *
	 * @return string The Indexable table name.
	 */
	protected function get_indexable_table() {
		return Model::get_table_name( 'Indexable' );
	}

	/**
	 * Retrieves the table names to use.
	 *
	 * @return string[] The table names to use.
	 */
	protected function get_tables() {
		return [
			$this->get_indexable_table(),
			Model::get_table_name( 'Indexable_Hierarchy' ),
			Model::get_table_name( 'Primary_Term' ),
		];
	}
}
cart.php000066600000002611151137606660006225 0ustar00<?php

/**
 * Cart config
 *
 * Class Customify_WC_Cart.
 *
 * @since 0.2.2
 */
class Customify_WC_Cart {
	public function __construct() {
		add_filter( 'customify/customizer/config', array( $this, 'config' ), 100 );
		if ( is_admin() || is_customize_preview() ) {
			add_filter( 'Customify_Control_Args', array( $this, 'add_cart_url' ), 35 );
		}

		add_action( 'wp', array( $this, 'cart_hooks' ) );
	}

	public function cart_hooks() {
		if ( ! is_cart() ) {
			return;
		}

		$hide_cross_sell = Customify()->get_setting( 'wc_cart_page_hide_cross_sells' );
		if ( $hide_cross_sell ) {
			remove_action( 'woocommerce_cart_collaterals', 'woocommerce_cross_sell_display' );
			remove_action( 'woocommerce_after_cart_table', 'woocommerce_cross_sell_display' );
		}

	}

	public function add_cart_url( $args ) {
		$args['section_urls']['wc_cart_page'] = get_permalink( wc_get_page_id( 'cart' ) );

		return $args;
	}

	public function config( $configs ) {
		$section = 'wc_cart_page';

		$configs[] = array(
			'name'  => $section,
			'type'  => 'section',
			'panel' => 'woocommerce',
			'title' => __( 'Cart', 'customify' ),
		);

		$configs[] = array(
			'name'           => "{$section}_hide_cross_sells",
			'type'           => 'checkbox',
			'default'        => 1,
			'section'        => $section,
			'checkbox_label' => __( 'Hide cross-sells', 'customify' ),
		);

		return $configs;
	}
}

new Customify_WC_Cart();
colors.php000066600000004323151137606660006577 0ustar00<?php

class Customify_WC_Colors {
	function __construct() {
		add_filter( 'customify/customizer/config', array( $this, 'config' ), 100 );
	}

	function config( $configs ) {
		$section = 'global_styling';

		$configs[] = array(
			'name'    => "{$section}_shop_colors_heading",
			'type'    => 'heading',
			'section' => $section,
			'title'   => __( 'Shop Colors', 'customify' ),
		);

		$configs[] = array(
			'name'        => "{$section}_shop_primary",
			'type'        => 'color',
			'section'     => $section,
			'title'       => __( 'Shop Buttons', 'customify' ),
			'placeholder' => '#c3512f',
			'description' => __( 'Color for add to cart, checkout buttons. Default is Secondary Color.', 'customify' ),
			'css_format'  => apply_filters(
				'customify/styling/shop-buttons',
				'
					.woocommerce .button.add_to_cart_button, 
					.woocommerce .button.alt,
					.woocommerce .button.added_to_cart, 
					.woocommerce .button.checkout, 
					.woocommerce .button.product_type_variable,
					.item--wc_cart .cart-icon .cart-qty .customify-wc-total-qty
					{
					    background-color: {{value}};
					}'
			),
			'selector'    => 'format',
		);

		$configs[] = array(
			'name'        => "{$section}_shop_rating_stars",
			'type'        => 'color',
			'section'     => $section,
			'title'       => __( 'Rating Stars', 'customify' ),
			'description' => __( 'Color for rating stars, default is Secondary Color.', 'customify' ),
			'placeholder' => '#c3512f',
			'css_format'  => apply_filters(
				'customify/styling/shop-rating-stars',
				'
					.comment-form-rating a, 
					.star-rating,
					.comment-form-rating a:hover, 
					.comment-form-rating a:focus, 
					.star-rating:hover, 
					.star-rating:focus
					{
					    color: {{value}};
					}'
			),
			'selector'    => 'format',
		);

		$configs[] = array(
			'name'        => "{$section}_shop_onsale",
			'type'        => 'color',
			'section'     => $section,
			'title'       => __( 'On Sale', 'customify' ),
			'placeholder' => '#77a464',
			'css_format'  => apply_filters(
				'customify/styling/shop-onsale',
				'
					span.onsale
					{
					    background-color: {{value}};
					}'
			),
			'selector'    => 'format',
		);

		return $configs;
	}
}

new Customify_WC_Colors();
header/cart.php000066600000031740151137606660007462 0ustar00<?php

class Customify_Builder_Item_WC_Cart {
	/**
	 * @var string Item Id.
	 */
	public $id = 'wc_cart'; // Required.
	/**
	 * @var string Section ID.
	 */
	public $section = 'wc_cart'; // Optional.
	/**
	 * @var string Item Name.
	 */
	public $name = 'wc_cart'; // Optional.
	/**
	 * @var string|void Item label.
	 */
	public $label = ''; // Optional.
	/**
	 * @var int Priority.
	 */
	public $priority = 200;
	/**
	 * @var string Panel ID.
	 */
	public $panel = 'header_settings';

	/**
	 * Optional construct
	 *
	 * Customify_Builder_Item_HTML constructor.
	 */
	public function __construct() {
		$this->label = __( 'Shopping Cart', 'customify' );
	}

	/**
	 * Register Builder item
	 *
	 * @return array
	 */
	public function item() {
		return array(
			'name'    => $this->label,
			'id'      => $this->id,
			'col'     => 0,
			'width'   => '4',
			'section' => $this->section, // Customizer section to focus when click settings.
		);
	}

	/**
	 * Optional, Register customize section and panel.
	 *
	 * @return array
	 */
	function customize() {
		$fn     = array( $this, 'render' );
		$config = array(
			array(
				'name'     => $this->section,
				'type'     => 'section',
				'panel'    => $this->panel,
				'priority' => $this->priority,
				'title'    => $this->label,
			),

			array(
				'name'            => "{$this->name}_text",
				'type'            => 'text',
				'section'         => $this->section,
				'selector'        => '.builder-header-' . $this->id . '-item',
				'render_callback' => $fn,
				'title'           => __( 'Label', 'customify' ),
				'default'         => __( 'Cart', 'customify' ),
			),

			array(
				'name'            => "{$this->name}_icon",
				'type'            => 'icon',
				'section'         => $this->section,
				'selector'        => '.builder-header-' . $this->id . '-item',
				'render_callback' => $fn,
				'default'         => array(
					'icon' => 'fa fa-shopping-basket',
					'type' => 'font-awesome',
				),
				'title'           => __( 'Icon', 'customify' ),
			),

			array(
				'name'            => "{$this->name}_icon_position",
				'type'            => 'select',
				'section'         => $this->section,
				'selector'        => '.builder-header-' . $this->id . '-item',
				'render_callback' => $fn,
				'default'         => 'after',
				'choices'         => array(
					'before' => __( 'Before', 'customify' ),
					'after'  => __( 'After', 'customify' ),
				),
				'title'           => __( 'Icon Position', 'customify' ),
			),

			array(
				'name'            => "{$this->name}_link_to",
				'type'            => 'select',
				'section'         => $this->section,
				'selector'        => '.builder-header-' . $this->id . '-item',
				'render_callback' => $fn,
				'default'         => 'cart',
				'choices'         => array(
					'cart'     => __( 'Cart Page', 'customify' ),
					'checkout' => __( 'Checkout', 'customify' ),
				),
				'title'           => __( 'Link To', 'customify' ),
			),

			array(
				'name'            => "{$this->name}_show_label",
				'type'            => 'checkbox',
				'default'         => array(
					'desktop' => 1,
					'tablet'  => 1,
					'mobile'  => 0,
				),
				'section'         => $this->section,
				'selector'        => '.builder-header-' . $this->id . '-item',
				'render_callback' => $fn,
				'theme_supports'  => '',
				'label'           => __( 'Show Label', 'customify' ),
				'checkbox_label'  => __( 'Show Label', 'customify' ),
				'device_settings' => true,
			),

			array(
				'name'            => "{$this->name}_show_sub_total",
				'type'            => 'checkbox',
				'section'         => $this->section,
				'selector'        => '.builder-header-' . $this->id . '-item',
				'render_callback' => $fn,
				'theme_supports'  => '',
				'label'           => __( 'Sub Total', 'customify' ),
				'checkbox_label'  => __( 'Show Sub Total', 'customify' ),
				'device_settings' => true,
				'default'         => array(
					'desktop' => 1,
					'tablet'  => 1,
					'mobile'  => 0,
				),
			),

			array(
				'name'            => "{$this->name}_show_qty",
				'type'            => 'checkbox',
				'section'         => $this->section,
				'selector'        => '.builder-header-' . $this->id . '-item',
				'render_callback' => $fn,
				'default'         => 1,
				'label'           => __( 'Quantity', 'customify' ),
				'checkbox_label'  => __( 'Show Quantity', 'customify' ),
			),

			array(
				'name'            => "{$this->name}_sep",
				'type'            => 'text',
				'section'         => $this->section,
				'selector'        => '.builder-header-' . $this->id . '-item',
				'render_callback' => $fn,
				'title'           => __( 'Separator', 'customify' ),
				'default'         => __( '/', 'customify' ),
			),

			array(
				'name'       => "{$this->name}_label_styling",
				'type'       => 'styling',
				'section'    => $this->section,
				'title'      => __( 'Styling', 'customify' ),
				'selector'   => array(
					'normal' => '.builder-header-' . $this->id . '-item .cart-item-link',
					'hover'  => '.builder-header-' . $this->id . '-item:hover .cart-item-link',
				),
				'css_format' => 'styling',
				'default'    => array(),
				'fields'     => array(
					'normal_fields' => array(
						'link_color'    => false, // disable for special field.
						'margin'        => false,
						'bg_image'      => false,
						'bg_cover'      => false,
						'bg_position'   => false,
						'bg_repeat'     => false,
						'bg_attachment' => false,
					),
					'hover_fields'  => array(
						'link_color' => false, // disable for special field.
					),
				),
			),

			array(
				'name'       => "{$this->name}_typography",
				'type'       => 'typography',
				'section'    => $this->section,
				'title'      => __( 'Typography', 'customify' ),
				'selector'   => '.builder-header-' . $this->id . '-item',
				'css_format' => 'typography',
				'default'    => array(),
			),

			array(
				'name'    => "{$this->name}_icon_h",
				'type'    => 'heading',
				'section' => $this->section,
				'title'   => __( 'Icon Settings', 'customify' ),
			),

			array(
				'name'            => "{$this->name}_icon_size",
				'type'            => 'slider',
				'section'         => $this->section,
				'device_settings' => true,
				'max'             => 150,
				'title'           => __( 'Icon Size', 'customify' ),
				'selector'        => '.builder-header-' . $this->id . '-item .cart-icon i:before',
				'css_format'      => 'font-size: {{value}};',
				'default'         => array(),
			),

			array(
				'name'        => "{$this->name}_icon_styling",
				'type'        => 'styling',
				'section'     => $this->section,
				'title'       => __( 'Styling', 'customify' ),
				'description' => __( 'Advanced styling for cart icon', 'customify' ),
				'selector'    => array(
					'normal' => '.builder-header-' . $this->id . '-item .cart-item-link .cart-icon i',
					'hover'  => '.builder-header-' . $this->id . '-item:hover .cart-item-link .cart-icon i',
				),
				'css_format'  => 'styling',
				'default'     => array(),
				'fields'      => array(
					'normal_fields' => array(
						'link_color'    => false, // disable for special field.
						'bg_image'      => false,
						'bg_cover'      => false,
						'bg_position'   => false,
						'bg_repeat'     => false,
						'bg_attachment' => false,
					),
					'hover_fields'  => array(
						'link_color' => false, // disable for special field.
					),
				),
			),

			array(
				'name'        => "{$this->name}_qty_styling",
				'type'        => 'styling',
				'section'     => $this->section,
				'title'       => __( 'Quantity', 'customify' ),
				'description' => __( 'Advanced styling for cart quantity', 'customify' ),
				'selector'    => array(
					'normal' => '.builder-header-' . $this->id . '-item  .cart-icon .cart-qty .customify-wc-total-qty',
					'hover'  => '.builder-header-' . $this->id . '-item:hover .cart-icon .cart-qty .customify-wc-total-qty',
				),
				'css_format'  => 'styling',
				'default'     => array(),
				'fields'      => array(
					'normal_fields' => array(
						'link_color'    => false, // disable for special field.
						'bg_image'      => false,
						'bg_cover'      => false,
						'bg_position'   => false,
						'bg_repeat'     => false,
						'bg_attachment' => false,
					),
					'hover_fields'  => array(
						'link_color' => false, // disable for special field.
					),
				),
			),

			array(
				'name'    => "{$this->name}_d_h",
				'type'    => 'heading',
				'section' => $this->section,
				'title'   => __( 'Dropdown Settings', 'customify' ),
			),

			array(
				'name'            => "{$this->name}_d_align",
				'type'            => 'select',
				'section'         => $this->section,
				'title'           => __( 'Dropdown Alignment', 'customify' ),
				'selector'        => '.builder-header-' . $this->id . '-item',
				'render_callback' => $fn,
				'default'         => array(),
				'choices'         => array(
					'left'  => __( 'Left', 'customify' ),
					'right' => __( 'Right', 'customify' ),
				),
			),

			array(
				'name'            => "{$this->name}_d_width",
				'type'            => 'slider',
				'section'         => $this->section,
				'device_settings' => true,
				'min'             => 280,
				'max'             => 600,
				'title'           => __( 'Dropdown Width', 'customify' ),
				'selector'        => '.builder-header-' . $this->id . '-item  .cart-dropdown-box',
				'css_format'      => 'width: {{value}};',
				'default'         => array(),
			),

		);

		// Item Layout.
		return array_merge( $config, customify_header_layout_settings( $this->id, $this->section ) );
	}

	function array_to_class( $array, $prefix ) {
		if ( ! is_array( $array ) ) {
			return $prefix . '-' . $array;
		}
		$classes = array();
		$array   = array_reverse( $array );
		foreach ( $array as $k => $v ) {
			if ( 1 == $v ) {
				$v = 'show';
			} elseif ( 0 == $v ) {
				$v = 'hide';
			}
			$classes[] = "{$prefix}-{$k}-{$v}";
		}

		return join( ' ', $classes );
	}

	/**
	 * Optional. Render item content
	 */
	public function render() {
		$icon          = Customify()->get_setting( "{$this->name}_icon" );
		$icon_position = Customify()->get_setting( "{$this->name}_icon_position" );
		$text          = Customify()->get_setting( "{$this->name}_text" );

		$show_label     = Customify()->get_setting( "{$this->name}_show_label", 'all' );
		$show_sub_total = Customify()->get_setting( "{$this->name}_show_sub_total", 'all' );
		$show_qty       = Customify()->get_setting( "{$this->name}_show_qty" );
		$sep            = Customify()->get_setting( "{$this->name}_sep" );
		$link_to        = Customify()->get_setting( "{$this->name}_link_to" );

		$classes = array();

		$align = Customify()->get_setting( "{$this->name}_d_align" );
		if ( ! $align ) {
			$align = 'right';
		}
		$classes[] = $this->array_to_class( $align, 'd-align' );

		$label_classes    = $this->array_to_class( $show_label, 'wc-cart' );
		$subtotal_classes = $this->array_to_class( $show_sub_total, 'wc-cart' );

		$icon = wp_parse_args(
			$icon,
			array(
				'type' => '',
				'icon' => '',
			)
		);

		$icon_html = '';
		if ( $icon['icon'] ) {
			$icon_html = '<i class="' . esc_attr( $icon['icon'] ) . '"></i> ';
		}

		if ( $text ) {
			$text = '<span class="cart-text cart-label ' . esc_attr( $label_classes ) . '">' . sanitize_text_field( $text ) . '</span>';
		}

		$sub_total  = WC()->cart->get_cart_subtotal();
		$quantities = WC()->cart->get_cart_item_quantities();

		$html = $text;

		if ( $sep && $html ) {
			$html .= '<span class="cart-sep cart-label ' . esc_attr( $label_classes ) . '">' . sanitize_text_field( $sep ) . '</span>';
		}
		$html .= '<span class="cart-subtotal cart-label ' . esc_attr( $subtotal_classes ) . '"><span class="customify-wc-sub-total">' . $sub_total . '</span></span>';

		$qty   = array_sum( $quantities );
		$class = 'customify-wc-total-qty';
		if ( $qty <= 0 ) {
			$class .= ' hide-qty';
		}

		if ( $icon_html ) {
			$icon_html = '<span class="cart-icon">' . $icon_html;
			if ( $show_qty ) {
				$icon_html .= '<span class="cart-qty"><span class="' . $class . '">' . array_sum( $quantities ) . '</span></span>';
			}
			$icon_html .= '</span>';
		}

		if ( 'before' == $icon_position ) {
			$html = $icon_html . $html;
		} else {
			$html = $html . $icon_html;
		}

		$classes[] = 'builder-header-' . $this->id . '-item';
		$classes[] = 'item--' . $this->id;

		$link = '';
		if ( 'checkout' == $link_to ) {
			$link = get_permalink( wc_get_page_id( 'checkout' ) );
		} else {
			$link = get_permalink( wc_get_page_id( 'cart' ) );
		}

		echo '<div class="' . esc_attr( join( ' ', $classes ) ) . '">';

		echo '<a href="' . esc_url( $link ) . '" class="cart-item-link text-uppercase text-small link-meta">';
		echo $html; // WPCS: XSS OK.
		echo '</a>';

		add_filter( 'woocommerce_widget_cart_is_hidden', '__return_false', 999 );

		echo '<div class="cart-dropdown-box widget-area">';
		the_widget(
			'WC_Widget_Cart',
			array(
				'hide_if_empty' => 0,
			)
		);
		echo '</div>';

		remove_filter( 'woocommerce_widget_cart_is_hidden', '__return_false', 999 );

		echo '</div>';
	}
}

Customify_Customize_Layout_Builder()->register_item( 'header', new Customify_Builder_Item_WC_Cart() );
catalog.php000066600000001257151137606660006713 0ustar00<?php

class Customify_WC_Products {
	function __construct() {
		add_filter( 'customify/customizer/config', array( $this, 'config' ), 100 );
	}

	function config( $configs ) {
		$section = 'woocommerce_product_catalog';

		$configs[] = array(
			'name'    => 'woocommerce_catalog_tablet_columns',
			'type'    => 'text',
			'section' => $section,
			'label'   => __( 'Products per row on tablet', 'customify' ),
		);
		$configs[] = array(
			'name'    => 'woocommerce_catalog_mobile_columns',
			'type'    => 'text',
			'section' => $section,
			'default' => 1,
			'label'   => __( 'Products per row on mobile', 'customify' ),
		);

		return $configs;
	}
}

new Customify_WC_Products();
catalog-designer.php000066600000036736151137606660010523 0ustar00<?php

class Customify_WC_Catalog_Designer {

	private $configs = array();

	function __construct() {
		add_filter( 'customify/customizer/config', array( $this, 'config' ), 100 );
		if ( is_admin() || is_customize_preview() ) {
			add_filter( 'Customify_Control_Args', array( $this, 'add_catalog_url' ), 35 );
		}

		// Loop.
		add_action( 'customify_wc_product_loop', array( $this, 'render' ) );
	}

	/**
	 * Get callback function for item part
	 *
	 * @param string $item_id ID of builder item.
	 *
	 * @return string|object|boolean
	 */
	function callback( $item_id ) {
		$cb = apply_filters( 'customify/product-designer/part', false, $item_id, $this );
		if ( ! is_callable( $cb ) ) {
			$cb = array( $this, 'product__' . $item_id );
		}
		if ( is_callable( $cb ) ) {
			return $cb;
		}

		return false;
	}

	function render() {

		$items = Customify()->get_setting( 'wc_cd_positions' );

		$this->configs['excerpt_type']   = Customify()->get_setting( 'wc_cd_excerpt_type' );
		$this->configs['excerpt_length'] = Customify()->get_setting( 'wc_cd_excerpt_length' );

		$this->configs = apply_filters( 'customify_wc_catalog_designer/configs', $this->configs );

		$cb = $this->callback( 'media' );
		if ( $cb ) {
			call_user_func( $cb, array( null, $this ) );
		}

		echo '<div class="wc-product-contents">';

		/**
		 * Hook: woocommerce_before_shop_loop_item.
		 */
		do_action( 'woocommerce_before_shop_loop_item' );

		$html = '';

		/**
		 * Allow 3rg party to render items html
		 */
		$html = apply_filters( 'customify/product-designer/render_html', $html, $items, $this );

		if ( ! $html ) {
			foreach ( (array) $items as $item ) {
				$item = wp_parse_args(
					$item,
					array(
						'_key'         => '',
						'_visibility'  => '',
						'show_in_grid' => 1,
						'show_in_list' => 1,
					)
				);
				if ( 'hidden' !== $item['_visibility'] ) {

					$cb = $this->callback( $item['_key'] );

					if ( is_callable( $cb ) ) {
						$classes   = array();
						$classes[] = 'wc-product__part';
						$classes[] = 'wc-product__' . $item['_key'];

						if ( $item['show_in_grid'] ) {
							$classes[] = 'show-in-grid';
						} else {
							$classes[] = 'hide-in-grid';
						}
						if ( $item['show_in_list'] ) {
							$classes[] = 'show-in-list';
						} else {
							$classes[] = 'hide-in-list';
						}

						$item_html = '';
						ob_start();
						call_user_func( $cb, array( $item, $this ) );
						$item_html = ob_get_contents();
						ob_end_clean();

						if ( trim( $item_html ) != '' ) {
							$html .= '<div class="' . esc_attr( join( ' ', $classes ) ) . '">';
							$html .= $item_html;
							$html .= '</div>';
						}
					}
				}
			}
		}

		echo $html; // WPCS: XSS OK.

		/**
		 * Hook: woocommerce_after_shop_loop_item.
		 */
		do_action( 'woocommerce_after_shop_loop_item' );

		echo '</div>'; // End .wc-product-contents.

	}

	/**
	 * Preview url when section open
	 *
	 * @param array $args The section urls config.
	 *
	 * @return array
	 */
	function add_catalog_url( $args ) {
		$args['section_urls']['wc_catalog_designer'] = get_permalink( wc_get_page_id( 'shop' ) );

		return $args;
	}

	/**
	 * Get Default builder items for product designer
	 *
	 * @since 2.0.5
	 *
	 * @return array
	 */
	function get_default_items() {
		$items = array(
			array(
				'_key'         => 'category',
				'_visibility'  => '',
				'show_in_grid' => 1,
				'show_in_list' => 1,
				'title'        => __( 'Category', 'customify' ),
			),
			array(
				'_visibility'  => '',
				'_key'         => 'title',
				'title'        => __( 'Title', 'customify' ),
				'show_in_grid' => 1,
				'show_in_list' => 1,
			),
			array(
				'_key'         => 'rating',
				'_visibility'  => '',
				'show_in_grid' => 1,
				'show_in_list' => 1,
				'title'        => __( 'Rating', 'customify' ),
			),

			array(
				'_key'         => 'price',
				'_visibility'  => '',
				'show_in_grid' => 1,
				'show_in_list' => 1,
				'title'        => __( 'Price', 'customify' ),
			),
			array(
				'_key'         => 'description',
				'_visibility'  => '',
				'show_in_grid' => 0,
				'show_in_list' => 1,
				'title'        => __( 'Short Description', 'customify' ),
			),
			array(
				'_key'         => 'add_to_cart',
				'_visibility'  => '',
				'show_in_grid' => 1,
				'show_in_list' => 1,
				'title'        => __( 'Add To Cart', 'customify' ),
			),
		);

		return apply_filters( 'customify/product-designer/body-items', $items );
	}


	function config( $configs ) {

		$section = 'wc_catalog_designer';

		$configs[] = array(
			'name'     => $section,
			'type'     => 'section',
			'panel'    => 'woocommerce',
			'priority' => 10,
			'label'    => __( 'Product Catalog Designer', 'customify' ),
		);

		// Catalog header.
		$configs[] = array(
			'name'            => 'wc_cd_show_catalog_header',
			'type'            => 'checkbox',
			'section'         => $section,
			'default'         => 1,
			'priority'        => 10,
			'selector'        => '.wc-product-listing',
			'render_callback' => 'woocommerce_content',
			'label'           => __( 'Show Catalog Filtering Bar', 'customify' ),
		);

		// Show view mod.
		$configs[] = array(
			'name'            => 'wc_cd_show_view_mod',
			'type'            => 'checkbox',
			'section'         => $section,
			'default'         => 1,
			'selector'        => '.wc-product-listing',
			'render_callback' => 'woocommerce_content',
			'checkbox_label'  => __( 'Show Grid/List View Buttons', 'customify' ),
			'priority'        => 11,
		);

		$configs[] = array(
			'name'            => 'wc_cd_default_view',
			'type'            => 'select',
			'section'         => $section,
			'default'         => 'grid',
			'priority'        => 12,
			'choices'         => array(
				'grid' => __( 'Grid', 'customify' ),
				'list' => __( 'List', 'customify' ),
			),
			'selector'        => '.wc-product-listing',
			'render_callback' => 'woocommerce_content',
			'label'           => __( 'Default View Mod', 'customify' ),
		);

		$configs[] = array(
			'name'             => 'wc_cd_positions',
			'section'          => $section,
			'label'            => __( 'Outside Media Items & Positions', 'customify' ),
			'type'             => 'repeater',
			'live_title_field' => 'title',
			'addable'          => false,
			'priority'         => 15,
			'selector'         => '.wc-product-listing',
			'render_callback'  => 'woocommerce_content',
			'default'          => $this->get_default_items(),
			'fields'           => apply_filters(
				'customify/product-designer/body-field-config',
				array(
					array(
						'name' => '_key',
						'type' => 'hidden',
					),
					array(
						'name'  => 'title',
						'type'  => 'hidden',
						'label' => __( 'Title', 'customify' ),
					),
					array(
						'name'           => 'show_in_grid',
						'type'           => 'checkbox',
						'checkbox_label' => __( 'Show in grid view', 'customify' ),
					),
					array(
						'name'           => 'show_in_list',
						'type'           => 'checkbox',
						'checkbox_label' => __( 'Show in list view', 'customify' ),
					),
				)
			),
		);

		$configs[] = array(
			'name'     => 'wc_cd_excerpt_type',
			'type'     => 'select',
			'section'  => $section,
			'priority' => 17,
			'title'    => __( 'List view excerpt type', 'customify' ),
			'choices'  => array(
				'excerpt' => __( 'Product short description', 'customify' ),
				'content' => __( 'Full content', 'customify' ),
				'more'    => __( 'Strip by more tag', 'customify' ),
				'custom'  => __( 'Custom', 'customify' ),
			),
		);

		$configs[] = array(
			'name'     => 'wc_cd_excerpt_length',
			'type'     => 'text',
			'section'  => $section,
			'priority' => 17,
			'title'    => __( 'Custom list view excerpt length', 'customify' ),
			'required' => array( 'wc_cd_excerpt_type', '=', 'custom' ),
		);

		// Product Media.
		$configs[] = array(
			'name'     => 'wc_cd_memdia_h',
			'type'     => 'heading',
			'section'  => $section,
			'priority' => 25,
			'label'    => __( 'Product Media & Alignment', 'customify' ),
		);

		$configs[] = array(
			'name'            => 'wc_cd_list_media_width',
			'type'            => 'slider',
			'section'         => $section,
			'unit'            => '%',
			'max'             => 100,
			'device_settings' => true,
			'priority'        => 26,
			'selector'        => 'format',
			'css_format'      => '.woocommerce-listing.wc-list-view .product.customify-col:not(.product-category) .wc-product-inner .wc-product-media { flex-basis: {{value_no_unit}}%; } .woocommerce-listing.wc-list-view .product.customify-col:not(.product-category) .wc-product-inner .wc-product-contents{ flex-basis: calc(100% - {{value_no_unit}}%); }',
			'title'           => __( 'List View Media Width', 'customify' ),
		);

		$configs[] = array(
			'name'            => 'wc_cd_media_secondary',
			'type'            => 'select',
			'choices'         => array(
				'first' => __( 'Use first image of product gallery', 'customify' ),
				'last'  => __( 'Use last image of product gallery', 'customify' ),
				'none'  => __( 'Disable', 'customify' ),
			),
			'section'         => $section,
			'default'         => 'first',
			'priority'        => 27,
			'selector'        => '.wc-product-listing',
			'render_callback' => 'woocommerce_content',
			'description'     => __( 'This setting adds a hover effect that will reveal a secondary product thumbnail to product images on your product listings. This is ideal for displaying front and back images of products.', 'customify' ),
			'title'           => __( 'Secondary Thumbnail', 'customify' ),
		);

		$configs[] = array(
			'name'            => 'wc_cd_item_grid_align',
			'type'            => 'text_align_no_justify',
			'section'         => $section,
			'device_settings' => true,
			'priority'        => 28,
			'selector'        => '.wc-grid-view .wc-product-contents',
			'css_format'      => 'text-align: {{value}};',
			'title'           => __( 'Grid View - Content Alignment', 'customify' ),
		);

		$configs[] = array(
			'name'            => 'wc_cd_item_list_align',
			'type'            => 'text_align_no_justify',
			'section'         => $section,
			'device_settings' => true,
			'priority'        => 28,
			'selector'        => '.wc-list-view .wc-product-contents',
			'css_format'      => 'text-align: {{value}};',
			'title'           => __( 'List View - Content Alignment', 'customify' ),
		);

		// Product Sale Bubble.
		$configs[] = array(
			'name'     => 'wc_cd_sale_bubble_h',
			'type'     => 'heading',
			'section'  => $section,
			'priority' => 30,
			'label'    => __( 'Product Onsale Bubble', 'customify' ),
		);

		$configs[] = array(
			'name'            => 'wc_cd_sale_bubble_type',
			'type'            => 'select',
			'default'         => 'text',
			'priority'        => 31,
			'choices'         => array(
				'text'    => __( 'Text', 'customify' ),
				'percent' => __( 'Discount percent', 'customify' ),
				'value'   => __( 'Discount value', 'customify' ),
			),
			'selector'        => '.wc-product-listing',
			'render_callback' => 'woocommerce_content',
			'section'         => $section,
			'label'           => __( 'Display Type', 'customify' ),
		);

		$configs[] = array(
			'name'        => 'wc_cd_sale_bubble_styling',
			'type'        => 'styling',
			'section'     => $section,
			'priority'    => 32,
			'title'       => __( 'Styling', 'customify' ),
			'description' => __( 'Advanced styling for onsale button', 'customify' ),
			'selector'    => array(
				'normal' => '.woocommerce span.onsale',
			),
			'css_format'  => 'styling',
			'default'     => array(),
			'fields'      => array(
				'normal_fields' => array(
					'link_color'    => false, // disable for special field.
					'margin'        => false,
					'bg_image'      => false,
					'bg_cover'      => false,
					'bg_position'   => false,
					'bg_repeat'     => false,
					'bg_attachment' => false,
				),
				'hover_fields'  => false,
			),
		);

		return $configs;
	}

	function product__media() {
		echo '<div class="wc-product-media">';
		/**
		 * Hook: customify/wc-product/before-media
		 * hooked: woocommerce_template_loop_product_link_open - 10
		 */
		do_action( 'customify/wc-product/before-media' );
		woocommerce_show_product_loop_sale_flash();
		woocommerce_template_loop_product_thumbnail();
		customify_wc_secondary_product_thumbnail();
		do_action( 'customify_after_loop_product_media' );
		/**
		 * Hook: customify/wc-product/after-media
		 * hooked: woocommerce_template_loop_product_link_close - 10
		 */
		do_action( 'customify/wc-product/after-media' );
		echo '</div>';
	}

	function product__title() {

		/**
		 * Hook: woocommerce_before_shop_loop_item_title.
		 *
		 * @hooked woocommerce_show_product_loop_sale_flash - 10
		 * @hooked woocommerce_template_loop_product_thumbnail - 10
		 */
		do_action( 'woocommerce_before_shop_loop_item_title' );

		/**
		 * @see    woocommerce_shop_loop_item_title.
		 *
		 * @hooked woocommerce_template_loop_product_title - 10
		 */
		do_action( 'woocommerce_shop_loop_item_title' );

		/**
		 * Hook: woocommerce_after_shop_loop_item_title.
		 *
		 * @hooked woocommerce_template_loop_rating - 5
		 * @hooked woocommerce_template_loop_price - 10
		 */
		do_action( 'woocommerce_after_shop_loop_item_title' );

	}

	/**
	 * Trim the excerpt with custom length.
	 *
	 * @see wp_trim_excerpt
	 *
	 * @param string  $text           Text to trim.
	 * @param integer $excerpt_length Number word to trim.
	 *
	 * @return mixed|string
	 */
	function trim_excerpt( $text, $excerpt_length = null ) {
		$text = strip_shortcodes( $text );
		/** This filter is documented in wp-includes/post-template.php */
		$text = apply_filters( 'the_content', $text );
		$text = str_replace( ']]>', ']]&gt;', $text );

		if ( ! $excerpt_length ) {
			/**
			 * Filters the number of words in an excerpt.
			 *
			 * @since 2.7.0
			 *
			 * @param int $number The number of words. Default 55.
			 */
			$excerpt_length = apply_filters( 'excerpt_length', 55 );
		}
		$more_text    = ' &hellip;';
		$excerpt_more = apply_filters( 'excerpt_more', $more_text );

		$text = wp_trim_words( $text, $excerpt_length, $excerpt_more );

		return $text;
	}

	function product__description() {
		echo '<div class="woocommerce-loop-product__desc">';

		if ( 'excerpt' == $this->configs['excerpt_type'] ) {
			the_excerpt();
		} elseif ( 'more' == $this->configs['excerpt_type'] ) {
			the_content( '', true );
		} elseif ( 'content' == $this->configs['excerpt_type'] ) {
			the_content( '', false );
		} else {
			$text = '';
			global $post;
			if ( $post ) {
				if ( $post->post_excerpt ) {
					$text = $post->post_excerpt;
				} else {
					$text = $post->post_content;
				}
			}
			$excerpt = $this->trim_excerpt( $text, $this->configs['excerpt_length'] );
			if ( $excerpt ) {
				// WPCS: XSS OK.
				echo apply_filters( 'the_excerpt', $excerpt );
			} else {
				the_excerpt();
			}
		}

		echo '</div>';

	}

	function product__price() {
		woocommerce_template_loop_price();
	}

	function product__rating() {
		woocommerce_template_loop_rating();
	}

	function product__category() {
		global $post;

		$tax = 'product_cat';
		$num = 1;

		$terms = get_the_terms( $post, $tax );

		if ( is_wp_error( $terms ) ) {
			return $terms;
		}

		if ( empty( $terms ) ) {
			return false;
		}

		$links = array();

		foreach ( $terms as $term ) {
			$link = get_term_link( $term, $tax );
			if ( is_wp_error( $link ) ) {
				return $link;
			}
			$links[] = '<a class="text-uppercase text-xsmall link-meta" href="' . esc_url( $link ) . '" rel="tag">' . esc_html( $term->name ) . '</a>';
		}

		$categories_list = array_slice( $links, 0, $num );

		echo join( ' ', $categories_list );
	}

	function product__add_to_cart() {
		woocommerce_template_loop_add_to_cart();
	}

}

new Customify_WC_Catalog_Designer();
single-product.php000066600000024160151137606660010236 0ustar00<?php

/**
 * Class Customify_WC_Single_Product
 *
 * Single product settings
 */
class Customify_WC_Single_Product {
	function __construct() {
		add_filter( 'customify/customizer/config', array( $this, 'config' ), 100 );
		if ( is_admin() || is_customize_preview() ) {
			add_filter( 'Customify_Control_Args', array( $this, 'add_product_url' ), 35 );
		}

		add_action( 'wp', array( $this, 'single_product_hooks' ) );
	}

	/**
	 * Add more class if nav showing
	 *
	 * @param array $classes HTML classes.
	 *
	 * @return array
	 */
	function post_class( $classes ) {
		if ( Customify()->get_setting( 'wc_single_product_nav_show' ) ) {
			$classes[] = 'nav-in-title';
		}
		return $classes;
	}

	/**
	 * Get adjacent product
	 *
	 * @param bool   $in_same_term In same term.
	 * @param string $excluded_terms Exlclude terms.
	 * @param bool   $previous Previous.
	 * @param string $taxonomy Taxonomy.
	 *
	 * @return null|string|WP_Post
	 */
	public function get_adjacent_product( $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'product_cat' ) {
		return get_adjacent_post( $in_same_term, $excluded_terms, $previous, $taxonomy );
	}

	/**
	 * Display prev - next button
	 */
	public function product_prev_next() {
		if ( ! Customify()->get_setting( 'wc_single_product_nav_show' ) ) {
			return;
		}
		$prev_post = $this->get_adjacent_product();
		$next_post = $this->get_adjacent_product( false, '', false );
		if ( $prev_post || $next_post ) {
			?>
			<div class="wc-product-nav">
				<?php if ( $prev_post ) { ?>
					<a href="<?php echo esc_url( get_permalink( $prev_post ) ); ?>" title="<?php the_title_attribute( array( 'post' => $prev_post ) ); ?>" class="prev-link">
						<span class="nav-btn nav-next"><?php echo apply_filters( 'customify_nav_prev_icon', '' ); ?></span>
						<?php if ( has_post_thumbnail( $prev_post ) ) { ?>
							<span class="nav-thumbnail">
								<?php
								echo get_the_post_thumbnail( $prev_post, 'woocommerce_thumbnail' );
								?>
							</span>
						<?php } ?>
					</a>
				<?php } ?>
				<?php if ( $next_post ) { ?>
					<a href="<?php echo esc_url( get_permalink( $next_post ) ); ?>" title="<?php the_title_attribute( array( 'post' => $next_post ) ); ?>" class="next-link">
						<span class="nav-btn nav-next">
						<?php echo apply_filters( 'customify_nav_next_icon', '' ); ?>
						</span>
						<?php if ( has_post_thumbnail( $next_post ) ) { ?>
							<span class="nav-thumbnail">
								<?php
								echo get_the_post_thumbnail( $next_post, 'woocommerce_thumbnail' );
								?>
							</span>
						<?php } ?>
					</a>
				<?php } ?>
			</div>
			<?php
		}
	}

	/**
	 * Hooks for single product
	 */
	function single_product_hooks() {
		if ( ! is_product() ) {
			return;
		}

		add_action( 'wc_after_single_product_title', array( $this, 'product_prev_next' ), 2 );
		add_filter( 'post_class', array( $this, 'post_class' ) );

		if ( Customify()->get_setting( 'wc_single_product_tab_hide_description' ) ) {
			add_filter( 'woocommerce_product_description_heading', '__return_false', 999 );
		}

		if ( Customify()->get_setting( 'wc_single_product_tab_hide_attr_heading' ) ) {
			add_filter( 'woocommerce_product_additional_information_heading', '__return_false', 999 );
		}

		$tab_type = Customify()->get_setting( 'wc_single_product_tab' );

		if ( 'section' == $tab_type || 'toggle' == $tab_type ) {
			add_filter( 'woocommerce_product_description_heading', '__return_false', 999 );
			add_filter( 'woocommerce_product_additional_information_heading', '__return_false', 999 );
		}

	}

	/**
	 * Add url to customize preview when section open
	 *
	 * @param array $args Args to add.
	 *
	 * @return mixed
	 */
	public function add_product_url( $args ) {

		$query = new WP_Query(
			array(
				'post_type'      => 'product',
				'posts_per_page' => 1,
				'orderby'        => 'rand',
			)
		);

		$products = $query->get_posts();
		if ( count( $products ) ) {
			$args['section_urls']['wc_single_product'] = get_permalink( $products[0] );
		}

		return $args;
	}

	/**
	 * Customize config
	 *
	 * @param array $configs Config args.
	 *
	 * @return array
	 */
	public function config( $configs ) {
		$section = 'wc_single_product';

		$configs[] = array(
			'name'     => $section,
			'type'     => 'section',
			'panel'    => 'woocommerce',
			'title'    => __( 'Single Product Page', 'customify' ),
			'priority' => 19,
		);

		$configs[] = array(
			'name'    => 'wc_single_layout_h',
			'type'    => 'heading',
			'section' => $section,
			'label'   => __( 'Layout', 'customify' ),
		);

		/*
		$configs[] = array(
			'name'    => 'wc_single_layout',
			'type'    => 'select',
			'section' => $section,
			'default' => 'default',
			'label'   => __( 'Layout', 'customify' ),
			'choices' => array(
				'default'    => __( 'Default', 'customify' ),
				'top-medium' => __( 'Top Gallery Boxed', 'customify' ),
				'top-full'   => __( 'Top Gallery Full Width', 'customify' ),
				'left-grid'  => __( 'Left Gallery Grid', 'customify' ),
			)
		);
		*/

		$configs[] = array(
			'name'             => 'wc_single_layout',
			'type'             => 'image_select',
			'section'          => $section,
			'title'            => __( 'Layout', 'customify' ),
			'default'          => 'default',

			'disabled_msg'     => __( 'This option is available in Customify Pro plugin only.', 'customify' ),
			'disabled_pro_msg' => __( 'Please activate module Single Product Layouts to use this layout.', 'customify' ),

			'choices'          => array(
				'default'    => array(
					'img'   => esc_url( get_template_directory_uri() ) . '/assets/images/customizer/wc-layout-default.svg',
					'label' => __( 'Default', 'customify' ),
				),
				'top-medium' => array(
					'img'     => esc_url( get_template_directory_uri() ) . '/assets/images/customizer/wc-layout-top-medium.svg',
					'label'   => __( 'Top Gallery Boxed', 'customify' ),
					'disable' => 1,
					'bubble'  => __( 'Pro', 'customify' ),
				),
				'top-full'   => array(
					'img'     => esc_url( get_template_directory_uri() ) . '/assets/images/customizer/wc-layout-top-full.svg',
					'label'   => __( 'Top Gallery Full Width', 'customify' ),
					'disable' => 1,
					'bubble'  => __( 'Pro', 'customify' ),
				),
				'left-grid'  => array(
					'img'     => esc_url( get_template_directory_uri() ) . '/assets/images/customizer/wc-layout-left-grid.svg',
					'label'   => __( 'Left Gallery Grid', 'customify' ),
					'disable' => 1,
					'bubble'  => __( 'Pro', 'customify' ),

				),
			),
		);

		$configs[] = array(
			'name'     => "{$section}_nav_heading",
			'type'     => 'heading',
			'section'  => $section,
			'title'    => __( 'Product Navigation', 'customify' ),
			'priority' => 39,
		);

		$configs[] = array(
			'name'           => "{$section}_nav_show",
			'type'           => 'checkbox',
			'default'        => 1,
			'section'        => $section,
			'checkbox_label' => __( 'Show Product Navigation', 'customify' ),
			'priority'       => 39,
		);

		$configs[] = array(
			'name'     => "{$section}_tab_heading",
			'type'     => 'heading',
			'section'  => $section,
			'title'    => __( 'Product Tabs', 'customify' ),
			'priority' => 40,
		);

		$configs[] = array(
			'name'     => "{$section}_tab",
			'type'     => 'select',
			'default'  => 'horizontal',
			'section'  => $section,
			'label'    => __( 'Tab Layout', 'customify' ),
			'choices'  => array(
				'horizontal' => __( 'Horizontal', 'customify' ),
				'vertical'   => __( 'Vertical', 'customify' ),
				'toggle'     => __( 'Toggle', 'customify' ),
				'sections'   => __( 'Sections', 'customify' ),
			),
			'priority' => 45,
		);

		$configs[] = array(
			'name'           => "{$section}_tab_hide_description",
			'type'           => 'checkbox',
			'default'        => 1,
			'section'        => $section,
			'checkbox_label' => __( 'Hide product description heading', 'customify' ),
			'priority'       => 46,
		);

		$configs[] = array(
			'name'           => "{$section}_tab_hide_attr_heading",
			'type'           => 'checkbox',
			'default'        => 1,
			'section'        => $section,
			'checkbox_label' => __( 'Hide product additional information heading', 'customify' ),
			'priority'       => 47,
		);

		$configs[] = array(
			'name'           => "{$section}_tab_hide_review_heading",
			'type'           => 'checkbox',
			'default'        => 0,
			'section'        => $section,
			'checkbox_label' => __( 'Hide product review heading', 'customify' ),
			'selector'       => '.woocommerce-Reviews-title',
			'css_format'     => 'display: none;',
			'priority'       => 48,
		);

		$configs[] = array(
			'name'     => "{$section}_upsell_heading",
			'type'     => 'heading',
			'section'  => $section,
			'title'    => __( 'Upsell Products', 'customify' ),
			'priority' => 60,
		);

		$configs[] = array(
			'name'     => "{$section}_upsell_number",
			'type'     => 'text',
			'default'  => 3,
			'section'  => $section,
			'label'    => __( 'Number of upsell products', 'customify' ),
			'priority' => 65,
		);

		$configs[] = array(
			'name'            => "{$section}_upsell_columns",
			'type'            => 'text',
			'device_settings' => true,
			'section'         => $section,
			'label'           => __( 'Upsell products per row', 'customify' ),
			'priority'        => 66,
		);

		$configs[] = array(
			'name'     => "{$section}_related_heading",
			'type'     => 'heading',
			'section'  => $section,
			'title'    => __( 'Related Products', 'customify' ),
			'priority' => 70,
		);

		$configs[] = array(
			'name'     => "{$section}_related_number",
			'type'     => 'text',
			'default'  => 3,
			'section'  => $section,
			'label'    => __( 'Number of related products', 'customify' ),
			'priority' => 75,
		);

		$configs[] = array(
			'name'            => "{$section}_related_columns",
			'type'            => 'text',
			'device_settings' => true,
			'section'         => $section,
			'label'           => __( 'Related products per row', 'customify' ),
			'priority'        => 76,
		);

		$configs[] = array(
			'name'           => 'wc_single_layout_breadcrumb',
			'type'           => 'checkbox',
			'section'        => $section,
			'default'        => 1,
			'checkbox_label' => __( 'Show shop breadcrumb', 'customify' ),
		);

		return $configs;
	}
}

new Customify_WC_Single_Product();
migrations/.htaccess000066600000000424151143721140010521 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>.htaccess000066600000000424151143721140006345 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>