PDF rausgenommen
This commit is contained in:
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the cleanup logic when the text link counter features has been disabled.
|
||||
*/
|
||||
class WPSEO_Link_Cleanup_Transient implements WPSEO_WordPress_Integration {
|
||||
|
||||
/**
|
||||
* Registers the hooks.
|
||||
*/
|
||||
public function register_hooks() {
|
||||
add_action( 'update_option_wpseo', array( $this, 'remove_transients_on_updated_option' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the transient when the option is updated.
|
||||
*
|
||||
* @param mixed $old_value The old value.
|
||||
* @param mixed $value The new value.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function remove_transients_on_updated_option( $old_value, $value ) {
|
||||
$option_name = 'enable_text_link_counter';
|
||||
if ( $value[ $option_name ] === false && $old_value[ $option_name ] !== $value[ $option_name ] ) {
|
||||
WPSEO_Link_Table_Accessible::cleanup();
|
||||
WPSEO_Meta_Table_Accessible::cleanup();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the link column count. This class contains the count for each post id on the current page.
|
||||
*/
|
||||
class WPSEO_Link_Column_Count {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $count = array();
|
||||
|
||||
/**
|
||||
* Sets the counts for the set target field.
|
||||
*
|
||||
* @param array $post_ids The posts to get the count for.
|
||||
*/
|
||||
public function set( $post_ids ) {
|
||||
if ( empty( $post_ids ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->count = $this->get_results( $post_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the link count for given post id.
|
||||
*
|
||||
* @param int $post_id The post id.
|
||||
* @param string $target_field The field to show.
|
||||
*
|
||||
* @return int|null The total amount of links or null if the target field
|
||||
* does not exist for the given post id.
|
||||
*/
|
||||
public function get( $post_id, $target_field = 'internal_link_count' ) {
|
||||
if ( array_key_exists( $post_id, $this->count ) && array_key_exists( $target_field, $this->count[ $post_id ] ) ) {
|
||||
return $this->count[ $post_id ][ $target_field ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the link count for the given post ids.
|
||||
*
|
||||
* @param array $post_ids Array with post_ids.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_results( $post_ids ) {
|
||||
global $wpdb;
|
||||
|
||||
$storage = new WPSEO_Meta_Storage();
|
||||
|
||||
$results = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
'
|
||||
SELECT internal_link_count, incoming_link_count, object_id
|
||||
FROM ' . $storage->get_table_name() . '
|
||||
WHERE object_id IN (' . implode( ',', array_fill( 0, count( $post_ids ), '%d' ) ) . ')',
|
||||
$post_ids
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
$output = array();
|
||||
foreach ( $results as $result ) {
|
||||
$output[ (int) $result['object_id'] ] = array(
|
||||
'internal_link_count' => $result['internal_link_count'],
|
||||
'incoming_link_count' => (int) $result['incoming_link_count'],
|
||||
);
|
||||
}
|
||||
|
||||
// Set unfound items to zero.
|
||||
foreach ( $post_ids as $post_id ) {
|
||||
if ( ! array_key_exists( $post_id, $output ) ) {
|
||||
$output[ $post_id ] = array(
|
||||
'internal_link_count' => null,
|
||||
'incoming_link_count' => 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
@ -0,0 +1,275 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the link columns. This class will add and handle the link columns.
|
||||
*/
|
||||
class WPSEO_Link_Columns {
|
||||
|
||||
/**
|
||||
* Partial column name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COLUMN_LINKED = 'linked';
|
||||
|
||||
/**
|
||||
* Partial column name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COLUMN_LINKS = 'links';
|
||||
|
||||
/**
|
||||
* @var WPSEO_Link_Column_Count
|
||||
*/
|
||||
protected $link_count;
|
||||
|
||||
/**
|
||||
* Storage to use.
|
||||
*
|
||||
* @var WPSEO_Meta_Storage
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* List of public post types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $public_post_types = array();
|
||||
|
||||
/**
|
||||
* WPSEO_Link_Columns constructor.
|
||||
*
|
||||
* @param WPSEO_Meta_Storage $storage The storage object to use.
|
||||
*/
|
||||
public function __construct( WPSEO_Meta_Storage $storage ) {
|
||||
$this->storage = $storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the hooks.
|
||||
*/
|
||||
public function register_hooks() {
|
||||
global $pagenow;
|
||||
$is_ajax_request = defined( 'DOING_AJAX' ) && DOING_AJAX;
|
||||
|
||||
if ( ! WPSEO_Metabox::is_post_overview( $pagenow ) && ! $is_ajax_request ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit when either table is not present or accessible.
|
||||
if ( ! WPSEO_Link_Table_Accessible::is_accessible() || ! WPSEO_Meta_Table_Accessible::is_accessible() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $is_ajax_request ) {
|
||||
add_action( 'admin_init', array( $this, 'set_count_objects' ) );
|
||||
}
|
||||
|
||||
// Hook into tablenav to calculate links and linked.
|
||||
add_action( 'manage_posts_extra_tablenav', array( $this, 'count_objects' ) );
|
||||
|
||||
add_filter( 'posts_clauses', array( $this, 'order_by_links' ), 1, 2 );
|
||||
add_filter( 'posts_clauses', array( $this, 'order_by_linked' ), 1, 2 );
|
||||
|
||||
add_filter( 'admin_init', array( $this, 'register_init_hooks' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hooks that require to be registered after `init`.
|
||||
*/
|
||||
public function register_init_hooks() {
|
||||
$this->public_post_types = apply_filters( 'wpseo_link_count_post_types', WPSEO_Post_Type::get_accessible_post_types() );
|
||||
|
||||
if ( is_array( $this->public_post_types ) && $this->public_post_types !== array() ) {
|
||||
array_walk( $this->public_post_types, array( $this, 'set_post_type_hooks' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the query pieces to allow ordering column by links to post.
|
||||
*
|
||||
* @param array $pieces Array of Query pieces.
|
||||
* @param \WP_Query $query The Query on which to apply.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function order_by_links( $pieces, $query ) {
|
||||
if ( 'wpseo-' . self::COLUMN_LINKS !== $query->get( 'orderby' ) ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
return $this->build_sort_query_pieces( $pieces, $query, 'internal_link_count' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the query pieces to allow ordering column by links to post.
|
||||
*
|
||||
* @param array $pieces Array of Query pieces.
|
||||
* @param \WP_Query $query The Query on which to apply.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function order_by_linked( $pieces, $query ) {
|
||||
if ( 'wpseo-' . self::COLUMN_LINKED !== $query->get( 'orderby' ) ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
return $this->build_sort_query_pieces( $pieces, $query, 'incoming_link_count' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the pieces for a sorting query.
|
||||
*
|
||||
* @param array $pieces Array of Query pieces.
|
||||
* @param \WP_Query $query The Query on which to apply.
|
||||
* @param string $field The field in the table to JOIN on.
|
||||
*
|
||||
* @return array Modified Query pieces.
|
||||
*/
|
||||
protected function build_sort_query_pieces( $pieces, $query, $field ) {
|
||||
global $wpdb;
|
||||
|
||||
// We only want our code to run in the main WP query.
|
||||
if ( ! $query->is_main_query() ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
// Get the order query variable - ASC or DESC.
|
||||
$order = strtoupper( $query->get( 'order' ) );
|
||||
|
||||
// Make sure the order setting qualifies. If not, set default as ASC.
|
||||
if ( ! in_array( $order, array( 'ASC', 'DESC' ), true ) ) {
|
||||
$order = 'ASC';
|
||||
}
|
||||
|
||||
$table = $this->storage->get_table_name();
|
||||
|
||||
$pieces['join'] .= " LEFT JOIN $table AS yst_links ON yst_links.object_id = {$wpdb->posts}.ID ";
|
||||
$pieces['orderby'] = "{$field} $order, FIELD( {$wpdb->posts}.post_status, 'publish' ) $order, {$pieces['orderby']}";
|
||||
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hooks for each post type.
|
||||
*
|
||||
* @param string $post_type The post type.
|
||||
*/
|
||||
public function set_post_type_hooks( $post_type ) {
|
||||
add_filter( 'manage_' . $post_type . '_posts_columns', array( $this, 'add_post_columns' ) );
|
||||
add_action( 'manage_' . $post_type . '_posts_custom_column', array( $this, 'column_content' ), 10, 2 );
|
||||
add_filter( 'manage_edit-' . $post_type . '_sortable_columns', array( $this, 'column_sort' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the columns for the post overview.
|
||||
*
|
||||
* @param array $columns Array with columns.
|
||||
*
|
||||
* @return array The extended array with columns.
|
||||
*/
|
||||
public function add_post_columns( $columns ) {
|
||||
if ( ! is_array( $columns ) ) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
$columns[ 'wpseo-' . self::COLUMN_LINKS ] = sprintf(
|
||||
'<span class="yoast-linked-to yoast-column-header-has-tooltip" data-tooltip-text="%1$s"><span class="screen-reader-text">%2$s</span></span>',
|
||||
esc_attr__( 'Number of outgoing internal links in this post. See "Yoast Columns" text in the help tab for more info.', 'wordpress-seo' ),
|
||||
esc_html__( 'Outgoing internal links', 'wordpress-seo' )
|
||||
);
|
||||
|
||||
if ( ! WPSEO_Link_Query::has_unprocessed_posts( $this->public_post_types ) ) {
|
||||
$columns[ 'wpseo-' . self::COLUMN_LINKED ] = sprintf(
|
||||
'<span class="yoast-linked-from yoast-column-header-has-tooltip" data-tooltip-text="%1$s"><span class="screen-reader-text">%2$s</span></span>',
|
||||
esc_attr__( 'Number of internal links linking to this post. See "Yoast Columns" text in the help tab for more info.', 'wordpress-seo' ),
|
||||
esc_html__( 'Received internal links', 'wordpress-seo' )
|
||||
);
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure we calculate all values in one query.
|
||||
*
|
||||
* @param string $target Extra table navigation location which is triggered.
|
||||
*/
|
||||
public function count_objects( $target ) {
|
||||
if ( 'top' === $target ) {
|
||||
$this->set_count_objects();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the objects to use for the count.
|
||||
*/
|
||||
public function set_count_objects() {
|
||||
global $wp_query;
|
||||
|
||||
$posts = $wp_query->get_posts();
|
||||
$post_ids = array();
|
||||
|
||||
// Post lists return a list of objects.
|
||||
if ( isset( $posts[0] ) && is_object( $posts[0] ) ) {
|
||||
$post_ids = wp_list_pluck( $posts, 'ID' );
|
||||
}
|
||||
elseif ( ! empty( $posts ) ) {
|
||||
// Page list returns an array of post IDs.
|
||||
$post_ids = array_keys( $posts );
|
||||
}
|
||||
|
||||
$post_ids = WPSEO_Link_Query::filter_unprocessed_posts( $post_ids );
|
||||
|
||||
$links = new WPSEO_Link_Column_Count();
|
||||
$links->set( $post_ids );
|
||||
|
||||
$this->link_count = $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the column content for the given column.
|
||||
*
|
||||
* @param string $column_name Column to display the content for.
|
||||
* @param int $post_id Post to display the column content for.
|
||||
*/
|
||||
public function column_content( $column_name, $post_id ) {
|
||||
$link_count = null;
|
||||
|
||||
switch ( $column_name ) {
|
||||
case 'wpseo-' . self::COLUMN_LINKS:
|
||||
$link_count = $this->link_count->get( $post_id, 'internal_link_count' );
|
||||
break;
|
||||
case 'wpseo-' . self::COLUMN_LINKED:
|
||||
if ( get_post_status( $post_id ) === 'publish' ) {
|
||||
$link_count = $this->link_count->get( $post_id, 'incoming_link_count' );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( isset( $link_count ) ) {
|
||||
echo (int) $link_count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sortable columns.
|
||||
*
|
||||
* @param array $columns Array with sortable columns.
|
||||
*
|
||||
* @return array The extended array with sortable columns.
|
||||
*/
|
||||
public function column_sort( array $columns ) {
|
||||
$columns[ 'wpseo-' . self::COLUMN_LINKS ] = 'wpseo-' . self::COLUMN_LINKS;
|
||||
$columns[ 'wpseo-' . self::COLUMN_LINKED ] = 'wpseo-' . self::COLUMN_LINKED;
|
||||
|
||||
return $columns;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents compatibility with php version 5.3.
|
||||
*/
|
||||
class WPSEO_Link_Compatibility_Notifier {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const NOTIFICATION_ID = 'wpseo-links-compatibility';
|
||||
|
||||
/**
|
||||
* Adds the notification to the notification center.
|
||||
*/
|
||||
public function add_notification() {
|
||||
Yoast_Notification_Center::get()->add_notification( $this->get_notification() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the notification from the notification center.
|
||||
*/
|
||||
public function remove_notification() {
|
||||
Yoast_Notification_Center::get()->remove_notification( $this->get_notification() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notification when the version is incompatible.
|
||||
*
|
||||
* @return Yoast_Notification The notification.
|
||||
*/
|
||||
protected function get_notification() {
|
||||
return new Yoast_Notification(
|
||||
sprintf(
|
||||
/* translators: %1$s: Yoast SEO. %2$s: Version number of Yoast SEO. */
|
||||
esc_html__( 'The %3$sText link counter%4$s feature (introduced in %1$s %2$s) is currently disabled.', 'wordpress-seo' ),
|
||||
'Yoast SEO',
|
||||
'5.0',
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
) . ' ' .
|
||||
sprintf(
|
||||
/* translators: %1$s: Yoast SEO. %2$s: PHP version %3$s: The current PHP version. */
|
||||
esc_html__( 'For this feature to work %1$s requires at least PHP version %2$s. We have detected PHP version %3$s on this website.', 'wordpress-seo' ),
|
||||
'Yoast SEO',
|
||||
'5.3',
|
||||
phpversion()
|
||||
) . '<br>' .
|
||||
sprintf(
|
||||
/* translators: %1$s: link to knowledge base article about solving PHP issue. %2$s: is anchor closing. */
|
||||
esc_html__( 'Please read the following %1$sknowledge base article%2$s to find out how to resolve this problem.', 'wordpress-seo' ),
|
||||
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/16f' ) . '" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'type' => Yoast_Notification::WARNING,
|
||||
'id' => self::NOTIFICATION_ID,
|
||||
'capabilities' => 'wpseo_manage_options',
|
||||
'priority' => 0.8,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the content processor. It will extract links from the content and
|
||||
* saves them for the given post id.
|
||||
*/
|
||||
class WPSEO_Link_Content_Processor {
|
||||
|
||||
/**
|
||||
* @var WPSEO_Link_Storage
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* @var WPSEO_Meta_Storage
|
||||
*/
|
||||
private $count_storage;
|
||||
|
||||
/**
|
||||
* Sets an instance of a storage object.
|
||||
*
|
||||
* @param WPSEO_Link_Storage $storage The storage object to use.
|
||||
* @param WPSEO_Meta_Storage $count_storage The storage object for the link
|
||||
* counts.
|
||||
*/
|
||||
public function __construct( WPSEO_Link_Storage $storage, WPSEO_Meta_Storage $count_storage ) {
|
||||
$this->storage = $storage;
|
||||
$this->count_storage = $count_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the content for the given post id.
|
||||
*
|
||||
* @param int $post_id The post id.
|
||||
* @param string $content The content to process.
|
||||
*/
|
||||
public function process( $post_id, $content ) {
|
||||
$link_extractor = new WPSEO_Link_Extractor( $content );
|
||||
$link_processor = new WPSEO_Link_Factory(
|
||||
new WPSEO_Link_Type_Classifier( home_url() ),
|
||||
new WPSEO_Link_Internal_Lookup(),
|
||||
new WPSEO_Link_Filter( get_permalink( $post_id ) )
|
||||
);
|
||||
|
||||
$extracted_links = $link_extractor->extract();
|
||||
$links = $link_processor->build( $extracted_links );
|
||||
|
||||
$internal_links = array_filter( $links, array( $this, 'filter_internal_link' ) );
|
||||
|
||||
$stored_links = $this->get_stored_internal_links( $post_id );
|
||||
|
||||
$this->storage->cleanup( $post_id );
|
||||
$this->storage->save_links( $post_id, $links );
|
||||
|
||||
$this->update_link_counts( $post_id, count( $internal_links ), array_merge( $stored_links, $internal_links ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the link counts for the post and referenced posts.
|
||||
*
|
||||
* @param int $post_id Post to update link counts for.
|
||||
* @param int|null $count Number of internal links.
|
||||
* @param array $links Links to process for incoming link count update.
|
||||
*/
|
||||
public function update_link_counts( $post_id, $count, array $links ) {
|
||||
$this->store_internal_link_count( $post_id, $count );
|
||||
$this->update_incoming_links( $post_id, $links );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the stored internal links for the supplied post.
|
||||
*
|
||||
* @param int $post_id The post to fetch links for.
|
||||
*
|
||||
* @return WPSEO_Link[] List of internal links connected to the post.
|
||||
*/
|
||||
public function get_stored_internal_links( $post_id ) {
|
||||
$links = $this->storage->get_links( $post_id );
|
||||
return array_filter( $links, array( $this, 'filter_internal_link' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters on INTERNAL links.
|
||||
*
|
||||
* @param WPSEO_Link $link Link to test type of.
|
||||
*
|
||||
* @return bool True for internal link, false for external link.
|
||||
*/
|
||||
protected function filter_internal_link( WPSEO_Link $link ) {
|
||||
return $link->get_type() === WPSEO_Link::TYPE_INTERNAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the total links for the post.
|
||||
*
|
||||
* @param int $post_id The post id.
|
||||
* @param int $internal_link_count Total amount of links in the post.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function store_internal_link_count( $post_id, $internal_link_count ) {
|
||||
$this->count_storage->save_meta_data( $post_id, array( 'internal_link_count' => $internal_link_count ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the incoming link count.
|
||||
*
|
||||
* @param int $post_id Post which is processed, this needs to be recalculated too.
|
||||
* @param WPSEO_Link[] $links Links to update the incoming link count of.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function update_incoming_links( $post_id, $links ) {
|
||||
$post_ids = $this->get_internal_post_ids( $links );
|
||||
$post_ids = array_merge( array( $post_id ), $post_ids );
|
||||
$this->count_storage->update_incoming_link_count( $post_ids, $this->storage );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the post IDs from the links.
|
||||
*
|
||||
* @param WPSEO_Link[] $links Links to update the incoming link count of.
|
||||
*
|
||||
* @return int[] List of post IDs.
|
||||
*/
|
||||
protected function get_internal_post_ids( $links ) {
|
||||
$post_ids = array();
|
||||
foreach ( $links as $link ) {
|
||||
$post_ids[] = $link->get_target_post_id();
|
||||
}
|
||||
|
||||
return array_filter( $post_ids );
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the link extractor.
|
||||
*/
|
||||
class WPSEO_Link_Extractor {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Sets the content.
|
||||
*
|
||||
* @param string $content The content to extract the links from.
|
||||
*/
|
||||
public function __construct( $content ) {
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the hrefs from the content and returns them as an array.
|
||||
*
|
||||
* @return array All the extracted links
|
||||
*/
|
||||
public function extract() {
|
||||
$links = array();
|
||||
|
||||
if ( strpos( $this->content, 'href' ) === false ) {
|
||||
return $links;
|
||||
}
|
||||
|
||||
$regexp = '<a\s[^>]*href=("??)([^" >]*?)\\1[^>]*>';
|
||||
|
||||
// Used modifiers iU to match case insensitive and make greedy quantifiers lazy.
|
||||
if ( preg_match_all( "/$regexp/iU", $this->content, $matches, PREG_SET_ORDER ) ) {
|
||||
foreach ( $matches as $match ) {
|
||||
$links[] = trim( $match[2], "'" );
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the conversion from array with string links into WPSEO_Link objects.
|
||||
*/
|
||||
class WPSEO_Link_Factory {
|
||||
|
||||
/**
|
||||
* @var WPSEO_Link_Type_Classifier
|
||||
*/
|
||||
protected $classifier;
|
||||
|
||||
/**
|
||||
* @var WPSEO_Link_Internal_Lookup
|
||||
*/
|
||||
protected $internal_lookup;
|
||||
|
||||
/**
|
||||
* @var WPSEO_Link_Filter
|
||||
*/
|
||||
protected $filter;
|
||||
|
||||
/**
|
||||
* Sets the dependencies for this object.
|
||||
*
|
||||
* @param WPSEO_Link_Type_Classifier $classifier The classifier to use.
|
||||
* @param WPSEO_Link_Internal_Lookup $internal_lookup The internal lookup to use.
|
||||
* @param WPSEO_Link_Filter $filter The link filter.
|
||||
*/
|
||||
public function __construct( WPSEO_Link_Type_Classifier $classifier, WPSEO_Link_Internal_Lookup $internal_lookup, WPSEO_Link_Filter $filter ) {
|
||||
$this->classifier = $classifier;
|
||||
$this->internal_lookup = $internal_lookup;
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an array of links to WPSEO_Link object.
|
||||
*
|
||||
* @param array $extracted_links The links for format.
|
||||
*
|
||||
* @return WPSEO_Link[] The formatted links.
|
||||
*/
|
||||
public function build( array $extracted_links ) {
|
||||
$extracted_links = array_map( array( $this, 'build_link' ), $extracted_links );
|
||||
$filtered_links = array_filter(
|
||||
$extracted_links,
|
||||
array( $this->filter, 'internal_link_with_fragment_filter' )
|
||||
);
|
||||
|
||||
return $filtered_links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the link.
|
||||
*
|
||||
* @param string $link The link to build.
|
||||
*
|
||||
* @return WPSEO_Link The built link.
|
||||
*/
|
||||
public function build_link( $link ) {
|
||||
$link_type = $this->classifier->classify( $link );
|
||||
|
||||
$target_post_id = 0;
|
||||
if ( $link_type === WPSEO_Link::TYPE_INTERNAL ) {
|
||||
$target_post_id = $this->internal_lookup->lookup( $link );
|
||||
}
|
||||
|
||||
return self::get_link( $link, $target_post_id, $link_type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the link object.
|
||||
*
|
||||
* @param string $url The URL of the link.
|
||||
* @param int $target_post_id The target post ID.
|
||||
* @param string $type The link type.
|
||||
*
|
||||
* @return WPSEO_Link Generated link object.
|
||||
*/
|
||||
public static function get_link( $url, $target_post_id, $type ) {
|
||||
return new WPSEO_Link( $url, $target_post_id, $type );
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the filter for filtering links.
|
||||
*/
|
||||
class WPSEO_Link_Filter {
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $current_page_path;
|
||||
|
||||
/**
|
||||
* Sets the current page path.
|
||||
*
|
||||
* @param string $current_page The current page.
|
||||
*/
|
||||
public function __construct( $current_page = '' ) {
|
||||
$this->current_page_path = untrailingslashit( WPSEO_Link_Utils::get_url_part( $current_page, 'path' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters all internal links that contains an fragment in the URL.
|
||||
*
|
||||
* @param WPSEO_Link $link The link that might be filtered.
|
||||
*
|
||||
* @return bool False when url contains a fragment.
|
||||
*/
|
||||
public function internal_link_with_fragment_filter( WPSEO_Link $link ) {
|
||||
// When the type is external.
|
||||
if ( $link->get_type() === WPSEO_Link::TYPE_EXTERNAL ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$url_parts = wp_parse_url( $link->get_url() );
|
||||
|
||||
if ( isset( $url_parts['path'] ) ) {
|
||||
return ! $this->is_current_page( untrailingslashit( $url_parts['path'] ) );
|
||||
}
|
||||
|
||||
return ( ! isset( $url_parts['fragment'] ) && ! isset( $url_parts['query'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the url path the same as the current page path.
|
||||
*
|
||||
* @param string $url_path The url path.
|
||||
*
|
||||
* @return bool True when path is equal to the current page path.
|
||||
*/
|
||||
protected function is_current_page( $url_path ) {
|
||||
return ( ! empty( $url_path ) && $url_path === $this->current_page_path );
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents installer for the link module.
|
||||
*/
|
||||
class WPSEO_Link_Installer {
|
||||
|
||||
/**
|
||||
* @var WPSEO_Installable[]
|
||||
*/
|
||||
protected $installables = array();
|
||||
|
||||
/**
|
||||
* Sets the installables.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->installables = array(
|
||||
new WPSEO_Link_Storage(),
|
||||
new WPSEO_Meta_Storage(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the installation of the link module.
|
||||
*/
|
||||
public function install() {
|
||||
foreach ( $this->get_installables() as $installable ) {
|
||||
$installable->install();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a installable object to the installer.
|
||||
*
|
||||
* @param WPSEO_Installable $installable The installable object.
|
||||
*/
|
||||
public function add_installable( WPSEO_Installable $installable ) {
|
||||
$this->installables[] = $installable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the installable objects.
|
||||
*
|
||||
* @return WPSEO_Installable[] The installables to use.
|
||||
*/
|
||||
protected function get_installables() {
|
||||
return $this->installables;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the internal link lookup. This class tries get the postid for a given internal link.
|
||||
*/
|
||||
class WPSEO_Link_Internal_Lookup {
|
||||
|
||||
/**
|
||||
* Gets a post id for the given link for the given type. If type is outbound it returns 0 as post id.
|
||||
*
|
||||
* @param string $link The link to populate.
|
||||
*
|
||||
* @return int The post id belongs to given link if link is internal.
|
||||
*/
|
||||
public function lookup( $link ) {
|
||||
// @codingStandardsIgnoreStart
|
||||
return url_to_postid( $link );
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the notifier for adding link indexing notification to the dashboard.
|
||||
*/
|
||||
class WPSEO_Link_Notifier {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const NOTIFICATION_ID = 'wpseo-reindex-links';
|
||||
|
||||
/**
|
||||
* Registers all hooks to WordPress.
|
||||
*/
|
||||
public function register_hooks() {
|
||||
if ( filter_input( INPUT_GET, 'page' ) === 'wpseo_dashboard' ) {
|
||||
add_action( 'admin_init', array( $this, 'cleanup_notification' ) );
|
||||
}
|
||||
|
||||
if ( ! wp_next_scheduled( self::NOTIFICATION_ID ) ) {
|
||||
wp_schedule_event( time(), 'daily', self::NOTIFICATION_ID );
|
||||
}
|
||||
|
||||
add_action( self::NOTIFICATION_ID, array( $this, 'manage_notification' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the notification when it is set and the amount of unindexed items is lower than the threshold.
|
||||
*/
|
||||
public function cleanup_notification() {
|
||||
if ( ! $this->has_notification() || $this->requires_notification() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->remove_notification( $this->get_notification() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the notification when it isn't set already and the amount of unindexed items is greater than the set
|
||||
* threshold.
|
||||
*/
|
||||
public function manage_notification() {
|
||||
if ( $this->has_notification() || ! $this->requires_notification() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->add_notification( $this->get_notification() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the notification has been set already.
|
||||
*
|
||||
* @return bool True when there is a notification.
|
||||
*/
|
||||
public function has_notification() {
|
||||
$notification = Yoast_Notification_Center::get()->get_notification_by_id( self::NOTIFICATION_ID );
|
||||
|
||||
return $notification instanceof Yoast_Notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a notification to the notification center.
|
||||
*
|
||||
* @param Yoast_Notification $notification The notification to add.
|
||||
*/
|
||||
protected function add_notification( Yoast_Notification $notification ) {
|
||||
Yoast_Notification_Center::get()->add_notification( $notification );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the notification from the notification center.
|
||||
*
|
||||
* @param Yoast_Notification $notification The notification to remove.
|
||||
*/
|
||||
protected function remove_notification( Yoast_Notification $notification ) {
|
||||
Yoast_Notification_Center::get()->remove_notification( $notification );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the notification.
|
||||
*
|
||||
* @return Yoast_Notification The notification to show.
|
||||
*/
|
||||
protected function get_notification() {
|
||||
return new Yoast_Notification(
|
||||
esc_html__( 'To make sure all the links in your texts are counted, we need to analyze all your texts.', 'wordpress-seo' ) . ' ' .
|
||||
esc_html__( 'All you have to do is press the following button and we\'ll go through all your texts for you.', 'wordpress-seo' ) . '<br><br>' .
|
||||
'<button type="button" id="noticeRunLinkIndex" class="button">' . esc_html__( 'Count links', 'wordpress-seo' ) . '</button><br><br>' .
|
||||
sprintf(
|
||||
/* translators: 1: link to yoast.com post about internal linking suggestion. 2: is anchor closing. */
|
||||
esc_html__( 'The Text link counter feature provides insights in how many links are found in your text and how many links are referring to your text. This is very helpful when you are improving your %1$sinternal linking%2$s.', 'wordpress-seo' ),
|
||||
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/15m' ) . '" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'type' => Yoast_Notification::WARNING,
|
||||
'id' => self::NOTIFICATION_ID,
|
||||
'capabilities' => 'wpseo_manage_options',
|
||||
'priority' => 0.8,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the unindexed threshold is exceeded.
|
||||
*
|
||||
* @return bool True when the threshold is exceeded.
|
||||
*/
|
||||
protected function requires_notification() {
|
||||
$post_types = apply_filters( 'wpseo_link_count_post_types', WPSEO_Post_Type::get_accessible_post_types() );
|
||||
if ( ! is_array( $post_types ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return WPSEO_Link_Query::has_unprocessed_posts( $post_types );
|
||||
}
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Database helper class.
|
||||
*/
|
||||
class WPSEO_Link_Query {
|
||||
|
||||
/**
|
||||
* Determine if there are any unprocessed public posts.
|
||||
*
|
||||
* @param array $post_types List of post types to check with.
|
||||
*
|
||||
* @return bool True if unprocessed posts are found, false if none are found.
|
||||
*/
|
||||
public static function has_unprocessed_posts( array $post_types ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $post_types ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$post_types = self::format_post_types( $post_types );
|
||||
$count_table = self::get_count_table_name();
|
||||
|
||||
// Get any object which has not got the processed meta key.
|
||||
$query = '
|
||||
SELECT ID
|
||||
FROM ' . $wpdb->posts . ' AS posts
|
||||
LEFT JOIN ' . $count_table . ' AS yoast_meta
|
||||
ON yoast_meta.object_id = posts.ID
|
||||
WHERE posts.post_status = "publish"
|
||||
AND posts.post_type IN ( ' . $post_types . ' )
|
||||
AND yoast_meta.internal_link_count IS NULL
|
||||
LIMIT 1';
|
||||
|
||||
// If anything is found, we have unprocessed posts.
|
||||
$results = $wpdb->get_var( $query );
|
||||
|
||||
return ! empty( $results );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out posts that have not been processed yet.
|
||||
*
|
||||
* @param array $post_ids Post IDs to filter.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function filter_unprocessed_posts( array $post_ids ) {
|
||||
global $wpdb;
|
||||
|
||||
$post_ids = array_filter( $post_ids );
|
||||
if ( empty( $post_ids ) || array() === $post_ids ) {
|
||||
return $post_ids;
|
||||
}
|
||||
|
||||
$count_table = self::get_count_table_name();
|
||||
|
||||
$query = '
|
||||
SELECT object_id
|
||||
FROM ' . $count_table . '
|
||||
WHERE object_id IN ( ' . implode( ',', $post_ids ) . ' )
|
||||
';
|
||||
|
||||
$results = $wpdb->get_results( $query, ARRAY_A );
|
||||
|
||||
return array_map( 'intval', wp_list_pluck( $results, 'object_id' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a limited set of unindexed posts.
|
||||
*
|
||||
* @param array $post_types The post type.
|
||||
* @param int $limit The limit for the resultset.
|
||||
*
|
||||
* @return array|null|object The set of unindexed posts.
|
||||
*/
|
||||
public static function get_unprocessed_posts( array $post_types, $limit = 5 ) {
|
||||
global $wpdb;
|
||||
|
||||
$count_table = self::get_count_table_name();
|
||||
$post_types = self::format_post_types( $post_types );
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
$query = 'SELECT posts.ID, posts.post_content
|
||||
FROM ' . $wpdb->posts . ' AS posts
|
||||
LEFT JOIN ' . $count_table . ' AS yoast_meta
|
||||
ON yoast_meta.object_id = posts.ID
|
||||
WHERE posts.post_status = "publish"
|
||||
AND posts.post_type IN ( ' . $post_types . ' )
|
||||
AND yoast_meta.internal_link_count IS NULL
|
||||
LIMIT %d
|
||||
';
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
return $wpdb->get_results(
|
||||
$wpdb->prepare( $query, $limit )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total amount of unindexed posts for given post type.
|
||||
*
|
||||
* @param array $post_types The post types.
|
||||
*
|
||||
* @return int The total of unindexed posts.
|
||||
*/
|
||||
public static function get_unprocessed_count( array $post_types ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $post_types ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$count_table = self::get_count_table_name();
|
||||
$post_types = self::format_post_types( $post_types );
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
$query = '
|
||||
SELECT COUNT( posts.ID )
|
||||
FROM ' . $wpdb->posts . ' AS posts
|
||||
LEFT JOIN ' . $count_table . ' AS yoast_meta
|
||||
ON yoast_meta.object_id = posts.ID
|
||||
WHERE posts.post_status = "publish"
|
||||
AND posts.post_type IN ( ' . $post_types . ' )
|
||||
AND yoast_meta.internal_link_count IS NULL';
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
return (int) $wpdb->get_var( $query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table name where the counts are stored.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_count_table_name() {
|
||||
$storage = new WPSEO_Meta_Storage();
|
||||
return $storage->get_table_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an array with post types as an SQL string.
|
||||
*
|
||||
* @param array $post_types The post types to format.
|
||||
*
|
||||
* @return string Post types formatted for use in SQL statement.
|
||||
*/
|
||||
protected static function format_post_types( array $post_types ) {
|
||||
$sanitized_post_types = array_map( 'esc_sql', $post_types );
|
||||
$post_types = sprintf( '"%s"', implode( '", "', $sanitized_post_types ) );
|
||||
|
||||
return $post_types;
|
||||
}
|
||||
}
|
@ -0,0 +1,238 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links\Reindex
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles the reindexing of links interface in the Dashboard.
|
||||
*/
|
||||
class WPSEO_Link_Reindex_Dashboard {
|
||||
|
||||
/**
|
||||
* Public post types to scan for unprocessed items.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $public_post_types = array();
|
||||
|
||||
/**
|
||||
* Number of unprocessed items.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $unprocessed = 0;
|
||||
|
||||
/**
|
||||
* Registers all hooks to WordPress.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
if ( ! $this->is_dashboard_page() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'calculate_unprocessed' ), 9 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ), 10 );
|
||||
|
||||
add_action( 'admin_footer', array( $this, 'modal_box' ), 20 );
|
||||
|
||||
add_action( 'wpseo_tools_overview_list_items', array( $this, 'show_tools_overview_item' ), 10 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of unprocessed items per post type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function calculate_unprocessed() {
|
||||
$this->public_post_types = apply_filters( 'wpseo_link_count_post_types', WPSEO_Post_Type::get_accessible_post_types() );
|
||||
|
||||
if ( is_array( $this->public_post_types ) && $this->public_post_types !== array() ) {
|
||||
$this->unprocessed = WPSEO_Link_Query::get_unprocessed_count( $this->public_post_types );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the tools page overview list.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function show_tools_overview_item() {
|
||||
echo '<li>';
|
||||
echo '<strong>' . esc_html__( 'Text link counter', 'wordpress-seo' ) . '</strong><br/>';
|
||||
|
||||
if ( ! $this->has_unprocessed() ) {
|
||||
echo $this->message_already_indexed();
|
||||
}
|
||||
|
||||
if ( $this->has_unprocessed() ) {
|
||||
printf( '<span id="reindexLinks">%s</span>', $this->message_start_indexing() );
|
||||
}
|
||||
|
||||
echo '</li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the model box.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function modal_box() {
|
||||
if ( ! $this->is_dashboard_page() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adding the thickbox.
|
||||
add_thickbox();
|
||||
|
||||
$blocks = array();
|
||||
|
||||
if ( ! $this->has_unprocessed() ) {
|
||||
$inner_text = sprintf(
|
||||
'<p>%s</p>',
|
||||
esc_html__( 'All your texts are already counted, there is no need to count them again.', 'wordpress-seo' )
|
||||
);
|
||||
}
|
||||
|
||||
if ( $this->has_unprocessed() ) {
|
||||
$progress = sprintf(
|
||||
/* translators: 1: expands to a <span> containing the number of items recalculated. 2: expands to a <strong> containing the total number of items. */
|
||||
__( 'Text %1$s of %2$s processed.', 'wordpress-seo' ),
|
||||
'<span id="wpseo_count_index_links">0</span>',
|
||||
sprintf( '<strong id="wpseo_count_total">%d</strong>', $this->get_unprocessed_count() )
|
||||
);
|
||||
|
||||
$inner_text = '<div id="wpseo_index_links_progressbar" class="wpseo-progressbar"></div>';
|
||||
$inner_text .= sprintf( '<p>%s</p>', $progress );
|
||||
}
|
||||
|
||||
$blocks[] = sprintf(
|
||||
'<div><p>%s</p>%s</div>',
|
||||
esc_html__( 'Counting links in your texts', 'wordpress-seo' ),
|
||||
$inner_text
|
||||
);
|
||||
?>
|
||||
<div id="wpseo_index_links_wrapper" class="hidden">
|
||||
<?php echo implode( '<hr />', $blocks ); ?>
|
||||
<button onclick="tb_remove();" type="button"
|
||||
class="button"><?php esc_html_e( 'Stop counting', 'wordpress-seo' ); ?></button>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues site wide analysis script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue() {
|
||||
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
||||
$asset_manager->enqueue_script( 'reindex-links' );
|
||||
|
||||
$data = array(
|
||||
'amount' => $this->get_unprocessed_count(),
|
||||
'restApi' => array(
|
||||
'root' => esc_url_raw( rest_url() ),
|
||||
'endpoint' => WPSEO_Link_Reindex_Post_Endpoint::REST_NAMESPACE . '/' . WPSEO_Link_Reindex_Post_Endpoint::ENDPOINT_QUERY,
|
||||
'nonce' => wp_create_nonce( 'wp_rest' ),
|
||||
),
|
||||
'message' => array(
|
||||
'indexingCompleted' => $this->message_already_indexed(),
|
||||
),
|
||||
'l10n' => array(
|
||||
'calculationInProgress' => __( 'Calculation in progress...', 'wordpress-seo' ),
|
||||
'calculationCompleted' => __( 'Calculation completed.', 'wordpress-seo' ),
|
||||
),
|
||||
);
|
||||
|
||||
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'reindex-links', 'yoastReindexLinksData', array( 'data' => $data ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current page is the dashboard page.
|
||||
*
|
||||
* @return bool True when current page is the dashboard page.
|
||||
*/
|
||||
protected function is_dashboard_page() {
|
||||
return ( filter_input( INPUT_GET, 'page' ) === 'wpseo_tools' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the string to display when everything has been indexed.
|
||||
*
|
||||
* @return string The message to show when everything has been indexed.
|
||||
*/
|
||||
public function message_already_indexed() {
|
||||
return '<span class="wpseo-checkmark-ok-icon"></span>' . esc_html__( 'Good job! All the links in your texts have been counted.', 'wordpress-seo' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if there are unprocessed items.
|
||||
*
|
||||
* @return bool True if there are unprocessed items.
|
||||
*/
|
||||
public function has_unprocessed() {
|
||||
return $this->unprocessed > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of unprocessed items.
|
||||
*
|
||||
* @return int Number of unprocessed items.
|
||||
*/
|
||||
public function get_unprocessed_count() {
|
||||
return $this->unprocessed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the message to show starting indexation.
|
||||
*
|
||||
* @return string The message.
|
||||
*/
|
||||
public function message_start_indexing() {
|
||||
return sprintf(
|
||||
'<a id="openLinkIndexing" href="#TB_inline?width=600&height=%1$s&inlineId=wpseo_index_links_wrapper" title="%2$s" class="btn button yoast-js-index-links yoast-js-calculate-index-links--all thickbox">%2$s</a>',
|
||||
175,
|
||||
esc_attr__( 'Count links in your texts', 'wordpress-seo' )
|
||||
);
|
||||
}
|
||||
|
||||
/* ********************* DEPRECATED METHODS ********************* */
|
||||
|
||||
/**
|
||||
* Add the indexing interface for links to the dashboard.
|
||||
*
|
||||
* @deprecated 7.0
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_link_index_interface() {
|
||||
_deprecated_function( __METHOD__, 'WPSEO 7.0' );
|
||||
|
||||
$html = '';
|
||||
$html .= '<h2>' . esc_html__( 'Text link counter', 'wordpress-seo' ) . '</h2>';
|
||||
$html .= '<p>' . sprintf(
|
||||
/* translators: 1: link to yoast.com post about internal linking suggestion. 4: is Yoast.com 3: is anchor closing. */
|
||||
__( 'The links in all your public texts need to be counted. This will provide insights of which texts need more links to them. If you want to know more about the why and how of internal linking, check out %1$sthe article about internal linking on %2$s%3$s.', 'wordpress-seo' ),
|
||||
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/15n' ) . '" target="_blank">',
|
||||
'Yoast.com',
|
||||
'</a>'
|
||||
) . '</p>';
|
||||
|
||||
if ( ! $this->has_unprocessed() ) {
|
||||
$html .= '<p>' . $this->message_already_indexed() . '</p>';
|
||||
}
|
||||
|
||||
if ( $this->has_unprocessed() ) {
|
||||
$html .= '<p id="reindexLinks">' . $this->message_start_indexing() . '</p>';
|
||||
}
|
||||
|
||||
$html .= '<br />';
|
||||
|
||||
echo $html;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links\Reindex
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WPSEO_Link_Reindex_Post_Endpoint.
|
||||
*/
|
||||
class WPSEO_Link_Reindex_Post_Endpoint {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const REST_NAMESPACE = 'yoast/v1';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const ENDPOINT_QUERY = 'reindex_posts';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const CAPABILITY_RETRIEVE = 'edit_posts';
|
||||
|
||||
/**
|
||||
* @var WPSEO_Link_Reindex_Post_Service
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* WPSEO_Link_Reindex_Post_Endpoint constructor.
|
||||
*
|
||||
* @param WPSEO_Link_Reindex_Post_Service $service The service to handle the requests to the endpoint.
|
||||
*/
|
||||
public function __construct( WPSEO_Link_Reindex_Post_Service $service ) {
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the REST endpoint to WordPress.
|
||||
*/
|
||||
public function register() {
|
||||
$route_args = array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this->service, 'reindex' ),
|
||||
'permission_callback' => array( $this, 'can_retrieve_data' ),
|
||||
);
|
||||
register_rest_route( self::REST_NAMESPACE, self::ENDPOINT_QUERY, $route_args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current user is allowed to use this endpoint.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_retrieve_data() {
|
||||
return current_user_can( self::CAPABILITY_RETRIEVE );
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links\Reindex
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WPSEO_Link_Reindex_Post_Service.
|
||||
*/
|
||||
class WPSEO_Link_Reindex_Post_Service {
|
||||
|
||||
/**
|
||||
* Reindexes the unprocessed posts by REST request.
|
||||
*
|
||||
* @return WP_REST_Response The response object.
|
||||
*/
|
||||
public function reindex() {
|
||||
return new WP_REST_Response( $this->process_posts() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the posts.
|
||||
*
|
||||
* @return int The total amount of unprocessed posts.
|
||||
*/
|
||||
protected function process_posts() {
|
||||
if ( ! $this->is_processable() ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$posts = $this->get_unprocessed_posts();
|
||||
array_walk( $posts, array( $this, 'process_post' ) );
|
||||
|
||||
return count( $posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all unprocessed posts.
|
||||
*
|
||||
* @return array The unprocessed posts.
|
||||
*/
|
||||
protected function get_unprocessed_posts() {
|
||||
$post_types = apply_filters( 'wpseo_link_count_post_types', WPSEO_Post_Type::get_accessible_post_types() );
|
||||
if ( ! is_array( $post_types ) ) {
|
||||
return array();
|
||||
}
|
||||
return WPSEO_Link_Query::get_unprocessed_posts( $post_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the required tables are accessible.
|
||||
*
|
||||
* @return bool True when the tables are accessible.
|
||||
*/
|
||||
protected function is_processable() {
|
||||
return WPSEO_Link_Table_Accessible::is_accessible() && WPSEO_Meta_Table_Accessible::is_accessible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the post.
|
||||
*
|
||||
* @param stdObject $post The post to process.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function process_post( $post ) {
|
||||
// Some plugins might output data, so let's buffer this to prevent wrong responses.
|
||||
ob_start();
|
||||
|
||||
// Apply the filters to have the same content as shown on the frontend.
|
||||
$content = apply_filters( 'the_content', $post->post_content );
|
||||
$content = str_replace( ']]>', ']]>', $content );
|
||||
|
||||
ob_end_clean();
|
||||
|
||||
$content_processor = $this->get_content_processor();
|
||||
$content_processor->process( $post->ID, $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the content processor.
|
||||
*
|
||||
* @return WPSEO_Link_Content_Processor The instance of the link content processor.
|
||||
*/
|
||||
protected function get_content_processor() {
|
||||
static $content_processor;
|
||||
|
||||
if ( $content_processor === null ) {
|
||||
$content_processor = new WPSEO_Link_Content_Processor( new WPSEO_Link_Storage(), new WPSEO_Meta_Storage() );
|
||||
}
|
||||
|
||||
return $content_processor;
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the storage of an seo link.
|
||||
*/
|
||||
class WPSEO_Link_Storage implements WPSEO_Installable {
|
||||
|
||||
/**
|
||||
* Table name for the link storage.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE_NAME = 'yoast_seo_links';
|
||||
|
||||
/**
|
||||
* @var WPSEO_Database_Proxy
|
||||
*/
|
||||
protected $database_proxy;
|
||||
|
||||
/**
|
||||
* @deprecated 7.4
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
protected $table_prefix;
|
||||
|
||||
/**
|
||||
* Initializes the database table.
|
||||
*
|
||||
* @param string $table_prefix Optional. Deprecated argument.
|
||||
*/
|
||||
public function __construct( $table_prefix = null ) {
|
||||
if ( $table_prefix !== null ) {
|
||||
_deprecated_argument( __METHOD__, 'WPSEO 7.4' );
|
||||
}
|
||||
|
||||
$this->database_proxy = new WPSEO_Database_Proxy( $GLOBALS['wpdb'], self::TABLE_NAME, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table name to use.
|
||||
*
|
||||
* @return string The table name.
|
||||
*/
|
||||
public function get_table_name() {
|
||||
return $this->database_proxy->get_table_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database table.
|
||||
*
|
||||
* @return boolean True if the table was created, false if something went wrong.
|
||||
*/
|
||||
public function install() {
|
||||
return $this->database_proxy->create_table(
|
||||
array(
|
||||
'id bigint(20) unsigned NOT NULL AUTO_INCREMENT',
|
||||
'url varchar(255) NOT NULL',
|
||||
'post_id bigint(20) unsigned NOT NULL',
|
||||
'target_post_id bigint(20) unsigned NOT NULL',
|
||||
'type VARCHAR(8) NOT NULL',
|
||||
),
|
||||
array(
|
||||
'PRIMARY KEY (id)',
|
||||
'KEY link_direction (post_id, type)',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of links from the database.
|
||||
*
|
||||
* @param int $post_id The post to get the links for.
|
||||
*
|
||||
* @return WPSEO_Link[] The links connected to the post.
|
||||
*/
|
||||
public function get_links( $post_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$results = $this->database_proxy->get_results(
|
||||
$wpdb->prepare(
|
||||
'
|
||||
SELECT url, post_id, target_post_id, type
|
||||
FROM ' . $this->get_table_name() . '
|
||||
WHERE post_id = %d',
|
||||
$post_id
|
||||
)
|
||||
);
|
||||
|
||||
if ( $this->database_proxy->has_error() ) {
|
||||
WPSEO_Link_Table_Accessible::set_inaccessible();
|
||||
}
|
||||
|
||||
$links = array();
|
||||
foreach ( $results as $link ) {
|
||||
$links[] = WPSEO_Link_Factory::get_link( $link->url, $link->target_post_id, $link->type );
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks the given links to save them.
|
||||
*
|
||||
* @param integer $post_id The post id to save.
|
||||
* @param WPSEO_Link[] $links The link to save.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save_links( $post_id, array $links ) {
|
||||
array_walk( $links, array( $this, 'save_link' ), $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all records for given post_id.
|
||||
*
|
||||
* @param int $post_id The post_id to remove the records for.
|
||||
*
|
||||
* @return int|false The number of rows updated, or false on error.
|
||||
*/
|
||||
public function cleanup( $post_id ) {
|
||||
$is_deleted = $this->database_proxy->delete(
|
||||
array( 'post_id' => $post_id ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
if ( $is_deleted === false ) {
|
||||
WPSEO_Link_Table_Accessible::set_inaccessible();
|
||||
}
|
||||
|
||||
return $is_deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the link into the database.
|
||||
*
|
||||
* @param WPSEO_Link $link The link to save.
|
||||
* @param int $link_key The link key. Unused.
|
||||
* @param int $post_id The post id to save the link for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function save_link( WPSEO_Link $link, $link_key, $post_id ) {
|
||||
$inserted = $this->database_proxy->insert(
|
||||
array(
|
||||
'url' => $link->get_url(),
|
||||
'post_id' => $post_id,
|
||||
'target_post_id' => $link->get_target_post_id(),
|
||||
'type' => $link->get_type(),
|
||||
),
|
||||
array( '%s', '%d', '%d', '%s' )
|
||||
);
|
||||
|
||||
if ( $inserted === false ) {
|
||||
WPSEO_Link_Table_Accessible::set_inaccessible();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the notice when the table is not accessible.
|
||||
*/
|
||||
class WPSEO_Link_Table_Accessible_Notifier {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const NOTIFICATION_ID = 'wpseo-links-table-not-accessible';
|
||||
|
||||
/**
|
||||
* Adds the notification to the notification center.
|
||||
*/
|
||||
public function add_notification() {
|
||||
Yoast_Notification_Center::get()->add_notification( $this->get_notification() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the notification from the notification center.
|
||||
*/
|
||||
public function remove_notification() {
|
||||
Yoast_Notification_Center::get()->remove_notification( $this->get_notification() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notification when the table is not accessible.
|
||||
*
|
||||
* @return Yoast_Notification The notification.
|
||||
*/
|
||||
protected function get_notification() {
|
||||
return new Yoast_Notification(
|
||||
sprintf(
|
||||
/* translators: %1$s: Yoast SEO. %2$s: Version number of Yoast SEO. %3$s: strong opening tag, %4$s: strong closing tag */
|
||||
esc_html__( 'The %3$sText link counter%4$s feature (introduced in %1$s %2$s) is currently disabled.', 'wordpress-seo' ),
|
||||
'Yoast SEO',
|
||||
'5.0',
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
) . ' ' .
|
||||
sprintf(
|
||||
/* translators: %1$s: Yoast SEO. */
|
||||
esc_html__( 'For this feature to work, %1$s needs to create a table in your database. We were unable to create this table automatically.', 'wordpress-seo' ),
|
||||
'Yoast SEO'
|
||||
) . '<br><br>' .
|
||||
sprintf(
|
||||
/* translators: %1$s: link to knowledge base article about solving table issue. %2$s: is anchor closing. */
|
||||
esc_html__( 'Please read the following %1$sknowledge base article%2$s to find out how to resolve this problem.', 'wordpress-seo' ),
|
||||
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/15o' ) . '">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'type' => Yoast_Notification::WARNING,
|
||||
'id' => self::NOTIFICATION_ID,
|
||||
'capabilities' => 'wpseo_manage_options',
|
||||
'priority' => 0.8,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the state of the table being accessible.
|
||||
*/
|
||||
class WPSEO_Link_Table_Accessible {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const ACCESSIBLE = '0';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const INACCESSBILE = '1';
|
||||
|
||||
/**
|
||||
* Checks if the given table name exists.
|
||||
*
|
||||
* @return bool True when table is accessible.
|
||||
*/
|
||||
public static function is_accessible() {
|
||||
$value = get_transient( self::transient_name() );
|
||||
|
||||
// If the value is not set, check the table.
|
||||
if ( false === $value ) {
|
||||
return self::check_table();
|
||||
}
|
||||
|
||||
return $value === self::ACCESSIBLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transient value to 1, to indicate the table is not accessible.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function set_inaccessible() {
|
||||
set_transient( self::transient_name(), self::INACCESSBILE, HOUR_IN_SECONDS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the transient.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function cleanup() {
|
||||
delete_transient( self::transient_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transient value to 0, to indicate the table is accessible.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function set_accessible() {
|
||||
/*
|
||||
* Prefer to set a 0 timeout, but if the timeout was set before WordPress will not delete the transient
|
||||
* correctly when overridden with a zero value.
|
||||
*
|
||||
* Setting a YEAR_IN_SECONDS instead.
|
||||
*/
|
||||
set_transient( self::transient_name(), self::ACCESSIBLE, YEAR_IN_SECONDS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the table exists if not, set the transient to indicate the inaccessible table.
|
||||
*
|
||||
* @return bool True if table is accessible.
|
||||
*/
|
||||
protected static function check_table() {
|
||||
global $wpdb;
|
||||
|
||||
$storage = new WPSEO_Link_Storage();
|
||||
$query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $storage->get_table_name() );
|
||||
if ( $wpdb->get_var( $query ) !== $storage->get_table_name() ) {
|
||||
self::set_inaccessible();
|
||||
return false;
|
||||
}
|
||||
|
||||
self::set_accessible();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the transient.
|
||||
*
|
||||
* @return string The name of the transient to use.
|
||||
*/
|
||||
protected static function transient_name() {
|
||||
return 'wpseo_link_table_inaccessible';
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the classifier for a link. Determines of a link is an outbound or internal one.
|
||||
*/
|
||||
class WPSEO_Link_Type_Classifier {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $base_host = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $base_path = '';
|
||||
|
||||
/**
|
||||
* Constructor setting the base url.
|
||||
*
|
||||
* @param string $base_url The base url to set.
|
||||
*/
|
||||
public function __construct( $base_url ) {
|
||||
|
||||
$this->base_host = WPSEO_Link_Utils::get_url_part( $base_url, 'host' );
|
||||
|
||||
$base_path = WPSEO_Link_Utils::get_url_part( $base_url, 'path' );
|
||||
if ( $base_path ) {
|
||||
$this->base_path = trailingslashit( $base_path );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given link is an outbound or an internal link.
|
||||
*
|
||||
* @param string $link The link to classify.
|
||||
*
|
||||
* @return string Returns outbound or internal.
|
||||
*/
|
||||
public function classify( $link ) {
|
||||
$url_parts = wp_parse_url( $link );
|
||||
|
||||
// Because parse_url may return false.
|
||||
if ( ! is_array( $url_parts ) ) {
|
||||
$url_parts = array();
|
||||
}
|
||||
|
||||
if ( $this->contains_protocol( $url_parts ) && $this->is_external_link( $url_parts ) ) {
|
||||
return WPSEO_Link::TYPE_EXTERNAL;
|
||||
}
|
||||
|
||||
return WPSEO_Link::TYPE_INTERNAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a link starts with an HTTP[S] protocol.
|
||||
*
|
||||
* @param array $url_parts The url parts to use.
|
||||
*
|
||||
* @return bool True if the url starts with an https:// or http:// protocol.
|
||||
*/
|
||||
protected function contains_protocol( array $url_parts ) {
|
||||
return isset( $url_parts['scheme'] ) && $url_parts['scheme'] !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the link contains the home_url. Returns true if this isn't the case.
|
||||
*
|
||||
* @param array $url_parts The url parts to use.
|
||||
*
|
||||
* @return bool True when the link doesn't contain the home url.
|
||||
*/
|
||||
protected function is_external_link( array $url_parts ) {
|
||||
if ( isset( $url_parts['scheme'] ) && ! in_array( $url_parts['scheme'], array( 'http', 'https' ), true ) ) {
|
||||
return true;
|
||||
}
|
||||
// When the base host is equal to the host.
|
||||
if ( isset( $url_parts['host'] ) && $url_parts['host'] !== $this->base_host ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// There is no base path.
|
||||
if ( empty( $this->base_path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// When there is a path.
|
||||
if ( isset( $url_parts['path'] ) ) {
|
||||
return ( strpos( $url_parts['path'], $this->base_path ) === false );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the utils for the links module.
|
||||
*/
|
||||
class WPSEO_Link_Utils {
|
||||
|
||||
/**
|
||||
* Returns the value that is part of the given url.
|
||||
*
|
||||
* @param string $url The url to parse.
|
||||
* @param string $part The url part to use.
|
||||
*
|
||||
* @return string The value of the url part.
|
||||
*/
|
||||
public static function get_url_part( $url, $part ) {
|
||||
$url_parts = wp_parse_url( $url );
|
||||
|
||||
if ( isset( $url_parts[ $part ] ) ) {
|
||||
return $url_parts[ $part ];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the loader for link watcher.
|
||||
*/
|
||||
class WPSEO_Link_Watcher_Loader {
|
||||
|
||||
/**
|
||||
* Loads the link watcher.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function load() {
|
||||
$storage = new WPSEO_Link_Storage();
|
||||
$count_storage = new WPSEO_Meta_Storage();
|
||||
$content_processor = new WPSEO_Link_Content_Processor( $storage, $count_storage );
|
||||
$link_watcher = new WPSEO_Link_Watcher( $content_processor );
|
||||
$link_watcher->register_hooks();
|
||||
}
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the link watcher. This class will watch for the save_post hook being called.
|
||||
*/
|
||||
class WPSEO_Link_Watcher {
|
||||
|
||||
/**
|
||||
* @var WPSEO_Link_Content_Processor
|
||||
*/
|
||||
protected $content_processor;
|
||||
|
||||
/**
|
||||
* WPSEO_Link_Watcher constructor.
|
||||
*
|
||||
* @param WPSEO_Link_Content_Processor $content_processor The processor to use.
|
||||
*/
|
||||
public function __construct( WPSEO_Link_Content_Processor $content_processor ) {
|
||||
$this->content_processor = $content_processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the hooks.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
public function register_hooks() {
|
||||
add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
|
||||
add_action( 'delete_post', array( $this, 'delete_post' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the links that are used in the post.
|
||||
*
|
||||
* @param int $post_id The post id to.
|
||||
* @param WP_Post $post The post object.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save_post( $post_id, WP_Post $post ) {
|
||||
if ( ! WPSEO_Link_Table_Accessible::is_accessible() || ! WPSEO_Meta_Table_Accessible::is_accessible() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When the post is a revision.
|
||||
if ( wp_is_post_revision( $post->ID ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_statuses_to_skip = array( 'auto-draft', 'trash' );
|
||||
|
||||
if ( in_array( $post->post_status, $post_statuses_to_skip, true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When the post isn't processable, just remove the saved links.
|
||||
if ( ! $this->is_processable( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->process( $post_id, $post->post_content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the seo links when the post is deleted.
|
||||
*
|
||||
* @param int $post_id The post id.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete_post( $post_id ) {
|
||||
if ( ! WPSEO_Link_Table_Accessible::is_accessible() || ! WPSEO_Meta_Table_Accessible::is_accessible() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch links to update related linked objects.
|
||||
$links = $this->content_processor->get_stored_internal_links( $post_id );
|
||||
|
||||
// Update the storage, remove all links for this post.
|
||||
$storage = new WPSEO_Link_Storage();
|
||||
$storage->cleanup( $post_id );
|
||||
|
||||
// Update link counts for object and referenced links.
|
||||
$this->content_processor->update_link_counts( $post_id, 0, $links );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the post is processable.
|
||||
*
|
||||
* @param int $post_id The post id.
|
||||
*
|
||||
* @return bool True when the post is processable.
|
||||
*/
|
||||
protected function is_processable( $post_id ) {
|
||||
/*
|
||||
* Do not use the `wpseo_link_count_post_types` because we want to always count the links,
|
||||
* even if we don't show them.
|
||||
*/
|
||||
$post_types = WPSEO_Post_Type::get_accessible_post_types();
|
||||
|
||||
return isset( $post_types[ get_post_type( $post_id ) ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the content for the given post id.
|
||||
*
|
||||
* @param int $post_id The post id to process.
|
||||
* @param string $content The content to process.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function process( $post_id, $content ) {
|
||||
// Apply the filters to have the same content as shown on the frontend.
|
||||
$content = apply_filters( 'the_content', $content );
|
||||
$content = str_replace( ']]>', ']]>', $content );
|
||||
|
||||
$this->content_processor->process( $post_id, $content );
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* WPSEO plugin file.
|
||||
*
|
||||
* @package WPSEO\Admin\Links
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an seo link.
|
||||
*/
|
||||
class WPSEO_Link {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const TYPE_EXTERNAL = 'external';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const TYPE_INTERNAL = 'internal';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $target_post_id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Sets the properties for the object.
|
||||
*
|
||||
* @param string $url The url.
|
||||
* @param int $target_post_id ID to the post where the link refers to.
|
||||
* @param string $type The url type: internal or outbound.
|
||||
*/
|
||||
public function __construct( $url, $target_post_id, $type ) {
|
||||
$this->url = $url;
|
||||
$this->target_post_id = $target_post_id;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set URL.
|
||||
*
|
||||
* @return string The set url.
|
||||
*/
|
||||
public function get_url() {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set target post id.
|
||||
*
|
||||
* @return int The set target post id.
|
||||
*/
|
||||
public function get_target_post_id() {
|
||||
return (int) $this->target_post_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set link type.
|
||||
*
|
||||
* @return string The set link type.
|
||||
*/
|
||||
public function get_type() {
|
||||
return $this->type;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user