Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/content-helper/editor-sidebar.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url', 'wp-wordcount'), 'version' => '1727f7296110931bb26b');
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url', 'wp-wordcount'), 'version' => '2674d449e64ff022e0ea');
24 changes: 12 additions & 12 deletions build/content-helper/editor-sidebar.js

Large diffs are not rendered by default.

35 changes: 7 additions & 28 deletions src/Models/class-inbound-smart-link.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use Masterminds\HTML5;
use Parsely\Parsely;
use Parsely\Utils\Utils;
use ReflectionClass;
use WP_Post;

Expand Down Expand Up @@ -484,34 +485,11 @@ private function get_paragraph( \WP_Post $post ) {
* @param string $url The URL.
*/
public function set_source_from_url( string $url ): bool {
// First try to find a post by URL.
if ( function_exists( 'wpcom_vip_url_to_postid' ) ) {
$source_post_id = wpcom_vip_url_to_postid( $url );
} else {
// phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.url_to_postid_url_to_postid
$source_post_id = url_to_postid( $url );
}
$source_post_id = Utils::get_post_id_by_url( $url );

// Found a post by URL, set the source post.
if ( 0 !== $source_post_id ) {
$this->set_source_post_id( $source_post_id );
return true;
}

// Since we couldn't find a post by URL, try to find a post with the same slug.
$post_slug = basename( $url );

$public_post_types = get_post_types(
array(
'public' => true,
'show_in_rest' => true,
)
);
$source_post = get_page_by_path( $post_slug, OBJECT, array_keys( $public_post_types ) );

if ( null !== $source_post ) {
// Set the source post and update the canonical URL.
$this->set_source_post( $source_post, $url );
$this->set_source_post_id( $source_post_id );
return true;
}

Expand Down Expand Up @@ -593,7 +571,8 @@ private function validate_link_placement( \DOMNode $node, string $search_text )
}

// Check if the link is already linked to this smart link.
if ( $current instanceof \DOMElement && $current->getAttribute( 'href' ) === $this->href ) {
if ( $current instanceof \DOMElement &&
strpos( $current->getAttribute( 'href' ), $this->get_link_href() ) !== false ) {
return new \WP_Error(
'traffic_boost_invalid_link_placement',
__( 'The current link is already linked to this smart link.', 'wp-parsely' )
Expand Down Expand Up @@ -746,7 +725,7 @@ public function apply() {
}

// Update the existing link.
$existing_link->setAttribute( 'href', $this->href );
$existing_link->setAttribute( 'href', $this->get_link_href() );
$existing_link->setAttribute( 'data-smartlink', $this->uid );
$existing_link->setAttribute( 'title', $this->title );
$smart_link_node = $existing_link;
Expand All @@ -762,7 +741,7 @@ public function apply() {

// Create the smart link anchor element.
$smart_link_anchor = $temp_doc->createElement( 'a' );
$smart_link_anchor->setAttribute( 'href', $this->href );
$smart_link_anchor->setAttribute( 'href', $this->get_link_href() );
$smart_link_anchor->setAttribute( 'data-smartlink', $this->uid );
$smart_link_anchor->setAttribute( 'title', $this->title );
$smart_link_anchor->textContent = $this->text;
Expand Down
50 changes: 38 additions & 12 deletions src/Models/class-smart-link.php
Original file line number Diff line number Diff line change
Expand Up @@ -374,15 +374,6 @@ public function save(): bool {
return true;
}

/**
* Updates the UID of the smart link.
*
* @since 3.18.0
*/
public function update_uid(): void {
$this->uid = $this->generate_uid();
}

/**
* Removes the smart link from the database.
*
Expand Down Expand Up @@ -434,6 +425,38 @@ public function exists(): bool {
return false;
}

/**
* Updates the UID of the smart link.
*
* @since 3.18.0
*/
public function update_uid(): void {
$this->uid = $this->generate_uid();
}

/**
* Returns the href of the smart link with ITM parameters appended.
*
* @since 3.18.0
*
* @param bool $skip_utm_params Whether to skip the ITM parameters.
* @return string The href of the smart link with ITM parameters appended.
*/
public function get_link_href( $skip_utm_params = false ): string {
if ( $skip_utm_params ) {
return $this->href;
}

return Utils::append_itm_params(
$this->href,
array(
'campaign' => 'parsely-pch',
'source' => 'smart-link',
'term' => $this->uid,
)
);
}

/**
* Loads the post meta of the smart link object.
*
Expand Down Expand Up @@ -662,7 +685,10 @@ public function to_array(): array {
return array(
'smart_link_id' => $this->smart_link_id,
'uid' => $this->uid,
'href' => $this->href,
'href' => array(
'raw' => $this->href,
'itm' => $this->get_link_href(),
),
'title' => $this->title,
'text' => $this->text,
'offset' => $this->offset,
Expand Down Expand Up @@ -699,15 +725,15 @@ public static function deserialize( string $json ): Base_Model {
}

// If the UID has been provided, set it on the model.
$smart_link = new Smart_Link( $data['href'], $data['title'], $data['text'], $data['offset'] );
$smart_link = new Smart_Link( $data['href']['raw'], $data['title'], $data['text'], $data['offset'] );

if ( isset( $data['uid'] ) ) {
$smart_link->set_uid( $data['uid'] );

if ( $smart_link->exists() ) {
$smart_link->load();
// Update the fields.
$smart_link->set_href( $data['href'] );
$smart_link->set_href( $data['href']['raw'] );
$smart_link->title = $data['title'];
$smart_link->text = $data['text'];
$smart_link->offset = $data['offset'];
Expand Down
37 changes: 37 additions & 0 deletions src/Utils/class-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
* Utils Class.
*
* @since 3.17.0
*
* @phpstan-type ItmParams array{
* campaign: string,
* source?: string,
* medium?: string,
* content?: string,
* term?: string,
* }
*/
class Utils {
const DATE_UTC_FORMAT = 'Y-m-d';
Expand Down Expand Up @@ -458,4 +466,33 @@ public static function get_post_id_by_url( string $url ): int {

return 0;
}

/**
* Appends ITM parameters to a URL.
*
* @since 3.18.0
*
* @param string $url The URL to append the ITM parameters to.
* @param ItmParams $params The ITM parameters to append.
* @return string The URL with the ITM parameters appended.
*/
public static function append_itm_params( string $url, $params ): string {
// Convert the params array to the correct format.
$mapping = array(
'campaign' => 'itm_campaign',
'source' => 'itm_source',
'medium' => 'itm_medium',
'content' => 'itm_content',
'term' => 'itm_term',
);

$itm_params = array();
foreach ( $params as $key => $value ) {
if ( array_key_exists( $key, $mapping ) ) {
$itm_params[ $mapping[ $key ] ] = $value;
}
}

return add_query_arg( $itm_params, $url );
}
}
61 changes: 61 additions & 0 deletions src/content-helper/common/utils/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,64 @@ export async function isEditorReady(): Promise<void> {
} );
} );
}

/**
* Adds ITM parameters to a URL.
*
* @since 3.18.0
*
* @param {string} url The URL to add ITM parameters to.
* @param {Object} itmParams The ITM parameters to add to the URL.
* @param {string} itmParams.campaign The campaign parameter.
* @param {string} itmParams.source The source parameter.
* @param {string} itmParams.medium The medium parameter.
* @param {string} itmParams.content The content parameter.
* @param {string} itmParams.term The term parameter.
*
* @return {string} The URL with ITM parameters added.
*/
export function addITMParamsToURL( url: string, itmParams: {
campaign: string;
source?: string;
medium?: string;
content?: string;
term?: string;
} ): string {
const urlObj = new URL( url );
urlObj.searchParams.set( 'itm_campaign', itmParams.campaign );

if ( itmParams.source ) {
urlObj.searchParams.set( 'itm_source', itmParams.source );
}
if ( itmParams.medium ) {
urlObj.searchParams.set( 'itm_medium', itmParams.medium );
}
if ( itmParams.content ) {
urlObj.searchParams.set( 'itm_content', itmParams.content );
}
if ( itmParams.term ) {
urlObj.searchParams.set( 'itm_term', itmParams.term );
}

return urlObj.toString();
}

/**
* Removes ITM parameters from a URL.
*
* @since 3.18.0
*
* @param {string} url The URL to remove ITM parameters from.
*
* @return {string} The URL with ITM parameters removed.
*/
export function removeITMParamsFromURL( url: string ): string {
const urlObj = new URL( url );
urlObj.searchParams.delete( 'itm_campaign' );
urlObj.searchParams.delete( 'itm_source' );
urlObj.searchParams.delete( 'itm_medium' );
urlObj.searchParams.delete( 'itm_content' );
urlObj.searchParams.delete( 'itm_term' );

return urlObj.toString();
}
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ export const SmartLinkingPanel = ( {

// Filter out self-referencing links.
links = links.filter( ( link ) => {
if ( link.href.includes( strippedPermalink ) ) {
if ( link.href.raw.includes( strippedPermalink ) ) {
// eslint-disable-next-line no-console
console.warn( `PCH Smart Linking: Skipping self-reference link: ${ link.href }` );
return false;
Expand Down
20 changes: 15 additions & 5 deletions src/content-helper/editor-sidebar/smart-linking/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import { DEFAULT_MAX_LINKS } from './smart-linking';
export type SmartLink = {
uid: string;
smart_link_id: number;
href: string;
href: {
raw: string;
itm: string;
};
text: string;
title: string;
offset: number;
Expand Down Expand Up @@ -172,6 +175,10 @@ export class SmartLinkingProvider extends BaseProvider {
* @return {Promise<SmartLink[]>} The outbound smart links with the extra data.
*/
private async fetchSmartLinksExtraData( smartLinks: SmartLink[] ): Promise<SmartLink[]> {
if ( smartLinks.length === 0 ) {
return [];
}

// Fetch the posts stats and meta for the outbound smart links.
const [ postsStats, postsMetas ] = await Promise.all( [
this.fetch<PerformanceData[]>( {
Expand All @@ -180,16 +187,16 @@ export class SmartLinkingProvider extends BaseProvider {
...getApiPeriodParams( Period.Days30 ),
limit: smartLinks.length,
sort: Metric.AvgEngaged, // Force return of visitors and avg_engaged.
urls: smartLinks.map( ( link ) => link.href ),
urls: smartLinks.map( ( link ) => link.href.raw ),
} ),
} ),
this.getPostMetaForURLs( smartLinks.map( ( link ) => link.href ) ),
this.getPostMetaForURLs( smartLinks.map( ( link ) => link.href.raw ) ),
] );

// Update the smart links with the extra data.
const updatedSmartLinks = smartLinks.map( ( link ) => {
const postMeta = postsMetas.find( ( meta ) => meta.url === link.href );
const postStats = postsStats.find( ( stat ) => stat.url === link.href );
const postMeta = postsMetas.find( ( meta ) => meta.url === link.href.raw );
const postStats = postsStats.find( ( stat ) => stat.url === link.href.raw );

// Don't include links for which we didn't find any data, as the URL
// probably doesn't exist. Include stats data in the check, so most
Expand Down Expand Up @@ -241,6 +248,9 @@ export class SmartLinkingProvider extends BaseProvider {
/**
* Returns a list of suggested links for the given content.
*
* @since 3.15.0
* @since 3.18.0 Fetches the extra data for the outbound smart links.
*
* @param {string} content The content to generate links for.
* @param {number} maxLinksPerPost The maximum number of links to return.
* @param {string[]} urlExclusionList A list of URLs to exclude from the suggestions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const SmartLinkingReviewModalComponent = ( {
*/
const applyLinkToBlock = async ( blockId: string, linkSuggestion: SmartLink ) => {
const anchor = document.createElement( 'a' );
anchor.href = linkSuggestion.href;
anchor.href = linkSuggestion.href.itm;
anchor.title = linkSuggestion.title;
// Add data-smartlink attribute to the anchor tag.
anchor.setAttribute( 'data-smartlink', linkSuggestion.uid );
Expand Down Expand Up @@ -293,7 +293,7 @@ const SmartLinkingReviewModalComponent = ( {
await applyLinkToBlock( selectedLink.match.blockId, selectedLink );

Telemetry.trackEvent( 'smart_linking_link_accepted', {
link: selectedLink.href,
link: selectedLink.href.raw,
title: selectedLink.title,
text: selectedLink.text,
uid: selectedLink.uid,
Expand Down Expand Up @@ -341,7 +341,7 @@ const SmartLinkingReviewModalComponent = ( {
await removeSmartLink( selectedLink.uid );

Telemetry.trackEvent( 'smart_linking_link_rejected', {
link: selectedLink.href,
link: selectedLink.href.raw,
title: selectedLink.title,
text: selectedLink.text,
uid: selectedLink.uid,
Expand Down Expand Up @@ -369,7 +369,7 @@ const SmartLinkingReviewModalComponent = ( {
await removeLinkFromBlock( block, selectedLink );

Telemetry.trackEvent( 'smart_linking_link_removed', {
link: selectedLink.href,
link: selectedLink.href.raw,
title: selectedLink.title,
text: selectedLink.text,
uid: selectedLink.uid,
Expand Down
Loading
Loading