Skip to content

Commit

Permalink
Improvements to the suggestion validation and handling
Browse files Browse the repository at this point in the history
  • Loading branch information
vaurdan committed Feb 25, 2025
1 parent 135ee3e commit df0ecb7
Show file tree
Hide file tree
Showing 13 changed files with 132 additions and 93 deletions.
10 changes: 8 additions & 2 deletions src/Models/class-inbound-smart-link.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,11 @@ private function get_post_data(): array {
$this->post_data = array(
'id' => $post->ID,
'title' => $post->post_title,
'type' => $post_type_label,
'type' => array(
'name' => $post->post_type,
'label' => $post_type_label ,
'rest' => $post_type->rest_namespace . "/" . ( $post_type->rest_base ? $post_type->rest_base : $post->post_type ),
),
'paragraph' => $paragraph['paragraph'],
'permalink' => get_permalink( $post ),
'edit_link' => get_edit_post_link( $post, 'html' ),
Expand Down Expand Up @@ -444,7 +448,9 @@ public function set_source_from_url( string $url ): bool {

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

$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 ) {
$this->set_source_post( $source_post );
Expand Down
4 changes: 3 additions & 1 deletion src/content-helper/common/base-wordpress-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,10 @@ export abstract class BaseWordPressProvider extends BaseProvider {
queryParams: QueryParams = {},
id?: string,
): Promise<FetchResponse<HydratedPost[]>> {
const restEndpoint = queryParams.rest_endpoint ?? '/wp/v2/posts';

const posts = await this.apiFetch<Post[]>( {
path: addQueryArgs( '/wp/v2/posts', { ...queryParams, _embed: true, context: 'edit' } ),
path: addQueryArgs( restEndpoint, { ...queryParams, _embed: true, context: 'edit' } ),
method: 'GET',
}, id );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { addQueryArgs } from '@wordpress/url';
* Internal dependencies
*/
import { HydratedPost } from '../../../../common/base-wordpress-provider';
import { SnackbarNotices } from '../../../../common/components/snackbar-notices';
import { ContentHelperError, ContentHelperErrorCode } from '../../../../common/content-helper-error';
import { TrafficBoostLink, TrafficBoostProvider } from '../provider';
import { TrafficBoostSidebarTabs, TrafficBoostStore } from '../store';
Expand Down Expand Up @@ -547,7 +546,6 @@ export const TrafficBoostPreview = ( {
isFrontendPreview={ isFrontendPreview }
onLoadingChange={ setIsLoading }
/>
<SnackbarNotices className="traffic-boost-preview-snackbar-notices" />
<PreviewFooter
activeLink={ activeLink }
totalItems={ totalItems }
Expand Down
156 changes: 80 additions & 76 deletions src/content-helper/dashboard-page/pages/traffic-boost/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,7 @@ export class TrafficBoostProvider extends BaseWordPressProvider {
return [];
}

const fetchedPosts = await this.getPosts( {
include: postIds,
posts_per_page: 100,
status: 'any',
} );

return response.data.map( ( inboundSmartLink ) => {
// Find the target post for the inbound smart link.
const sourcePost = fetchedPosts.data.find( ( post ) => post.id === inboundSmartLink.source?.post_id );

if ( ! sourcePost ) {
return false;
}

return this.createTrafficBoostLink( inboundSmartLink, sourcePost );
} ).filter( ( link ) => link !== false );
return this.createTrafficBoostLinks( response.data );
}

/**
Expand Down Expand Up @@ -216,45 +201,7 @@ export class TrafficBoostProvider extends BaseWordPressProvider {
// Filter out any smart links that are not valid.
const validSmartLinks = response.data.filter( ( inboundSmartLink ) => inboundSmartLink.validation?.valid );

// Get the post IDs from the inbound smart links.
const postIds = validSmartLinks.map( ( inboundSmartLink ) => inboundSmartLink.source?.post_id );

// Fetch the posts for the inbound smart links.
const fetchedPosts = await this.getPosts( {
include: postIds,
posts_per_page: 100,
status: 'any',
} );

// Create the traffic boost links.
const trafficBoostLinks = validSmartLinks.map( ( inboundSmartLink ) => {
const sourcePost = fetchedPosts.data.find( ( p ) => p.id === inboundSmartLink.source?.post_id );

if ( ! sourcePost ) {
return false;
}

return this.createTrafficBoostLink( inboundSmartLink, sourcePost );
} ).filter( ( link ) => link !== false );

return trafficBoostLinks;
}

/**
* Creates a suggestion for a given post, without generating the placement.
*
* @since 3.18.0
*
* @param {HydratedPost} post The post to create a suggestion for.
*
* @return {Promise<TrafficBoostLink>} The suggestion.
*/
public createSuggestion( post: HydratedPost ): TrafficBoostLink {
return {
uid: `suggestion-${ post.id }-${ Date.now() }`,
targetPost: post,
isSuggestion: true,
};
return this.createTrafficBoostLinks( validSmartLinks );
}

/**
Expand Down Expand Up @@ -392,26 +339,7 @@ export class TrafficBoostProvider extends BaseWordPressProvider {
return [];
}

// Now we need to fetch the posts for the inbound smart links.
const fetchedPosts = await this.getPosts( {
include: inboundSmartLinks.map( ( link ) => link.source?.post_id ),
posts_per_page: 100,
status: 'any',
} );

if ( fetchedPosts.total_items > 100 ) {
// eslint-disable-next-line no-console
console.warn( 'Parse.ly: More than 100 inbound smart links. This is not supported yet.' );
}

return fetchedPosts.data
.map( ( post ) => ( {
uid: `inbound-${ post.id }-${ Date.now() }`,
targetPost: post,
smartLink: inboundSmartLinks.find( ( link ) => link.source?.post_id === post.id ),
isSuggestion: false,
} ) )
.filter( ( link ) => link.smartLink !== undefined );
return this.createTrafficBoostLinks( inboundSmartLinks );
}

/**
Expand Down Expand Up @@ -496,6 +424,82 @@ export class TrafficBoostProvider extends BaseWordPressProvider {
return response.data;
}

/**
* Creates a suggestion for a given post, without generating the placement.
*
* @since 3.18.0
*
* @param {HydratedPost} post The post to create a suggestion for.
*
* @return {Promise<TrafficBoostLink>} The suggestion.
*/
public createSuggestion( post: HydratedPost ): TrafficBoostLink {
return {
uid: `suggestion-${ post.id }-${ Date.now() }`,
targetPost: post,
isSuggestion: true,
};
}

/**
* Creates traffic boost links from inbound smart links.
*
* @since 3.18.0
*
* @param {InboundSmartLink[]} inboundSmartLinks The inbound smart links to create traffic boost links from.
*
* @return {Promise<TrafficBoostLink[]>} The traffic boost links.
*/
private async createTrafficBoostLinks( inboundSmartLinks: InboundSmartLink[] ): Promise<TrafficBoostLink[]> {
// Split the inbound smart links into buckets of source post types.
const smartLinksByPostType = inboundSmartLinks.reduce( ( acc, inboundSmartLink ) => {
// Get the post REST endpoint for the inbound smart link source post type.
const postRestEndpoint = inboundSmartLink.post_data?.type.rest;

if ( ! postRestEndpoint ) {
return acc;
}

if ( ! acc[ postRestEndpoint ] ) {
acc[ postRestEndpoint ] = [];
}

acc[ postRestEndpoint ].push( inboundSmartLink );
return acc;
}, {} as Record<string, InboundSmartLink[]> );

// Get the posts for the inbound smart links in parallel.
const getPostsPromises = Object.entries( smartLinksByPostType ).map( async ( [ postRestEndpoint, smartLinks ] ) => {
// Get the post IDs from the inbound smart links.
const postIds = smartLinks.map( ( inboundSmartLink ) => inboundSmartLink.source?.post_id );

// Fetch the posts for the inbound smart links.
const fetchedPosts = await this.getPosts( {
include: postIds,
posts_per_page: 100,
status: 'any',
rest_endpoint: postRestEndpoint,
} );

return fetchedPosts.data;
} );

const fetchedPosts = await Promise.all( getPostsPromises );

// Create the traffic boost links.
const trafficBoostLinks = inboundSmartLinks.map( ( inboundSmartLink ) => {
const sourcePost = fetchedPosts.flat().find( ( p ) => p.id === inboundSmartLink.source?.post_id );

if ( ! sourcePost ) {
return false;
}

return this.createTrafficBoostLink( inboundSmartLink, sourcePost );
} ).filter( ( link ) => link !== false );

return trafficBoostLinks;
}

/**
* Creates a traffic boost link from an inbound smart link.
*
Expand All @@ -506,7 +510,7 @@ export class TrafficBoostProvider extends BaseWordPressProvider {
*
* @return {TrafficBoostLink} The traffic boost link.
*/
protected createTrafficBoostLink( inboundSmartLink: InboundSmartLink, targetPost: HydratedPost ): TrafficBoostLink {
private createTrafficBoostLink( inboundSmartLink: InboundSmartLink, targetPost: HydratedPost ): TrafficBoostLink {
return {
uid: inboundSmartLink.uid + '-' + Date.now(),
targetPost,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
min-height: 0;

.traffic-boost-single-link {
cursor: pointer;
flex: 0 0 auto;
display: flex;
border-bottom: 1px solid var(--gray-350);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { TrafficBoostLink, TrafficBoostProvider } from './provider';
import { TrafficBoostSidebar } from './sidebar/sidebar';
import { TrafficBoostSidebarTabs, TrafficBoostStore } from './store';
import './traffic-boost.scss';
import { SnackbarNotices } from '../../../common/components/snackbar-notices';

/**
* Traffic Boost Post page component.
Expand Down Expand Up @@ -436,6 +437,7 @@ export const TrafficBoostPostPage = (): React.JSX.Element => {
onUpdateInboundLink={ handleUpdateInboundLink }
/>
) }
<SnackbarNotices className={ selectedLink ? 'traffic-boost-snackbar-notices' : '' } />
</PageContainer>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@
align-self: stretch;
}
}

.wp-parsely-snackbar-notices.traffic-boost-snackbar-notices {
padding-left: to_rem(480px);
}
6 changes: 5 additions & 1 deletion src/content-helper/editor-sidebar/smart-linking/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ export type InboundSmartLink = SmartLink & {
post_data?: {
id: number;
title: string;
type: string;
type: {
name: string;
label: string;
rest: string;
};
paragraph: string;
is_first_paragraph: boolean;
is_last_paragraph: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const LinkingPostDetails = ( { link }: LinkingPostDetailsProps ): React.JSX.Elem
{ link.post_data?.date } by { link.post_data?.author }
</div>
</div>
<div className="linking-post-type">{ link.post_data?.type }</div>
<div className="linking-post-type">{ link.post_data?.type.label }</div>
</div>
);
};
Expand Down
13 changes: 11 additions & 2 deletions src/rest-api/content-helper/class-endpoint-traffic-boost.php
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,21 @@ public function generate_link_suggestions( WP_REST_Request $request ) {
*/
$discard_previous = $request->get_param( 'discard_previous' );


$inbound_suggestions = $this->suggestions_api->get_inbound_links(
$post,
array(
'max_items' => $max_items,
'max_link_words' => 4,
)
);

$time = $inbound_suggestions['request_duration'];
$raw_response = $inbound_suggestions['raw_response'];
$skipped = $inbound_suggestions['skipped'];

unset( $inbound_suggestions['request_duration'] );
unset( $inbound_suggestions['raw_response'] );
unset( $inbound_suggestions['skipped'] );

if ( is_wp_error( $inbound_suggestions ) ) {
return $inbound_suggestions;
}
Expand Down Expand Up @@ -338,6 +344,9 @@ function ( Inbound_Smart_Link $link ) use ( $save ) {

$response = array(
'data' => $suggestions,
'request_duration' => $time,
'raw_response' => $raw_response,
'skipped' => $skipped,
);

if ( null !== $discard_result ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function get_inbound_link_positions(
'text' => wp_strip_all_tags( $destination_post->post_content ),
);

$request_body = apply_filters( 'parsely_suggest_inbound_link_positions_request_body', $request_body, $source_post, $destination_post );
$request_body = apply_filters( 'wp_parsely_suggest_inbound_link_positions_request_body', $request_body, $source_post, $destination_post );

$response = $this->request( 'POST', array(), $request_body );

Expand Down
Loading

0 comments on commit df0ecb7

Please sign in to comment.