| Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/ |
| Current File : /home/x/b/o/xbodynamge/namtation/wp-content/third-party.tar |
elementor.php 0000666 00000057665 15113146334 0007277 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Elementor\Controls_Manager;
use Elementor\Core\DocumentTypes\PageBase;
use WP_Post;
use WP_Screen;
use WPSEO_Admin_Asset_Manager;
use WPSEO_Admin_Recommended_Replace_Vars;
use WPSEO_Language_Utils;
use WPSEO_Meta;
use WPSEO_Metabox_Analysis_Readability;
use WPSEO_Metabox_Analysis_SEO;
use WPSEO_Metabox_Formatter;
use WPSEO_Post_Metabox_Formatter;
use WPSEO_Replace_Vars;
use WPSEO_Shortlinker;
use WPSEO_Utils;
use Yoast\WP\SEO\Actions\Alert_Dismissal_Action;
use Yoast\WP\SEO\Conditionals\Admin\Estimated_Reading_Time_Conditional;
use Yoast\WP\SEO\Conditionals\Third_Party\Elementor_Edit_Conditional;
use Yoast\WP\SEO\Helpers\Capability_Helper;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use Yoast\WP\SEO\Presenters\Admin\Meta_Fields_Presenter;
/**
* Integrates the Yoast SEO metabox in the Elementor editor.
*/
class Elementor implements Integration_Interface {
/**
* The identifier for the elementor tab.
*/
const YOAST_TAB = 'yoast-tab';
/**
* Represents the post.
*
* @var WP_Post|null
*/
protected $post;
/**
* Represents the admin asset manager.
*
* @var WPSEO_Admin_Asset_Manager
*/
protected $asset_manager;
/**
* Represents the options helper.
*
* @var Options_Helper
*/
protected $options;
/**
* Represents the capability helper.
*
* @var Capability_Helper
*/
protected $capability;
/**
* Holds whether the socials are enabled.
*
* @var bool
*/
protected $social_is_enabled;
/**
* Holds whether the advanced settings are enabled.
*
* @var bool
*/
protected $is_advanced_metadata_enabled;
/**
* Helper to determine whether or not the SEO analysis is enabled.
*
* @var WPSEO_Metabox_Analysis_SEO
*/
protected $seo_analysis;
/**
* Helper to determine whether or not the readability analysis is enabled.
*
* @var WPSEO_Metabox_Analysis_Readability
*/
protected $readability_analysis;
/**
* Represents the estimated_reading_time_conditional.
*
* @var Estimated_Reading_Time_Conditional
*/
protected $estimated_reading_time_conditional;
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Elementor_Edit_Conditional::class ];
}
/**
* Constructor.
*
* @param WPSEO_Admin_Asset_Manager $asset_manager The asset manager.
* @param Options_Helper $options The options helper.
* @param Capability_Helper $capability The capability helper.
* @param Estimated_Reading_Time_Conditional $estimated_reading_time_conditional The Estimated Reading Time
* conditional.
*/
public function __construct(
WPSEO_Admin_Asset_Manager $asset_manager,
Options_Helper $options,
Capability_Helper $capability,
Estimated_Reading_Time_Conditional $estimated_reading_time_conditional
) {
$this->asset_manager = $asset_manager;
$this->options = $options;
$this->capability = $capability;
$this->seo_analysis = new WPSEO_Metabox_Analysis_SEO();
$this->readability_analysis = new WPSEO_Metabox_Analysis_Readability();
$this->social_is_enabled = $this->options->get( 'opengraph', false ) || $this->options->get( 'twitter', false );
$this->is_advanced_metadata_enabled = $this->capability->current_user_can( 'wpseo_edit_advanced_metadata' ) || $this->options->get( 'disableadvanced_meta' ) === false;
$this->estimated_reading_time_conditional = $estimated_reading_time_conditional;
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
\add_action( 'wp_ajax_wpseo_elementor_save', [ $this, 'save_postdata' ] );
// We need to delay the post type lookup to give other plugins a chance to register custom post types.
\add_action( 'init', [ $this, 'register_elementor_hooks' ], \PHP_INT_MAX );
}
/**
* Registers our Elementor hooks.
*/
public function register_elementor_hooks() {
if ( ! $this->display_metabox( $this->get_metabox_post()->post_type ) ) {
return;
}
\add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'init' ] );
// We are too late for elementor/init. We should see if we can be on time, or else this workaround works (we do always get the "else" though).
if ( ! \did_action( 'elementor/init' ) ) {
\add_action( 'elementor/init', [ $this, 'add_yoast_panel_tab' ] );
}
else {
$this->add_yoast_panel_tab();
}
\add_action( 'elementor/documents/register_controls', [ $this, 'register_document_controls' ] );
}
/**
* Initializes the integration.
*
* @return void
*/
public function init() {
$this->asset_manager->register_assets();
$this->enqueue();
$this->render_hidden_fields();
}
/**
* Register a panel tab slug, in order to allow adding controls to this tab.
*/
public function add_yoast_panel_tab() {
Controls_Manager::add_tab( $this::YOAST_TAB, 'Yoast SEO' );
}
/**
* Register additional document controls.
*
* @param PageBase $document The PageBase document.
*/
public function register_document_controls( $document ) {
// PageBase is the base class for documents like `post` `page` and etc.
if ( ! $document instanceof PageBase || ! $document::get_property( 'has_elements' ) ) {
return;
}
// This is needed to get the tab to appear, but will be overwritten in the JavaScript.
$document->start_controls_section(
'yoast_temporary_section',
[
'label' => 'Yoast SEO',
'tab' => self::YOAST_TAB,
]
);
$document->end_controls_section();
}
// Below is mostly copied from `class-metabox.php`. That constructor has side-effects we do not need.
/**
* Determines whether the metabox should be shown for the passed identifier.
*
* By default the check is done for post types, but can also be used for taxonomies.
*
* @param string|null $identifier The identifier to check.
* @param string $type The type of object to check. Defaults to post_type.
*
* @return bool Whether or not the metabox should be displayed.
*/
public function display_metabox( $identifier = null, $type = 'post_type' ) {
return WPSEO_Utils::is_metabox_active( $identifier, $type );
}
/**
* Saves the WP SEO metadata for posts.
*
* Outputs JSON via wp_send_json then stops code execution.
*
* {@internal $_POST parameters are validated via sanitize_post_meta().}}
*
* @return void
*/
public function save_postdata() {
global $post;
$post_id = \filter_input( \INPUT_POST, 'post_id', \FILTER_SANITIZE_NUMBER_INT );
if ( ! \current_user_can( 'edit_post', $post_id ) ) {
\wp_send_json_error( 'Forbidden', 403 );
}
\check_ajax_referer( 'wpseo_elementor_save', '_wpseo_elementor_nonce' );
// Bail if this is a multisite installation and the site has been switched.
if ( \is_multisite() && \ms_is_switched() ) {
\wp_send_json_error( 'Switched multisite', 409 );
}
\clean_post_cache( $post_id );
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- To setup the post we need to do this explicitly.
$post = \get_post( $post_id );
if ( ! \is_object( $post ) ) {
// Non-existent post.
\wp_send_json_error( 'Post not found', 400 );
}
\do_action( 'wpseo_save_compare_data', $post );
// Initialize meta, amongst other things it registers sanitization.
WPSEO_Meta::init();
$social_fields = [];
if ( $this->social_is_enabled ) {
$social_fields = WPSEO_Meta::get_meta_field_defs( 'social', $post->post_type );
}
// The below methods use the global post so make sure it is setup.
\setup_postdata( $post );
$meta_boxes = \apply_filters( 'wpseo_save_metaboxes', [] );
$meta_boxes = \array_merge(
$meta_boxes,
WPSEO_Meta::get_meta_field_defs( 'general', $post->post_type ),
WPSEO_Meta::get_meta_field_defs( 'advanced', $post->post_type ),
$social_fields,
WPSEO_Meta::get_meta_field_defs( 'schema', $post->post_type )
);
foreach ( $meta_boxes as $key => $meta_box ) {
// If analysis is disabled remove that analysis score value from the DB.
if ( $this->is_meta_value_disabled( $key ) ) {
WPSEO_Meta::delete( $key, $post_id );
continue;
}
$data = null;
$field_name = WPSEO_Meta::$form_prefix . $key;
if ( $meta_box['type'] === 'checkbox' ) {
$data = isset( $_POST[ $field_name ] ) ? 'on' : 'off';
}
else {
if ( isset( $_POST[ $field_name ] ) ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: Sanitized through sanitize_post_meta.
$data = \wp_unslash( $_POST[ $field_name ] );
// For multi-select.
if ( \is_array( $data ) ) {
$data = \array_map( [ 'WPSEO_Utils', 'sanitize_text_field' ], $data );
}
if ( \is_string( $data ) ) {
$data = ( $key !== 'canonical' ) ? WPSEO_Utils::sanitize_text_field( $data ) : WPSEO_Utils::sanitize_url( $data );
}
}
// Reset options when no entry is present with multiselect - only applies to `meta-robots-adv` currently.
if ( ! isset( $_POST[ $field_name ] ) && ( $meta_box['type'] === 'multiselect' ) ) {
$data = [];
}
}
if ( $data !== null ) {
WPSEO_Meta::set_value( $key, $data, $post_id );
}
}
// Saving the WP post to save the slug.
$slug = \filter_input( \INPUT_POST, WPSEO_Meta::$form_prefix . 'slug', \FILTER_SANITIZE_STRING );
if ( $post->post_name !== $slug ) {
$post_array = $post->to_array();
$post_array['post_name'] = $slug;
$save_successful = \wp_insert_post( $post_array );
if ( \is_wp_error( $save_successful ) ) {
\wp_send_json_error( 'Slug not saved', 400 );
}
// Update the post object to ensure we have the actual slug.
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Updating the post is needed to get the current slug.
$post = \get_post( $post_id );
if ( ! \is_object( $post ) ) {
\wp_send_json_error( 'Updated slug not found', 400 );
}
}
\do_action( 'wpseo_saved_postdata' );
// Output the slug, because it is processed by WP and we need the actual slug again.
\wp_send_json_success( [ 'slug' => $post->post_name ] );
}
/**
* Determines if the given meta value key is disabled.
*
* @param string $key The key of the meta value.
*
* @return bool Whether the given meta value key is disabled.
*/
public function is_meta_value_disabled( $key ) {
if ( $key === 'linkdex' && ! $this->seo_analysis->is_enabled() ) {
return true;
}
if ( $key === 'content_score' && ! $this->readability_analysis->is_enabled() ) {
return true;
}
return false;
}
/**
* Enqueues all the needed JS and CSS.
*
* @return void
*/
public function enqueue() {
$post_id = \get_queried_object_id();
if ( empty( $post_id ) ) {
$post_id = \sanitize_text_field( \filter_input( \INPUT_GET, 'post' ) );
}
if ( $post_id !== 0 ) {
// Enqueue files needed for upload functionality.
\wp_enqueue_media( [ 'post' => $post_id ] );
}
$this->asset_manager->enqueue_style( 'admin-global' );
$this->asset_manager->enqueue_style( 'metabox-css' );
$this->asset_manager->enqueue_style( 'scoring' );
$this->asset_manager->enqueue_style( 'select2' );
$this->asset_manager->enqueue_style( 'monorepo' );
$this->asset_manager->enqueue_style( 'admin-css' );
$this->asset_manager->enqueue_style( 'elementor' );
$this->asset_manager->enqueue_script( 'admin-global' );
$this->asset_manager->enqueue_script( 'elementor' );
$this->asset_manager->localize_script( 'elementor', 'wpseoAdminGlobalL10n', \YoastSEO()->helpers->wincher->get_admin_global_links() );
$this->asset_manager->localize_script( 'elementor', 'wpseoAdminL10n', WPSEO_Utils::get_admin_l10n() );
$this->asset_manager->localize_script( 'elementor', 'wpseoFeaturesL10n', WPSEO_Utils::retrieve_enabled_features() );
$plugins_script_data = [
'replaceVars' => [
'no_parent_text' => \__( '(no parent)', 'wordpress-seo' ),
'replace_vars' => $this->get_replace_vars(),
'recommended_replace_vars' => $this->get_recommended_replace_vars(),
'hidden_replace_vars' => $this->get_hidden_replace_vars(),
'scope' => $this->determine_scope(),
'has_taxonomies' => $this->current_post_type_has_taxonomies(),
],
'shortcodes' => [
'wpseo_filter_shortcodes_nonce' => \wp_create_nonce( 'wpseo-filter-shortcodes' ),
'wpseo_shortcode_tags' => $this->get_valid_shortcode_tags(),
],
];
$worker_script_data = [
'url' => \YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-analysis-worker' ),
'dependencies' => \YoastSEO()->helpers->asset->get_dependency_urls_by_handle( 'yoast-seo-analysis-worker' ),
'keywords_assessment_url' => \YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-used-keywords-assessment' ),
'log_level' => WPSEO_Utils::get_analysis_worker_log_level(),
// We need to make the feature flags separately available inside of the analysis web worker.
'enabled_features' => WPSEO_Utils::retrieve_enabled_features(),
];
$alert_dismissal_action = \YoastSEO()->classes->get( Alert_Dismissal_Action::class );
$dismissed_alerts = $alert_dismissal_action->all_dismissed();
$script_data = [
'media' => [ 'choose_image' => \__( 'Use Image', 'wordpress-seo' ) ],
'metabox' => $this->get_metabox_script_data(),
'userLanguageCode' => WPSEO_Language_Utils::get_language( \get_user_locale() ),
'isPost' => true,
'isBlockEditor' => WP_Screen::get()->is_block_editor(),
'isElementorEditor' => true,
'postStatus' => \get_post_status( $post_id ),
'analysis' => [
'plugins' => $plugins_script_data,
'worker' => $worker_script_data,
],
'dismissedAlerts' => $dismissed_alerts,
'webinarIntroElementorUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/webinar-intro-elementor' ),
];
if ( \post_type_supports( $this->get_metabox_post()->post_type, 'thumbnail' ) ) {
$this->asset_manager->enqueue_style( 'featured-image' );
$script_data['featuredImage'] = [
'featured_image_notice' => \__( 'SEO issue: The featured image should be at least 200 by 200 pixels to be picked up by Facebook and other social media sites.', 'wordpress-seo' ),
];
}
$this->asset_manager->localize_script( 'elementor', 'wpseoScriptData', $script_data );
$this->asset_manager->enqueue_user_language_script();
}
/**
* Renders the metabox hidden fields.
*
* @return void
*/
protected function render_hidden_fields() {
// Wrap in a form with an action and post_id for the submit.
\printf(
'<form id="yoast-form" method="post" action="%1$s"><input type="hidden" name="action" value="wpseo_elementor_save" /><input type="hidden" id="post_ID" name="post_id" value="%2$s" />',
\esc_url( \admin_url( 'admin-ajax.php' ) ),
\esc_attr( $this->get_metabox_post()->ID )
);
\wp_nonce_field( 'wpseo_elementor_save', '_wpseo_elementor_nonce' );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Meta_Fields_Presenter->present is considered safe.
echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'general' );
if ( $this->is_advanced_metadata_enabled ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Meta_Fields_Presenter->present is considered safe.
echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'advanced' );
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Meta_Fields_Presenter->present is considered safe.
echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'schema', $this->get_metabox_post()->post_type );
if ( $this->social_is_enabled ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Meta_Fields_Presenter->present is considered safe.
echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'social' );
}
\printf(
'<input type="hidden" id="%1$s" name="%1$s" value="%2$s" />',
\esc_attr( WPSEO_Meta::$form_prefix . 'slug' ),
\esc_attr( $this->get_post_slug() )
);
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output should be escaped in the filter.
echo \apply_filters( 'wpseo_elementor_hidden_fields', '' );
echo '</form>';
}
/**
* Returns the slug for the post being edited.
*
* @return string
*/
protected function get_post_slug() {
$post = $this->get_metabox_post();
// In case get_metabox_post returns null for whatever reason.
if ( ! $post instanceof WP_Post ) {
return '';
}
// Drafts might not have a post_name unless the slug has been manually changed.
// In this case we get it using get_sample_permalink.
if ( ! $post->post_name ) {
$sample = \get_sample_permalink( $post );
// Since get_sample_permalink runs through filters, ensure that it has the expected return value.
if ( \is_array( $sample ) && \count( $sample ) === 2 && \is_string( $sample[1] ) ) {
return $sample[1];
}
}
return $post->post_name;
}
/**
* Returns post in metabox context.
*
* @return WP_Post|null
*/
protected function get_metabox_post() {
if ( $this->post !== null ) {
return $this->post;
}
$post = \filter_input( \INPUT_GET, 'post' );
if ( ! empty( $post ) ) {
$post_id = (int) WPSEO_Utils::validate_int( $post );
$this->post = \get_post( $post_id );
return $this->post;
}
if ( isset( $GLOBALS['post'] ) ) {
$this->post = $GLOBALS['post'];
return $this->post;
}
return null;
}
/**
* Passes variables to js for use with the post-scraper.
*
* @return array
*/
protected function get_metabox_script_data() {
$permalink = '';
if ( \is_object( $this->get_metabox_post() ) ) {
$permalink = \get_sample_permalink( $this->get_metabox_post()->ID );
$permalink = $permalink[0];
}
$post_formatter = new WPSEO_Metabox_Formatter(
new WPSEO_Post_Metabox_Formatter( $this->get_metabox_post(), [], $permalink )
);
$values = $post_formatter->get_values();
/** This filter is documented in admin/filters/class-cornerstone-filter.php. */
$post_types = \apply_filters( 'wpseo_cornerstone_post_types', \YoastSEO()->helpers->post_type->get_accessible_post_types() );
if ( $values['cornerstoneActive'] && ! \in_array( $this->get_metabox_post()->post_type, $post_types, true ) ) {
$values['cornerstoneActive'] = false;
}
return $values;
}
/**
* Prepares the replace vars for localization.
*
* @return array Replace vars.
*/
protected function get_replace_vars() {
$cached_replacement_vars = [];
$vars_to_cache = [
'date',
'id',
'sitename',
'sitedesc',
'sep',
'page',
'currentyear',
'currentdate',
'currentmonth',
'currentday',
'tag',
'category',
'category_title',
'primary_category',
'pt_single',
'pt_plural',
'modified',
'name',
'user_description',
'pagetotal',
'pagenumber',
'post_year',
'post_month',
'post_day',
'author_first_name',
'author_last_name',
'permalink',
'post_content',
];
foreach ( $vars_to_cache as $var ) {
$cached_replacement_vars[ $var ] = \wpseo_replace_vars( '%%' . $var . '%%', $this->get_metabox_post() );
}
// Merge custom replace variables with the WordPress ones.
return \array_merge( $cached_replacement_vars, $this->get_custom_replace_vars( $this->get_metabox_post() ) );
}
/**
* Prepares the recommended replace vars for localization.
*
* @return array Recommended replacement variables.
*/
protected function get_recommended_replace_vars() {
$recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
// What is recommended depends on the current context.
$post_type = $recommended_replace_vars->determine_for_post( $this->get_metabox_post() );
return $recommended_replace_vars->get_recommended_replacevars_for( $post_type );
}
/**
* Returns the list of replace vars that should be hidden inside the editor.
*
* @return string[] The hidden replace vars.
*/
protected function get_hidden_replace_vars() {
return ( new WPSEO_Replace_Vars() )->get_hidden_replace_vars();
}
/**
* Gets the custom replace variables for custom taxonomies and fields.
*
* @param WP_Post $post The post to check for custom taxonomies and fields.
*
* @return array Array containing all the replacement variables.
*/
protected function get_custom_replace_vars( $post ) {
return [
'custom_fields' => $this->get_custom_fields_replace_vars( $post ),
'custom_taxonomies' => $this->get_custom_taxonomies_replace_vars( $post ),
];
}
/**
* Gets the custom replace variables for custom taxonomies.
*
* @param WP_Post $post The post to check for custom taxonomies.
*
* @return array Array containing all the replacement variables.
*/
protected function get_custom_taxonomies_replace_vars( $post ) {
$taxonomies = \get_object_taxonomies( $post, 'objects' );
$custom_replace_vars = [];
foreach ( $taxonomies as $taxonomy_name => $taxonomy ) {
if ( \is_string( $taxonomy ) ) { // If attachment, see https://core.trac.wordpress.org/ticket/37368 .
$taxonomy_name = $taxonomy;
$taxonomy = \get_taxonomy( $taxonomy_name );
}
if ( $taxonomy->_builtin && $taxonomy->public ) {
continue;
}
$custom_replace_vars[ $taxonomy_name ] = [
'name' => $taxonomy->name,
'description' => $taxonomy->description,
];
}
return $custom_replace_vars;
}
/**
* Gets the custom replace variables for custom fields.
*
* @param WP_Post $post The post to check for custom fields.
*
* @return array Array containing all the replacement variables.
*/
protected function get_custom_fields_replace_vars( $post ) {
$custom_replace_vars = [];
// If no post object is passed, return the empty custom_replace_vars array.
if ( ! \is_object( $post ) ) {
return $custom_replace_vars;
}
$custom_fields = \get_post_custom( $post->ID );
// Simply concatenate all fields containing replace vars so we can handle them all with a single regex find.
$replace_vars_fields = implode(
' ',
[
YoastSEO()->meta->for_post( $post->ID )->presentation->title,
YoastSEO()->meta->for_post( $post->ID )->presentation->meta_description,
]
);
preg_match_all( '/%%cf_([A-Za-z0-9_]+)%%/', $replace_vars_fields, $matches );
$fields_to_include = $matches[1];
foreach ( $custom_fields as $custom_field_name => $custom_field ) {
// Skip private custom fields.
if ( \substr( $custom_field_name, 0, 1 ) === '_' ) {
continue;
}
// Skip custom fields that are not used, new ones will be fetched dynamically.
if ( ! in_array( $custom_field_name, $fields_to_include, true ) ) {
continue;
}
// Skip custom field values that are serialized.
if ( \is_serialized( $custom_field[0] ) ) {
continue;
}
$custom_replace_vars[ $custom_field_name ] = $custom_field[0];
}
return $custom_replace_vars;
}
/**
* Determines the scope based on the post type.
* This can be used by the replacevar plugin to determine if a replacement needs to be executed.
*
* @return string String describing the current scope.
*/
protected function determine_scope() {
if ( $this->get_metabox_post()->post_type === 'page' ) {
return 'page';
}
return 'post';
}
/**
* Determines whether or not the current post type has registered taxonomies.
*
* @return bool Whether the current post type has taxonomies.
*/
protected function current_post_type_has_taxonomies() {
$post_taxonomies = \get_object_taxonomies( $this->get_metabox_post()->post_type );
return ! empty( $post_taxonomies );
}
/**
* Returns an array with shortcode tags for all registered shortcodes.
*
* @return array
*/
protected function get_valid_shortcode_tags() {
$shortcode_tags = [];
foreach ( $GLOBALS['shortcode_tags'] as $tag => $description ) {
$shortcode_tags[] = $tag;
}
return $shortcode_tags;
}
}
amp.php 0000666 00000002723 15113146334 0006043 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
use Yoast\WP\SEO\Integrations\Front_End_Integration;
use Yoast\WP\SEO\Integrations\Integration_Interface;
/**
* AMP integration.
*/
class AMP implements Integration_Interface {
/**
* The front end integration.
*
* @var Front_End_Integration
*/
protected $front_end;
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Front_End_Conditional::class ];
}
/**
* Constructs the AMP integration
*
* @param Front_End_Integration $front_end The front end integration.
*/
public function __construct( Front_End_Integration $front_end ) {
$this->front_end = $front_end;
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
\add_action( 'amp_post_template_head', [ $this, 'remove_amp_meta_output' ], 0 );
\add_action( 'amp_post_template_head', [ $this->front_end, 'call_wpseo_head' ], 9 );
}
/**
* Removes amp meta output.
*
* @return void
*/
public function remove_amp_meta_output() {
\remove_action( 'amp_post_template_head', 'amp_post_template_add_title' );
\remove_action( 'amp_post_template_head', 'amp_post_template_add_canonical' );
\remove_action( 'amp_post_template_head', 'amp_print_schemaorg_metadata' );
}
}
coauthors-plus.php 0000666 00000010113 15113146334 0010246 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use WP_User;
use Yoast\WP\SEO\Conditionals\Third_Party\CoAuthors_Plus_Activated_Conditional;
use Yoast\WP\SEO\Conditionals\Third_Party\CoAuthors_Plus_Flag_Conditional;
use Yoast\WP\SEO\Config\Schema_Types;
use Yoast\WP\SEO\Context\Meta_Tags_Context;
use Yoast\WP\SEO\Generators\Schema\Abstract_Schema_Piece;
use Yoast\WP\SEO\Generators\Schema\Third_Party\CoAuthor;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use Yoast\WP\SEO\Surfaces\Helpers_Surface;
/**
* Integrates the multiple authors capability from CoAuthors Plus into Yoast SEO schema.
*/
class CoAuthors_Plus implements Integration_Interface {
/**
* The helpers surface.
*
* @var Helpers_Surface
*/
protected $helpers;
/**
* Initializes the integration.
*
* @return void
*/
public function register_hooks() {
\add_filter( 'wpseo_schema_graph', [ $this, 'filter_graph' ], 11, 2 );
\add_filter( 'wpseo_schema_author', [ $this, 'filter_author_graph' ], 11, 4 );
}
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [
CoAuthors_Plus_Activated_Conditional::class,
CoAuthors_Plus_Flag_Conditional::class,
];
}
/**
* CoAuthors_Plus constructor.
*
* @codeCoverageIgnore It only sets dependencies.
*
* @param Helpers_Surface $helpers The helper surface.
*/
public function __construct( Helpers_Surface $helpers ) {
$this->helpers = $helpers;
}
/**
* Filters the graph output to add authors.
*
* @param array $data The schema graph.
* @param Meta_Tags_Context $context The context object.
* @param Abstract_Schema_Piece $graph_piece_generator The graph piece generator.
* @param Abstract_Schema_Piece[] $graph_piece_generators The graph piece generators.
*
* @return array The (potentially altered) schema graph.
*/
public function filter_author_graph( $data, $context, $graph_piece_generator, $graph_piece_generators ) {
if ( ! isset( $data['image']['url'] ) ) {
return $data;
}
if ( isset( $data['image']['@id'] ) ) {
$data['image']['@id'] .= \md5( $data['image']['url'] );
}
if ( isset( $data['logo']['@id'] ) ) {
$data['logo']['@id'] .= \md5( $data['image']['url'] );
}
return $data;
}
/**
* Filters the graph output to add authors.
*
* @param array $data The schema graph.
* @param Meta_Tags_Context $context Context object.
*
* @return array The (potentially altered) schema graph.
*/
public function filter_graph( $data, $context ) {
if ( ! \is_singular() ) {
return $data;
}
if ( ! \function_exists( '\get_coauthors' ) ) {
return $data;
}
/**
* Contains the authors from the CoAuthors Plus plugin.
*
* @var WP_User[] $author_objects
*/
$author_objects = \get_coauthors( $context->post->ID );
if ( \count( $author_objects ) <= 1 ) {
return $data;
}
$ids = [];
// Add the authors to the schema.
foreach ( $author_objects as $author ) {
if ( $author->ID === (int) $context->post->post_author ) {
continue;
}
$author_generator = new CoAuthor();
$author_generator->context = $context;
$author_generator->helpers = $this->helpers;
$author_data = $author_generator->generate_from_user_id( $author->ID );
if ( ! empty( $author_data ) ) {
$ids[] = [ '@id' => $author_data['@id'] ];
}
}
$schema_types = new Schema_Types();
$article_types = $schema_types->get_article_type_options_values();
// Change the author reference to reference our multiple authors.
$add_to_graph = false;
foreach ( $data as $key => $piece ) {
if ( \in_array( $piece['@type'], $article_types, true ) ) {
$data[ $key ]['author'] = \array_merge( [ $piece['author'] ], $ids );
$add_to_graph = true;
break;
}
}
if ( $add_to_graph ) {
if ( ! empty( $author_data ) ) {
if ( $context->site_represents !== 'person' || $author->ID !== $context->site_user_id ) {
$data[] = $author_data;
}
}
}
return $data;
}
}
wincher-publish.php 0000666 00000010742 15113146334 0010371 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use WP_Post;
use Yoast\WP\SEO\Actions\Wincher\Wincher_Account_Action;
use Yoast\WP\SEO\Actions\Wincher\Wincher_Keyphrases_Action;
use Yoast\WP\SEO\Conditionals\Wincher_Automatically_Track_Conditional;
use Yoast\WP\SEO\Conditionals\Wincher_Conditional;
use Yoast\WP\SEO\Conditionals\Wincher_Enabled_Conditional;
use Yoast\WP\SEO\Conditionals\Wincher_Token_Conditional;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
/**
* Handles automatically tracking published posts with Wincher.
*/
class Wincher_Publish implements Integration_Interface {
/**
* The Wincher enabled conditional.
*
* @var Wincher_Enabled_Conditional
*/
protected $wincher_enabled;
/**
* The options helper.
*
* @var Options_Helper
*/
protected $options_helper;
/**
* The Wincher keyphrases action handler.
*
* @var Wincher_Keyphrases_Action
*/
protected $keyphrases_action;
/**
* The Wincher account action handler.
*
* @var Wincher_Account_Action
*/
protected $account_action;
/**
* Wincher publish constructor.
*
* @param Wincher_Enabled_Conditional $wincher_enabled The WPML WPSEO conditional.
* @param Options_Helper $options_helper The options helper.
* @param Wincher_Keyphrases_Action $keyphrases_action The keyphrases action class.
* @param Wincher_Account_Action $account_action The account action class.
*/
public function __construct(
Wincher_Enabled_Conditional $wincher_enabled,
Options_Helper $options_helper,
Wincher_Keyphrases_Action $keyphrases_action,
Wincher_Account_Action $account_action
) {
$this->wincher_enabled = $wincher_enabled;
$this->options_helper = $options_helper;
$this->keyphrases_action = $keyphrases_action;
$this->account_action = $account_action;
}
/**
* Initializes the integration.
*
* @return void
*/
public function register_hooks() {
/**
* Called in the REST API when submitting the post copy in the Block Editor.
* Runs the republishing of the copy onto the original.
*/
\add_action( 'rest_after_insert_post', [ $this, 'track_after_rest_api_request' ] );
/**
* Called by `wp_insert_post()` when submitting the post copy, which runs in two cases:
* - In the Classic Editor, where there's only one request that updates everything.
* - In the Block Editor, only when there are custom meta boxes.
*/
\add_action( 'wp_insert_post', [ $this, 'track_after_post_request' ], \PHP_INT_MAX, 2 );
}
/**
* Returns the conditionals based in which this loadable should be active.
*
* This integration should only be active when the feature is enabled, a token is available and automatically tracking is enabled.
*
* @return array The conditionals.
*/
public static function get_conditionals() {
return [
Wincher_Conditional::class,
Wincher_Enabled_Conditional::class,
Wincher_Automatically_Track_Conditional::class,
Wincher_Token_Conditional::class,
];
}
/**
* Determines whether the current request is a REST request.
*
* @return bool Whether the request is a REST request.
*/
public function is_rest_request() {
return \defined( 'REST_REQUEST' ) && \REST_REQUEST;
}
/**
* Sends the keyphrases associated with the post to Wincher for automatic tracking.
*
* @param WP_Post $post The post to extract the keyphrases from.
*
* @return void
*/
public function track_request( $post ) {
if ( ! $post instanceof WP_Post ) {
return;
}
// Filter out empty entries.
$keyphrases = \array_filter( $this->keyphrases_action->collect_keyphrases_from_post( $post ) );
if ( ! empty( $keyphrases ) ) {
$this->keyphrases_action->track_keyphrases( $keyphrases, $this->account_action->check_limit() );
}
}
/**
* Republishes the original post with the passed post, when using the Block Editor.
*
* @param WP_Post $post The copy's post object.
*
* @return void
*/
public function track_after_rest_api_request( $post ) {
$this->track_request( $post );
}
/**
* Republishes the original post with the passed post, when using the Classic Editor.
*
* Runs also in the Block Editor to save the custom meta data only when there
* are custom meta boxes.
*
* @param int $post_id The copy's post ID.
* @param WP_Post $post The copy's post object.
*
* @return void
*/
public function track_after_post_request( $post_id, $post ) {
if ( $this->is_rest_request() ) {
return;
}
$this->track_request( $post );
}
}
the-events-calendar.php 0000666 00000002447 15113146334 0011122 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
use Yoast\WP\SEO\Conditionals\Open_Graph_Conditional;
use Yoast\WP\SEO\Conditionals\The_Events_Calendar_Conditional;
use Yoast\WP\SEO\Generators\Schema\Third_Party\Events_Calendar_Schema;
use Yoast\WP\SEO\Integrations\Integration_Interface;
/**
* Class The_Events_Calendar
*/
class The_Events_Calendar implements Integration_Interface {
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Front_End_Conditional::class, The_Events_Calendar_Conditional::class, Open_Graph_Conditional::class ];
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
\add_filter( 'wpseo_schema_graph_pieces', [ $this, 'add_graph_pieces' ], 11, 2 );
}
/**
* Adds the events graph pieces to the schema collector.
*
* @param array $pieces The current graph pieces.
* @param string $context The current context.
*
* @return array Extended graph pieces.
*/
public function add_graph_pieces( $pieces, $context ) {
$pieces[] = new Events_Calendar_Schema( $context );
return $pieces;
}
}
woocommerce.php 0000666 00000022515 15113146334 0007606 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use WPSEO_Replace_Vars;
use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
use Yoast\WP\SEO\Conditionals\WooCommerce_Conditional;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Helpers\Pagination_Helper;
use Yoast\WP\SEO\Helpers\Woocommerce_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Presentations\Indexable_Presentation;
use Yoast\WP\SEO\Repositories\Indexable_Repository;
/**
* WooCommerce integration.
*/
class WooCommerce implements Integration_Interface {
/**
* The options helper.
*
* @var Options_Helper
*/
private $options;
/**
* The WPSEO Replace Vars object.
*
* @var WPSEO_Replace_Vars
*/
private $replace_vars;
/**
* The memoizer for the meta tags context.
*
* @var Meta_Tags_Context_Memoizer
*/
protected $context_memoizer;
/**
* The indexable repository.
*
* @var Indexable_Repository
*/
private $repository;
/**
* The pagination helper.
*
* @var Pagination_Helper
*/
protected $pagination_helper;
/**
* The WooCommerce helper.
*
* @var Woocommerce_Helper
*/
private $woocommerce_helper;
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ WooCommerce_Conditional::class, Front_End_Conditional::class ];
}
/**
* WooCommerce constructor.
*
* @param Options_Helper $options The options helper.
* @param WPSEO_Replace_Vars $replace_vars The replace vars helper.
* @param Meta_Tags_Context_Memoizer $context_memoizer The meta tags context memoizer.
* @param Indexable_Repository $repository The indexable repository.
* @param Pagination_Helper $pagination_helper The paginataion helper.
* @param Woocommerce_Helper $woocommerce_helper The WooCommerce helper.
*/
public function __construct(
Options_Helper $options,
WPSEO_Replace_Vars $replace_vars,
Meta_Tags_Context_Memoizer $context_memoizer,
Indexable_Repository $repository,
Pagination_Helper $pagination_helper,
Woocommerce_Helper $woocommerce_helper
) {
$this->options = $options;
$this->replace_vars = $replace_vars;
$this->context_memoizer = $context_memoizer;
$this->repository = $repository;
$this->pagination_helper = $pagination_helper;
$this->woocommerce_helper = $woocommerce_helper;
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
\add_filter( 'wpseo_frontend_page_type_simple_page_id', [ $this, 'get_page_id' ] );
\add_filter( 'wpseo_breadcrumb_indexables', [ $this, 'add_shop_to_breadcrumbs' ] );
\add_filter( 'wpseo_title', [ $this, 'title' ], 10, 2 );
\add_filter( 'wpseo_metadesc', [ $this, 'description' ], 10, 2 );
\add_filter( 'wpseo_canonical', [ $this, 'canonical' ], 10, 2 );
\add_filter( 'wpseo_adjacent_rel_url', [ $this, 'adjacent_rel_url' ], 10, 3 );
}
/**
* Returns the correct canonical when WooCommerce is enabled.
*
* @param string $canonical The current canonical.
* @param Indexable_Presentation|null $presentation The indexable presentation.
*
* @return string The correct canonical.
*/
public function canonical( $canonical, $presentation = null ) {
if ( ! $this->woocommerce_helper->is_shop_page() ) {
return $canonical;
}
$url = $this->get_shop_paginated_link( 'curr', $presentation );
if ( $url ) {
return $url;
}
return $canonical;
}
/**
* Returns correct adjacent pages when WooCommerce is enabled.
*
* @param string $link The current link.
* @param string $rel Link relationship, prev or next.
* @param Indexable_Presentation|null $presentation The indexable presentation.
*
* @return string The correct link.
*/
public function adjacent_rel_url( $link, $rel, $presentation = null ) {
if ( ! $this->woocommerce_helper->is_shop_page() ) {
return $link;
}
if ( $rel !== 'next' && $rel !== 'prev' ) {
return $link;
}
$url = $this->get_shop_paginated_link( $rel, $presentation );
if ( $url ) {
return $url;
}
return $link;
}
/**
* Adds a breadcrumb for the shop page.
*
* @param Indexable[] $indexables The array with indexables.
*
* @return Indexable[] The indexables to be shown in the breadcrumbs, with the shop page added.
*/
public function add_shop_to_breadcrumbs( $indexables ) {
$shop_page_id = $this->woocommerce_helper->get_shop_page_id();
if ( ! is_int( $shop_page_id ) || $shop_page_id < 1 ) {
return $indexables;
}
foreach ( $indexables as $index => $indexable ) {
if ( $indexable->object_type === 'post-type-archive' && $indexable->object_sub_type === 'product' ) {
$indexables[ $index ] = $this->repository->find_by_id_and_type( $shop_page_id, 'post' );
}
}
return $indexables;
}
/**
* Returns the ID of the WooCommerce shop page when the currently opened page is the shop page.
*
* @param int $page_id The page id.
*
* @return int The Page ID of the shop.
*/
public function get_page_id( $page_id ) {
if ( ! $this->woocommerce_helper->is_shop_page() ) {
return $page_id;
}
return $this->woocommerce_helper->get_shop_page_id();
}
/**
* Handles the title.
*
* @param string $title The title.
* @param Indexable_Presentation|null $presentation The indexable presentation.
*
* @return string The title to use.
*/
public function title( $title, $presentation = null ) {
$presentation = $this->ensure_presentation( $presentation );
if ( $presentation->model->title ) {
return $title;
}
if ( ! $this->woocommerce_helper->is_shop_page() ) {
return $title;
}
if ( ! \is_archive() ) {
return $title;
}
$shop_page_id = $this->woocommerce_helper->get_shop_page_id();
if ( $shop_page_id < 1 ) {
return $title;
}
$product_template_title = $this->get_product_template( 'title-product', $shop_page_id );
if ( $product_template_title ) {
return $product_template_title;
}
return $title;
}
/**
* Handles the meta description.
*
* @param string $description The title.
* @param Indexable_Presentation|null $presentation The indexable presentation.
*
* @return string The description to use.
*/
public function description( $description, $presentation = null ) {
$presentation = $this->ensure_presentation( $presentation );
if ( $presentation->model->description ) {
return $description;
}
if ( ! $this->woocommerce_helper->is_shop_page() ) {
return $description;
}
if ( ! \is_archive() ) {
return $description;
}
$shop_page_id = $this->woocommerce_helper->get_shop_page_id();
if ( $shop_page_id < 1 ) {
return $description;
}
$product_template_description = $this->get_product_template( 'metadesc-product', $shop_page_id );
if ( $product_template_description ) {
return $product_template_description;
}
return $description;
}
/**
* Uses template for the given option name and replace the replacement variables on it.
*
* @param string $option_name The option name to get the template for.
* @param string $shop_page_id The page id to retrieve template for.
*
* @return string The rendered value.
*/
protected function get_product_template( $option_name, $shop_page_id ) {
$template = $this->options->get( $option_name );
$page = \get_post( $shop_page_id );
return $this->replace_vars->replace( $template, $page );
}
/**
* Get paginated link for shop page.
*
* @param string $rel Link relationship, prev or next or curr.
* @param Indexable_Presentation|null $presentation The indexable presentation.
*
* @return string|null The link.
*/
protected function get_shop_paginated_link( $rel, $presentation = null ) {
$presentation = $this->ensure_presentation( $presentation );
$permalink = $presentation->permalink;
if ( ! $permalink ) {
return null;
}
$current_page = \max( 1, $this->pagination_helper->get_current_archive_page_number() );
if ( $rel === 'curr' && $current_page === 1 ) {
return $permalink;
}
if ( $rel === 'curr' && $current_page > 1 ) {
return $this->pagination_helper->get_paginated_url( $permalink, $current_page );
}
if ( $rel === 'prev' && $current_page === 2 ) {
return $permalink;
}
if ( $rel === 'prev' && $current_page > 2 ) {
return $this->pagination_helper->get_paginated_url( $permalink, ( $current_page - 1 ) );
}
if ( $rel === 'next' && $current_page < $this->pagination_helper->get_number_of_archive_pages() ) {
return $this->pagination_helper->get_paginated_url( $permalink, ( $current_page + 1 ) );
}
return null;
}
/**
* Ensures a presentation is available.
*
* @param Indexable_Presentation $presentation The indexable presentation.
*
* @return Indexable_Presentation The presentation, taken from the current page if the input was invalid.
*/
protected function ensure_presentation( $presentation ) {
if ( \is_a( $presentation, Indexable_Presentation::class ) ) {
return $presentation;
}
$context = $this->context_memoizer->for_current_page();
return $context->presentation;
}
}
w3-total-cache.php 0000666 00000001474 15113146334 0010003 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Third_Party\W3_Total_Cache_Conditional;
use Yoast\WP\SEO\Integrations\Integration_Interface;
/**
* W3 Total Cache integration.
*/
class W3_Total_Cache implements Integration_Interface {
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ W3_Total_Cache_Conditional::class ];
}
/**
* Initializes the integration.
*
* On successful update/add of the taxonomy meta option, flush the W3TC cache.
*
* @return void
*/
public function register_hooks() {
\add_action( 'add_option_wpseo_taxonomy_meta', 'w3tc_objectcache_flush' );
\add_action( 'update_option_wpseo_taxonomy_meta', 'w3tc_objectcache_flush' );
}
}
web-stories.php 0000666 00000006036 15113146334 0007532 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Web_Stories_Conditional;
use Yoast\WP\SEO\Integrations\Front_End_Integration;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Presentations\Indexable_Presentation;
/**
* Web Stories integration.
*/
class Web_Stories implements Integration_Interface {
/**
* The front end integration.
*
* @var Front_End_Integration
*/
protected $front_end;
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Web_Stories_Conditional::class ];
}
/**
* Constructs the Web Stories integration
*
* @param Front_End_Integration $front_end The front end integration.
*/
public function __construct( Front_End_Integration $front_end ) {
$this->front_end = $front_end;
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
// If the title presenter was not removed, we will disable the web stories metadata because Yoast SEO will output the title already.
if ( ! $this->front_end->should_title_presenter_be_removed() ) {
\add_action( 'web_stories_enable_metadata', '__return_false' );
}
\add_action( 'web_stories_enable_schemaorg_metadata', '__return_false' );
\add_action( 'web_stories_enable_open_graph_metadata', '__return_false' );
\add_action( 'web_stories_enable_twitter_metadata', '__return_false' );
\add_action( 'web_stories_story_head', [ $this, 'web_stories_story_head' ], 1 );
\add_filter( 'wpseo_schema_article_type', [ $this, 'filter_schema_article_type' ], 10, 2 );
\add_filter( 'wpseo_metadesc', [ $this, 'filter_meta_description' ], 10, 2 );
}
/**
* Hooks into web story <head> generation to modify output.
*
* @return void
*/
public function web_stories_story_head() {
\remove_action( 'web_stories_story_head', 'rel_canonical' );
\add_action( 'web_stories_story_head', [ $this->front_end, 'call_wpseo_head' ], 9 );
}
/**
* Filters the meta description for stories.
*
* @param string $description The description sentence.
* @param Indexable_Presentation $presentation The presentation of an indexable.
* @return string The description sentence.
*/
public function filter_meta_description( $description, $presentation ) {
if ( $description || $presentation->model->object_sub_type !== 'web-story' ) {
return $description;
}
return \get_the_excerpt( $presentation->model->object_id );
}
/**
* Filters Article type for Web Stories.
*
* @param string|string[] $type The Article type.
* @param Indexable $indexable The indexable.
* @return string|string[] Article type.
*/
public function filter_schema_article_type( $type, $indexable ) {
if ( $indexable->object_sub_type !== 'web-story' ) {
return $type;
}
if ( \is_string( $type ) && $type === 'None' ) {
return 'Article';
}
return $type;
}
}
wordproof.php 0000666 00000016046 15113146334 0007312 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use WPSEO_Admin_Asset;
use WPSEO_Admin_Asset_Manager;
use Yoast\WP\SEO\Conditionals\Non_Multisite_Conditional;
use Yoast\WP\SEO\Conditionals\Third_Party\Wordproof_Integration_Active_Conditional;
use Yoast\WP\SEO\Conditionals\Third_Party\Wordproof_Plugin_Inactive_Conditional;
use Yoast\WP\SEO\Config\Wordproof_App_Config;
use Yoast\WP\SEO\Config\Wordproof_Translations;
use Yoast\WP\SEO\Helpers\Wordproof_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use YoastSEO_Vendor\WordProof\SDK\Helpers\CertificateHelper;
use YoastSEO_Vendor\WordProof\SDK\Helpers\PostMetaHelper;
use YoastSEO_Vendor\WordProof\SDK\WordPressSDK;
/**
* Class WordProof
*
* @package Yoast\WP\SEO\Integrations\Third_Party
*/
class Wordproof implements Integration_Interface {
/**
* The Yoast meta key used to save if a post shiould be timestamped.
*
* @var string
*/
protected $post_meta_key = '_yoast_wpseo_wordproof_timestamp';
/**
* The WordProof helper instance.
*
* @var Wordproof_Helper
*/
protected $wordproof;
/**
* Asset manager instance.
*
* @var WPSEO_Admin_Asset_Manager
*/
protected $asset_manager;
/**
* The WordProof integration constructor.
*
* @param Wordproof_Helper $wordproof The WordProof helper instance.
* @param WPSEO_Admin_Asset_Manager $asset_manager The WPSEO admin asset manager instance.
*/
public function __construct( Wordproof_Helper $wordproof, WPSEO_Admin_Asset_Manager $asset_manager = null ) {
if ( ! $asset_manager ) {
$asset_manager = new WPSEO_Admin_Asset_Manager();
}
$this->asset_manager = $asset_manager;
$this->wordproof = $wordproof;
}
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [
Wordproof_Plugin_Inactive_Conditional::class,
Non_Multisite_Conditional::class,
Wordproof_Integration_Active_Conditional::class,
];
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
/**
* Used to initialize the WordProof WordPress SDK.
*/
\add_action( 'init', [ $this, 'sdk_setup' ], 11 );
/**
* Enqueue the wordproof assets.
*/
\add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ], 10, 0 );
/**
* Add async to the wordproof scripts.
*/
\add_filter( 'script_loader_tag', [ $this, 'add_async_to_script' ], 10, 3 );
/**
* Removes the post meta timestamp key for the old privacy page.
*/
\add_action( 'update_option_wp_page_for_privacy_policy', [ $this, 'disable_timestamp_for_previous_legal_page' ], 10, 2 );
/**
* Removes the post meta timestamp key for the old terms and conditions page.
*/
\add_action( 'update_option_woocommerce_terms_page_id', [ $this, 'disable_timestamp_for_previous_legal_page' ], 10, 2 );
/**
* Called by the WordProof WordPress SDK to determine if the post should be timestamped.
*/
\add_filter( 'wordproof_timestamp_post_meta_key_overrides', [ $this, 'add_post_meta_key' ] );
/**
* Called by the WordProof WordPress SDK to determine if the post should be automatically timestamped.
*/
\add_filter( 'wordproof_timestamp_post_types', [ $this, 'wordproof_timestamp_post_types' ] );
/**
* Called by the WordProof WordPress SDK to determine if the certificate should be shown.
*/
\add_filter( 'wordproof_timestamp_show_certificate', [ $this, 'show_certificate' ], 10, 2 );
/**
* Called by WPSEO_Meta to add extra meta fields to the ones defined there.
*/
\add_filter( 'add_extra_wpseo_meta_fields', [ $this, 'add_meta_field' ] );
}
/**
* Initializes the WordProof WordPress SDK.
*/
public function sdk_setup() {
$config = new Wordproof_App_Config();
$translations = new Wordproof_Translations();
WordPressSDK::getInstance( $config, $translations )
->certificate()
->initialize();
}
/**
* Removes the WordProof timestamp post meta if a legal page is changed.
*
* @param int $old_post_id The old post id.
* @param int $new_post_id The new post id.
*/
public function disable_timestamp_for_previous_legal_page( $old_post_id, $new_post_id ) {
if ( $old_post_id !== $new_post_id ) {
\delete_post_meta( $old_post_id, '_yoast_wpseo_wordproof_timestamp' );
}
}
/**
* Return the Yoast post meta key for the SDK to determine if the post should be timestamped.
*
* @param array $meta_keys The array containing meta keys that should be used.
* @return array
*/
public function add_post_meta_key( $meta_keys ) {
return [ $this->post_meta_key ];
}
/**
* Return an empty array to disable automatically timestamping selected post types.
*
* @param array $post_types The array containing post types that should be automatically timestamped.
* @return array
*/
public function wordproof_timestamp_post_types( $post_types ) {
return [];
}
/**
* This filters hides the certificate if the Yoast post meta key is not set to true.
*
* @param bool $value If the certificate should be shown.
* @param WP_Post $post The post object of the post for which to determine the certificate should be shown.
* @return bool|null
*/
public function show_certificate( $value, $post ) {
if ( ! $value ) {
return $value;
}
if ( ! $this->wordproof->integration_is_active() ) {
return false;
}
return \boolval( PostMetaHelper::get( $post->ID, $this->post_meta_key ) );
}
/**
* Adds the WordProof integration toggle to the array.
*
* @param array $fields The currently registered meta fields.
*
* @return array A new array with meta fields.
*/
public function add_meta_field( $fields ) {
$fields['advanced']['wordproof_timestamp'] = [
'type' => 'hidden',
'title' => '',
'default_value' => '',
'description' => '0',
];
return $fields;
}
/**
* Enqueue the uikit script.
*
* @return void
*/
public function enqueue_assets() {
if ( CertificateHelper::show() ) {
$flat_version = $this->asset_manager->flatten_version( \WPSEO_VERSION );
/**
* We are using the Admin asset manager to register and enqueue a file served for all visitors,
* authenticated and unauthenticated users.
*/
$script = new WPSEO_Admin_Asset(
[
'name' => 'wordproof-uikit',
'src' => 'wordproof-uikit.js',
'version' => $flat_version,
]
);
$this->asset_manager->register_script( $script );
$this->asset_manager->enqueue_script( 'wordproof-uikit' );
}
}
/**
* Adds async to the wordproof-uikit script.
*
* @param string $tag The script tag for the enqueued script.
* @param string $handle The script's registered handle.
* @param string $src The script's source URL.
*
* @return string The script's tag.
*
* @phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript
*/
public function add_async_to_script( $tag, $handle, $src ) {
if ( $handle !== WPSEO_Admin_Asset_Manager::PREFIX . 'wordproof-uikit' ) {
return $tag;
}
return "<script src={$src} async></script>";
}
}
wpml.php 0000666 00000003526 15113146334 0006247 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Third_Party\WPML_Conditional;
use Yoast\WP\SEO\Integrations\Integration_Interface;
/**
* WPML integration.
*
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded -- Known false positive with acronyms. Fix expected in YoastCS 3.x.
*/
class WPML implements Integration_Interface {
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
\add_action( 'wpseo_home_url', [ $this, 'filter_home_url_before' ] );
\add_filter( 'home_url', [ $this, 'filter_home_url_after' ], 100 );
}
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ WPML_Conditional::class ];
}
/**
* Adds a filter to WPML's wpml_get_home_url filter to ensure we get the unmanipulated home URL.
*/
public function filter_home_url_before() {
\add_filter( 'wpml_get_home_url', [ $this, 'wpml_get_home_url' ], 10, 2 );
}
/**
* Removes the wpml_get_home_url filter to return the WPML, language-enriched home URL.
*
* @param string $home_url The filtered home URL.
*
* @return string The unfiltered home URL.
*/
public function filter_home_url_after( $home_url ) {
\remove_filter( 'wpml_get_home_url', [ $this, 'wpml_get_home_url' ], 10 );
return $home_url;
}
/**
* Returns the original URL instead of the language-enriched URL.
* This method gets automatically triggered by the wpml_get_home_url filter.
*
* @param string $home_url The url altered by WPML. Unused.
* @param string $url The url that isn't altered by WPML.
*
* @return string The original url.
*/
public function wpml_get_home_url( $home_url, $url ) {
return $url;
}
}
exclude-woocommerce-post-types.php 0000666 00000001613 15113146334 0013356 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\WooCommerce_Conditional;
use Yoast\WP\SEO\Integrations\Abstract_Exclude_Post_Type;
/**
* Excludes certain WooCommerce-specific post types from the indexable table.
*
* Posts with these post types will not be saved to the indexable table.
*/
class Exclude_WooCommerce_Post_Types extends Abstract_Exclude_Post_Type {
/**
* This integration is only active when the WooCommerce plugin
* is installed and activated.
*
* @return array|string[] The conditionals.
*/
public static function get_conditionals() {
return [ WooCommerce_Conditional::class ];
}
/**
* Returns the names of the post types to be excluded.
* To be used in the wpseo_indexable_excluded_post_types filter.
*
* @return array The names of the post types.
*/
public function get_post_type() {
return [ 'shop_order' ];
}
}
jetpack.php 0000666 00000001504 15113146334 0006703 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
use Yoast\WP\SEO\Conditionals\Jetpack_Conditional;
use Yoast\WP\SEO\Conditionals\Open_Graph_Conditional;
use Yoast\WP\SEO\Integrations\Integration_Interface;
/**
* Jetpack integration.
*/
class Jetpack implements Integration_Interface {
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Front_End_Conditional::class, Jetpack_Conditional::class, Open_Graph_Conditional::class ];
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
\add_filter( 'jetpack_enable_open_graph', '__return_false' );
}
}
woocommerce-permalinks.php 0000666 00000005642 15113146334 0011753 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Migrations_Conditional;
use Yoast\WP\SEO\Conditionals\WooCommerce_Conditional;
use Yoast\WP\SEO\Helpers\Indexable_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
/**
* The permalink watcher.
*/
class Woocommerce_Permalinks implements Integration_Interface {
/**
* Represents the indexable helper.
*
* @var Indexable_Helper
*/
protected $indexable_helper;
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ WooCommerce_Conditional::class, Migrations_Conditional::class ];
}
/**
* Constructor.
*
* @param Indexable_Helper $indexable_helper Indexable Helper.
*/
public function __construct( Indexable_Helper $indexable_helper ) {
$this->indexable_helper = $indexable_helper;
}
/**
* Registers the hooks.
*
* @codeCoverageIgnore
*/
public function register_hooks() {
\add_filter( 'wpseo_post_types_reset_permalinks', [ $this, 'filter_product_from_post_types' ] );
\add_action( 'update_option_woocommerce_permalinks', [ $this, 'reset_woocommerce_permalinks' ], 10, 2 );
}
/**
* Filters the product post type from the post type.
*
* @param array $post_types The post types to filter.
*
* @return array The filtered post types.
*/
public function filter_product_from_post_types( $post_types ) {
unset( $post_types['product'] );
return $post_types;
}
/**
* Resets the indexables for WooCommerce based on the changed permalink fields.
*
* @param array $old_value The old value.
* @param array $new_value The new value.
*/
public function reset_woocommerce_permalinks( $old_value, $new_value ) {
$changed_options = \array_diff( $old_value, $new_value );
if ( \array_key_exists( 'product_base', $changed_options ) ) {
$this->indexable_helper->reset_permalink_indexables( 'post', 'product' );
}
if ( \array_key_exists( 'attribute_base', $changed_options ) ) {
$attribute_taxonomies = $this->get_attribute_taxonomies();
foreach ( $attribute_taxonomies as $attribute_name ) {
$this->indexable_helper->reset_permalink_indexables( 'term', $attribute_name );
}
}
if ( \array_key_exists( 'category_base', $changed_options ) ) {
$this->indexable_helper->reset_permalink_indexables( 'term', 'product_cat' );
}
if ( \array_key_exists( 'tag_base', $changed_options ) ) {
$this->indexable_helper->reset_permalink_indexables( 'term', 'product_tag' );
}
}
/**
* Retrieves the taxonomies based on the attributes.
*
* @return array The taxonomies.
*/
protected function get_attribute_taxonomies() {
$taxonomies = [];
foreach ( \wc_get_attribute_taxonomies() as $attribute_taxonomy ) {
$taxonomies[] = \wc_attribute_taxonomy_name( $attribute_taxonomy->attribute_name );
}
$taxonomies = \array_filter( $taxonomies );
return $taxonomies;
}
}
wpml-wpseo-notification.php 0000666 00000006576 15113146334 0012076 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Third_Party\WPML_Conditional;
use Yoast\WP\SEO\Conditionals\Third_Party\WPML_WPSEO_Conditional;
use Yoast\WP\SEO\Helpers\Short_Link_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use Yoast_Notification;
use Yoast_Notification_Center;
/**
* Adds a notification to the dashboard if the WPML plugin is installed,
* but the Yoast SEO Multilingual plugin (a glue plugin to make Yoast SEO and WPML work nicely together)
* is not.
*/
class WPML_WPSEO_Notification implements Integration_Interface {
/**
* The notification ID.
*
* @internal
*/
const NOTIFICATION_ID = 'wpml-wpseo-not-installed';
/**
* The short link helper.
*
* @var Short_Link_Helper
*/
protected $short_link_helper;
/**
* The notification center.
*
* @var Yoast_Notification_Center
*/
protected $notification_center;
/**
* The WPML WPSEO conditional.
*
* @var WPML_WPSEO_Conditional
*/
protected $wpml_wpseo_conditional;
/**
* WPML WPSEO notification constructor.
*
* @param Short_Link_Helper $short_link_helper The short link helper.
* @param Yoast_Notification_Center $notification_center The notification center.
* @param WPML_WPSEO_Conditional $wpml_wpseo_conditional The WPML WPSEO conditional.
*/
public function __construct(
Short_Link_Helper $short_link_helper,
Yoast_Notification_Center $notification_center,
WPML_WPSEO_Conditional $wpml_wpseo_conditional
) {
$this->short_link_helper = $short_link_helper;
$this->notification_center = $notification_center;
$this->wpml_wpseo_conditional = $wpml_wpseo_conditional;
}
/**
* Initializes the integration.
*
* @return void
*/
public function register_hooks() {
\add_action( 'admin_notices', [ $this, 'notify_not_installed' ] );
}
/**
* Returns the conditionals based in which this loadable should be active.
*
* This integration should only be active when WPML is installed and activated.
*
* @return array The conditionals.
*/
public static function get_conditionals() {
return [ WPML_Conditional::class ];
}
/**
* Notify the user that the Yoast SEO Multilingual plugin is not installed
* (when the WPML plugin is installed).
*
* Remove the notification again when it is installed.
*
* @return void
*/
public function notify_not_installed() {
if ( ! $this->wpml_wpseo_conditional->is_met() ) {
$this->notification_center->add_notification( $this->get_notification() );
return;
}
$this->notification_center->remove_notification_by_id( self::NOTIFICATION_ID );
}
/**
* Generates the notification to show to the user when WPML is installed,
* but the Yoast SEO Multilingual plugin is not.
*
* @return Yoast_Notification The notification.
*/
protected function get_notification() {
return new Yoast_Notification(
\sprintf(
/* translators: %1$s expands to an opening anchor tag, %2$s expands to an closing anchor tag. */
\__( 'We notice that you have installed WPML. To make sure your canonical URLs are set correctly, %1$sinstall and activate the Yoast SEO Multilingual add-on%2$s as well!', 'wordpress-seo' ),
'<a href="' . \esc_url( $this->short_link_helper->get( 'https://yoa.st/wpml-yoast-seo' ) ) . '" target="_blank">',
'</a>'
),
[
'id' => self::NOTIFICATION_ID,
'type' => Yoast_Notification::WARNING,
]
);
}
}
bbpress.php 0000666 00000002651 15113146334 0006726 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
/**
* BbPress integration.
*/
class BbPress implements Integration_Interface {
/**
* The options helper.
*
* @var Options_Helper
*/
private $options;
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Front_End_Conditional::class ];
}
/**
* BbPress constructor.
*
* @codeCoverageIgnore It only sets dependencies.
*
* @param Options_Helper $options The options helper.
*/
public function __construct( Options_Helper $options ) {
$this->options = $options;
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
if ( $this->options->get( 'breadcrumbs-enable' ) !== true ) {
return;
}
/**
* If breadcrumbs are active (which they supposedly are if the users has enabled this settings,
* there's no reason to have bbPress breadcrumbs as well.
*
* {@internal The class itself is only loaded when the template tag is encountered
* via the template tag function in the wpseo-functions.php file.}}
*/
\add_filter( 'bbp_get_breadcrumb', '__return_false' );
}
}
wordproof-integration-toggle.php 0000666 00000011355 15113146334 0013110 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
use Yoast\WP\SEO\Helpers\Wordproof_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use Yoast_Feature_Toggle;
/**
* Class WordProofIntegrationToggle.
*
* @package Yoast\WP\SEO\Integrations\Third_Party
*/
class Wordproof_Integration_Toggle implements Integration_Interface {
/**
* The WordProof helper instance.
*
* @var Wordproof_Helper
*/
protected $wordproof;
/**
* The WordProof integration toggle constructor.
*
* @param Wordproof_Helper $wordproof The WordProof helper instance.
*/
public function __construct( Wordproof_Helper $wordproof ) {
$this->wordproof = $wordproof;
}
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Admin_Conditional::class ];
}
/**
* Initializes the integration.
*
* This is the place to register hooks and filters.
*
* @return void
*/
public function register_hooks() {
/**
* Called by Yoast_Integration_Toggles to add extra toggles to the ones defined there.
*/
\add_filter( 'wpseo_integration_toggles', [ $this, 'add_integration_toggle' ] );
/**
* Update the default wordproof_integration_active depending if the integration is disabled or not.
*/
\add_filter( 'wpseo_option_wpseo_defaults', [ $this, 'default_values' ] );
/**
* Add extra text after the integration toggle if the toggle is disabled.
*/
\add_action( 'Yoast\WP\SEO\admin_integration_after', [ $this, 'after_integration_toggle' ] );
/**
* Add extra text after the network integration toggle if the toggle is disabled.
*/
\add_action( 'Yoast\WP\SEO\admin_network_integration_after', [ $this, 'after_network_integration_toggle' ] );
}
/**
* Adds the WordProof integration toggle to the array.
*
* @param array $integration_toggles The integration toggles array.
*
* @return array The updated integration toggles array.
*/
public function add_integration_toggle( $integration_toggles ) {
if ( \is_array( $integration_toggles ) ) {
$integration_toggles[] = (object) [
/* translators: %s expands to WordProof */
'name' => \sprintf( \__( '%s integration', 'wordpress-seo' ), 'WordProof' ),
'setting' => 'wordproof_integration_active',
'label' => \sprintf(
/* translators: %s expands to WordProof */
\__( '%1$s can be used to timestamp your privacy page.', 'wordpress-seo' ),
'WordProof'
),
/* translators: %s expands to WordProof */
'read_more_label' => \sprintf( \__( 'Read more about how %s works.', 'wordpress-seo' ), 'WordProof ' ),
'read_more_url' => 'https://yoa.st/wordproof-integration',
'order' => 16,
'disabled' => $this->wordproof->integration_is_disabled(),
'new' => true,
];
}
return $integration_toggles;
}
/**
* Set the default WordProof integration option value depending if the integration is disabled or not.
*
* @param array $defaults Array containing default wpseo options.
*
* @return array
*/
public function default_values( $defaults ) {
if ( $this->wordproof->integration_is_disabled() ) {
$defaults['wordproof_integration_active'] = false;
}
return $defaults;
}
/**
* Add an explainer when the integration toggle is disabled.
*
* @param Yoast_Feature_Toggle $integration The integration toggle class.
*/
public function after_integration_toggle( $integration ) {
if ( $integration->setting === 'wordproof_integration_active' ) {
if ( $integration->disabled ) {
$conditional = $this->wordproof->integration_is_disabled( true );
if ( $conditional === 'Non_Multisite_Conditional' ) {
echo '<p>' . \sprintf(
/* translators: %s expands to WordProof */
\esc_html__( 'Currently, the %s integration is not available for multisites.', 'wordpress-seo' ),
'WordProof'
) . '</p>';
}
if ( $conditional === 'Wordproof_Plugin_Inactive_Conditional' ) {
echo '<p>' . \esc_html__( 'The WordProof Timestamp plugin needs to be disabled before you can activate this integration.', 'wordpress-seo' ) . '</p>';
}
}
}
}
/**
* Add an explainer when the network integration toggle is disabled.
*
* @param Yoast_Feature_Toggle $integration The integration toggle class.
*/
public function after_network_integration_toggle( $integration ) {
if ( $integration->setting === 'wordproof_integration_active' ) {
if ( $integration->disabled ) {
echo '<p>' . \sprintf(
/* translators: %s expands to WordProof */
\esc_html__( 'Currently, the %s integration is not available for multisites.', 'wordpress-seo' ),
'WordProof'
) . '</p>';
}
}
}
}
exclude-elementor-post-types.php 0000666 00000001650 15113146334 0013032 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Third_Party\Elementor_Activated_Conditional;
use Yoast\WP\SEO\Integrations\Abstract_Exclude_Post_Type;
/**
* Excludes certain Elementor-specific post types from the indexable table.
*
* Posts with these post types will not be saved to the indexable table.
*/
class Exclude_Elementor_Post_Types extends Abstract_Exclude_Post_Type {
/**
* This integration is only active when the Elementor plugin
* is installed and activated.
*
* @return array|string[] The conditionals.
*/
public static function get_conditionals() {
return [ Elementor_Activated_Conditional::class ];
}
/**
* Returns the names of the post types to be excluded.
* To be used in the wpseo_indexable_excluded_post_types filter.
*
* @return array The names of the post types.
*/
public function get_post_type() {
return [ 'elementor_library' ];
}
}
woocommerce-post-edit.php 0000666 00000002740 15113146334 0011512 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use WP_Post;
use Yoast\WP\SEO\Conditionals\Admin\Post_Conditional;
use Yoast\WP\SEO\Conditionals\WooCommerce_Conditional;
use Yoast\WP\SEO\Integrations\Integration_Interface;
/**
* A WooCommerce integration that runs in the post editor.
*/
class WooCommerce_Post_Edit implements Integration_Interface {
/**
* Register the hooks for this integration to work.
*
* @return void
*/
public function register_hooks() {
\add_filter( 'wpseo_post_edit_values', [ $this, 'remove_meta_description_date' ], 10, 2 );
}
/**
* Only run this integration when WooCommerce is active and the user is in the post editor.
*
* @return string[] The conditionals that should be met before this integration is loaded.
*/
public static function get_conditionals() {
return [ WooCommerce_Conditional::class, Post_Conditional::class ];
}
/**
* Don't show the date in the Google preview for WooCommerce products,
* since Google does not show dates for product pages in the search results.
*
* @param array $values Key-value map of variables we enqueue in the JavaScript of the post editor.
* @param WP_Post $post The post currently opened in the editor.
*
* @return array The values, where the `metaDescriptionDate` is set to the empty string.
*/
public function remove_meta_description_date( $values, $post ) {
if ( $post->post_type === 'product' ) {
$values['metaDescriptionDate'] = '';
}
return $values;
}
}
wincher.php 0000666 00000006654 15113146334 0006734 0 ustar 00 <?php
namespace Yoast\WP\SEO\Integrations\Third_Party;
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
use Yoast\WP\SEO\Helpers\Wincher_Helper;
use Yoast\WP\SEO\Integrations\Integration_Interface;
use Yoast_Feature_Toggle;
/**
* Adds the Wincher integration.
*/
class Wincher implements Integration_Interface {
/**
* The Wincher helper instance.
*
* @var Wincher_Helper
*/
protected $wincher;
/**
* The Wincher integration toggle constructor.
*
* @param Wincher_Helper $wincher The Wincher helper instance.
*/
public function __construct( Wincher_Helper $wincher ) {
$this->wincher = $wincher;
}
/**
* Initializes the integration.
*
* @return void
*/
public function register_hooks() {
/**
* Called by Yoast_Integration_Toggles to add extra toggles to the ones defined there.
*/
\add_filter( 'wpseo_integration_toggles', [ $this, 'add_integration_toggle' ] );
/**
* Called in dashboard/integrations.php to put additional content after the toggle.
*/
\add_action( 'Yoast\WP\SEO\admin_integration_after', [ $this, 'after_integration_toggle' ] );
/**
* Add extra text after the network integration toggle if the toggle is disabled.
*/
\add_action( 'Yoast\WP\SEO\admin_network_integration_after', [ $this, 'after_network_integration_toggle' ] );
}
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array The conditionals.
*/
public static function get_conditionals() {
return [ Admin_Conditional::class ];
}
/**
* Adds the Wincher integration toggle to the $integration_toggles array.
*
* @param array $integration_toggles The integration toggles array.
*
* @return array The updated integration toggles array.
*/
public function add_integration_toggle( $integration_toggles ) {
if ( \is_array( $integration_toggles ) ) {
$integration_toggles[] = (object) [
/* translators: %s: 'Wincher' */
'name' => \sprintf( \__( '%s integration', 'wordpress-seo' ), 'Wincher' ),
'setting' => 'wincher_integration_active',
'label' => \sprintf(
/* translators: %s: 'Wincher' */
\__( 'The %s integration offers the option to track specific keyphrases and gain insights in their positions.', 'wordpress-seo' ),
'Wincher'
),
'order' => 11,
'disabled' => \is_multisite(),
];
}
return $integration_toggles;
}
/**
* Adds the disabled note when the integration toggle is disabled.
*
* @param Yoast_Feature_Toggle $integration The integration toggle class.
*/
public function after_integration_toggle( $integration ) {
if ( $integration->setting === 'wincher_integration_active' ) {
require \WPSEO_PATH . 'admin/views/tabs/metas/paper-content/integrations/wincher.php';
if ( \is_multisite() ) {
$this->get_disabled_note();
}
}
}
/**
* Adds the disabled note to the network integration toggle.
*
* @param Yoast_Feature_Toggle $integration The integration toggle class.
*/
public function after_network_integration_toggle( $integration ) {
if ( $integration->setting === 'wincher_integration_active' ) {
$this->get_disabled_note();
}
}
/**
* Outputs the disabled note.
*
* @codeCoverageIgnore
*
* @return void
*/
protected function get_disabled_note() {
echo '<p>', \sprintf(
/* translators: %s expands to Wincher */
\esc_html__( 'Currently, the %s integration is not available for multisites.', 'wordpress-seo' ),
'Wincher'
), '</p>';
}
}
translatepress-conditional.php 0000666 00000000710 15113164176 0012637 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
/**
* Conditional that is only met when the TranslatePress plugin is active.
*/
class TranslatePress_Conditional implements Conditional {
/**
* Checks whether the TranslatePress plugin is active.
*
* @return bool Whether the TranslatePress plugin is active.
*/
public function is_met() {
return \class_exists( 'TRP_Translate_Press' );
}
}
wpml-conditional.php 0000666 00000000627 15113164176 0010553 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
/**
* Conditional that is only met when WPML is active.
*/
class WPML_Conditional implements Conditional {
/**
* Returns whether or not this conditional is met.
*
* @return bool Whether or not the conditional is met.
*/
public function is_met() {
return \defined( 'WPML_PLUGIN_FILE' );
}
}
coauthors-plus-flag-conditional.php 0000666 00000000721 15113164176 0013466 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Feature_Flag_Conditional;
/**
* Feature flag conditional for the CoAuthors Plus integration.
*/
class CoAuthors_Plus_Flag_Conditional extends Feature_Flag_Conditional {
/**
* Returns the name of the CoAuthors Plus integration feature flag.
*
* @return string The name of the feature flag.
*/
protected function get_feature_flag() {
return 'COAUTHORS_PLUS';
}
}
wordproof-plugin-inactive-conditional.php 0000666 00000000751 15113164176 0014707 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
/**
* Conditional that is met when the WordProof Timestamp plugin is inactive.
*/
class Wordproof_Plugin_Inactive_Conditional implements Conditional {
/**
* Returns whether or not the WordProof Timestamp plugin is active.
*
* @return bool Whether or not the WordProof Timestamp plugin is active.
*/
public function is_met() {
return ! \defined( 'WORDPROOF_VERSION' );
}
}
wpml-wpseo-conditional.php 0000666 00000001301 15113164176 0011674 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
/**
* Conditional that is met when the Yoast SEO Multilingual plugin,
* a glue plugin developed by and for WPML, is active.
*/
class WPML_WPSEO_Conditional implements Conditional {
/**
* Path to the Yoast SEO Multilingual plugin file.
*
* @internal
*/
const PATH_TO_WPML_WPSEO_PLUGIN_FILE = 'wp-seo-multilingual/plugin.php';
/**
* Returns whether or not the Yoast SEO Multilingual plugin is active.
*
* @return bool Whether or not the Yoast SEO Multilingual plugin is active.
*/
public function is_met() {
return \is_plugin_active( self::PATH_TO_WPML_WPSEO_PLUGIN_FILE );
}
}
wordproof-integration-active-conditional.php 0000666 00000001516 15113164176 0015405 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
use Yoast\WP\SEO\Helpers\Wordproof_Helper;
/**
* Conditional that is met when the WordProof integration is toggled on.
*/
class Wordproof_Integration_Active_Conditional implements Conditional {
/**
* The WordProof helper.
*
* @var Wordproof_Helper
*/
private $wordproof;
/**
* WordProof integration active constructor.
*
* @param Wordproof_Helper $wordproof The options helper.
*/
public function __construct( Wordproof_Helper $wordproof ) {
$this->wordproof = $wordproof;
}
/**
* Returns whether or not the WordProof Timestamp plugin is active.
*
* @return bool Whether or not the WordProof Timestamp plugin is active.
*/
public function is_met() {
return $this->wordproof->integration_is_active();
}
}
coauthors-plus-activated-conditional.php 0000666 00000000775 15113164176 0014532 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
/**
* Conditional that is met when the CoAuthors Plus plugin is installed and activated.
*/
class CoAuthors_Plus_Activated_Conditional implements Conditional {
/**
* Checks if the CoAuthors Plus plugin is installed and activated.
*
* @return bool `true` when the CoAuthors Plus plugin is installed and activated.
*/
public function is_met() {
return \defined( 'COAUTHORS_PLUS_VERSION' );
}
}
polylang-conditional.php 0000666 00000000666 15113164176 0011424 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
/**
* Conditional that is only met when the Polylang plugin is active.
*/
class Polylang_Conditional implements Conditional {
/**
* Checks whether the Polylang plugin is installed and active.
*
* @return bool Whether Polylang is installed and active.
*/
public function is_met() {
return \defined( 'POLYLANG_FILE' );
}
}
w3-total-cache-conditional.php 0000666 00000000747 15113164176 0012312 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
/**
* Conditional that is only met when in the admin.
*/
class W3_Total_Cache_Conditional implements Conditional {
/**
* Returns whether or not this conditional is met.
*
* @return bool Whether or not the conditional is met.
*/
public function is_met() {
if ( ! \defined( 'W3TC_DIR' ) ) {
return false;
}
return \function_exists( 'w3tc_objectcache_flush' );
}
}
elementor-activated-conditional.php 0000666 00000000745 15113164176 0013531 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
/**
* Conditional that is met when the Elementor plugin is installed and activated.
*/
class Elementor_Activated_Conditional implements Conditional {
/**
* Checks if the Elementor plugins is installed and activated.
*
* @return bool `true` when the Elementor plugin is installed and activated.
*/
public function is_met() {
return \defined( 'ELEMENTOR__FILE__' );
}
}
elementor-edit-conditional.php 0000666 00000001612 15113164176 0012504 0 ustar 00 <?php
namespace Yoast\WP\SEO\Conditionals\Third_Party;
use Yoast\WP\SEO\Conditionals\Conditional;
/**
* Conditional that is only met when on an Elementor edit page or when the current
* request is an ajax request for saving our post meta data.
*/
class Elementor_Edit_Conditional implements Conditional {
/**
* Returns whether or not this conditional is met.
*
* @return bool Whether or not the conditional is met.
*/
public function is_met() {
global $pagenow;
// Check if we are on an Elementor edit page.
$get_action = \filter_input( \INPUT_GET, 'action', \FILTER_SANITIZE_STRING );
if ( $pagenow === 'post.php' && $get_action === 'elementor' ) {
return true;
}
// Check if we are in our Elementor ajax request.
$post_action = \filter_input( \INPUT_POST, 'action', \FILTER_SANITIZE_STRING );
return \wp_doing_ajax() && $post_action === 'wpseo_elementor_save';
}
}
events-calendar-schema.php 0000666 00000015174 15114433065 0011604 0 ustar 00 <?php
namespace Yoast\WP\SEO\Generators\Schema\Third_Party;
use stdClass;
use Tribe__Events__JSON_LD__Event;
use Tribe__Events__Template__Month;
use Yoast\WP\SEO\Config\Schema_IDs;
use Yoast\WP\SEO\Context\Meta_Tags_Context;
use Yoast\WP\SEO\Generators\Schema\Abstract_Schema_Piece;
use Yoast\WP\SEO\Surfaces\Helpers_Surface;
/**
* A class to handle textdomains and other Yoast Event Schema related logic..
*/
class Events_Calendar_Schema extends Abstract_Schema_Piece {
/**
* The meta tags context.
*
* @var Meta_Tags_Context
*/
public $context;
/**
* The helpers surface
*
* @var Helpers_Surface
*/
public $helpers;
/**
* Determines whether or not a piece should be added to the graph.
*
* @return bool
*/
public function is_needed() {
if ( \is_single() && \get_post_type() === 'tribe_events' ) {
// The single event view.
return true;
}
elseif ( \tribe_is_month() ) {
// The month event view.
return true;
}
return false;
}
/**
* Adds our Event piece of the graph.
* Partially lifted from the 'Tribe__JSON_LD__Abstract' class.
*
* @see https://docs.theeventscalendar.com/reference/classes/tribe__json_ld__abstract/
* @return array Event Schema markup
*/
public function generate() {
$posts = [];
if ( \is_singular( 'tribe_events' ) ) {
global $post;
$posts[] = $post;
}
elseif ( \tribe_is_month() ) {
$posts = $this->get_month_events();
}
$tribe_data = $this->get_tribe_schema( $posts );
$tribe_data = $this->transform_tribe_schema( $tribe_data );
$data = [];
foreach ( $tribe_data as $t ) {
// Cast the schema object as array, the Yoast Class can't handle objects.
$data[] = (array) $t;
}
// If the resulting array only has one entry, print it directly.
if ( \count( $data ) === 1 ) {
$data = $data[0];
$data['mainEntityOfPage'] = [ '@id' => $this->context->main_schema_id ];
if ( $this->context->has_article ) {
$data['mainEntityOfPage'] = [ '@id' => $this->context->main_schema_id . Schema_IDs::ARTICLE_HASH ];
}
}
elseif ( \count( $data ) === 0 ) {
$data = false;
}
return $data;
}
/**
* Get and return the schema markup for a collection of posts.
* If the posts array is empty, only the current post is returned.
*
* @param array $posts The collection of posts we want schema markup for.
*
* @return array The tribe schema for these posts.
*/
private function get_tribe_schema( array $posts = [] ) {
$args = [
// We do not want the @context to be shown.
'context' => false,
];
$tribe_data = Tribe__Events__JSON_LD__Event::instance()->get_data( $posts, $args );
$type = \strtolower( \esc_attr( Tribe__Events__JSON_LD__Event::instance()->type ) );
foreach ( $tribe_data as $post_id => $_data ) {
Tribe__Events__JSON_LD__Event::instance()->set_type( $post_id, $type );
// Register this post as done already.
Tribe__Events__JSON_LD__Event::instance()->register( $post_id );
}
/**
* Allows the event data to be modifed by themes and other plugins.
*
* @example yoast_tec_json_ld_thing_data
* @example yoast_tec_json_ld_event_data
*
* @param array $data objects representing the Google Markup for each event.
* @param array $args the arguments used to get data
*/
$tribe_data = \apply_filters( "yoast_tec_json_ld_{$type}_data", $tribe_data, $args );
return $tribe_data;
}
/**
* Transform the tribe schema markup and adapt it to the Yoast SEO standard.
*
* @param array $data The data retrieved from the TEC plugin.
*
* @return array The transformed event data.
*/
private function transform_tribe_schema( array $data = [] ) {
$new_data = [];
foreach ( $data as $post_id => $d ) {
$permalink = \get_permalink( $post_id );
// EVENT.
// Generate an @id for the event.
$d->{'@id'} = $permalink . '#' . \strtolower( \esc_attr( $d->{'@type'} ) );
// Transform the post_thumbnail from the url to the @id of #primaryimage.
if ( \has_post_thumbnail( $post_id ) ) {
if ( \is_singular( 'tribe_events' ) ) {
// On a single view we can assume that Yoast SEO already printed the
// image schema for the post thumbnail.
$d->image = (object) [
'@id' => $permalink . '#primaryimage',
];
}
else {
$image_id = \get_post_thumbnail_id( $post_id );
$schema_id = $permalink . '#primaryimage';
$d->image = $this->helpers->schema->image->generate_from_attachment_id( $schema_id, $image_id );
}
}
if ( isset( $d->description ) && ! empty( $d->description ) ) {
// By the time the description arrives in this plugin it is heavily
// escaped. That's why we basically pull new text from the database.
$d->description = \get_the_excerpt( $post_id );
}
// ORGANIZER.
if ( \tribe_has_organizer( $post_id ) ) {
if ( ! $d->organizer ) {
$d->organizer = new stdClass();
}
$organizer_id = \tribe_get_organizer_id( $post_id );
$d->organizer->description = \get_the_excerpt( $organizer_id );
// Fix empty organizer/url and wrong organizer/sameAs.
if ( isset( $d->organizer->sameAs ) && $d->organizer->url === false ) {
$d->organizer->url = $d->organizer->sameAs;
}
unset( $d->organizer->sameAs );
}
// VENUE / LOCATION.
if ( \tribe_has_venue( $post_id ) ) {
if ( ! $d->location ) {
$d->location = new stdClass();
}
$venue_id = \tribe_get_venue_id( $post_id );
$d->location->description = \get_the_excerpt( $venue_id );
}
/*
* PERFORMER
* Unset the performer, as it is currently unused.
* @see: https://github.com/moderntribe/the-events-calendar/blob/5e737eb820c59bb9639d9ee9f4b88931a51c8554/src/Tribe/JSON_LD/Event.php#L151
*/
unset( $d->performer );
// OFFERS.
if ( isset( $d->offers ) && \is_array( $d->offers ) ) {
foreach ( $d->offers as $key => $offer ) {
unset( $d->offers[ $key ]->category );
}
}
$new_data[ $post_id ] = $d;
}
return $new_data;
}
/**
* Get an array of events for the requested month.
*
* @return array An array of posts of the custom post type event.
*/
private function get_month_events() {
$wp_query = \tribe_get_global_query_object();
$event_date = $wp_query->get( 'eventDate' );
$month = $event_date;
if ( empty( $month ) ) {
$month = \tribe_get_month_view_date();
}
$args = [
'eventDisplay' => 'custom',
'start_date' => Tribe__Events__Template__Month::calculate_first_cell_date( $month ),
'end_date' => Tribe__Events__Template__Month::calculate_final_cell_date( $month ),
'posts_per_page' => -1,
'hide_upcoming' => true,
];
return \tribe_get_events( $args );
}
}
coauthor.php 0000666 00000002730 15114433065 0007111 0 ustar 00 <?php
namespace Yoast\WP\SEO\Generators\Schema\Third_Party;
use Yoast\WP\SEO\Generators\Schema\Author;
/**
* Returns schema Author data for the CoAuthor Plus assigned user on a post.
*/
class CoAuthor extends Author {
/**
* The user ID of the author we're generating data for.
*
* @var int
*/
private $user_id;
/**
* Determine whether we should return Person schema.
*
* @return bool
*/
public function is_needed() {
return true;
}
/**
* Returns Person Schema data.
*
* @return bool|array Person data on success, false on failure.
*/
public function generate() {
$user_id = $this->determine_user_id();
if ( ! $user_id ) {
return false;
}
$data = $this->build_person_data( $user_id, true );
$data['@type'] = 'Person';
unset( $data['logo'] );
// If this is a post and the author archives are enabled, set the author archive url as the author url.
if ( $this->helpers->options->get( 'disable-author' ) !== true ) {
$data['url'] = $this->helpers->user->get_the_author_posts_url( $user_id );
}
return $data;
}
/**
* Generate the Person data given a user ID.
*
* @param int $user_id User ID.
*
* @return array|bool
*/
public function generate_from_user_id( $user_id ) {
$this->user_id = $user_id;
return $this->generate();
}
/**
* Determines a User ID for the Person data.
*
* @return bool|int User ID or false upon return.
*/
protected function determine_user_id() {
return $this->user_id;
}
}
.htaccess 0000666 00000000424 15114546007 0006351 0 ustar 00 <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>