diff --git a/client/data/marketplace/search-api.ts b/client/data/marketplace/search-api.ts
index b500594e250a2..b4c0d0e8b9115 100644
--- a/client/data/marketplace/search-api.ts
+++ b/client/data/marketplace/search-api.ts
@@ -39,6 +39,7 @@ function generateApiQueryString( {
pageHandle,
pageSize,
locale,
+ slugs,
}: SearchParams ) {
const sort = 'score_default';
@@ -84,7 +85,11 @@ function generateApiQueryString( {
params.sort = 'plugin_modified';
break;
default:
- params.filter = getFilterByCategory( category );
+ if ( Array.isArray( slugs ) && slugs.length ) {
+ params.filter = getFilterbySlugs( slugs || [] );
+ } else {
+ params.filter = getFilterByCategory( category );
+ }
params.sort = 'active_installs';
}
}
@@ -156,6 +161,18 @@ function getFilterbySlug( slug: string ): {
};
}
+function getFilterbySlugs( slugs: string[] ): {
+ bool: {
+ should: { terms: object }[];
+ };
+} {
+ return {
+ bool: {
+ should: [ { terms: { slug: slugs } } ],
+ },
+ };
+}
+
function getFilterByCategory( category: string ): {
bool: object;
} {
diff --git a/client/data/marketplace/types.ts b/client/data/marketplace/types.ts
index b1e9eef298237..bed6a38eb5281 100644
--- a/client/data/marketplace/types.ts
+++ b/client/data/marketplace/types.ts
@@ -6,6 +6,7 @@ export type PluginQueryOptions = {
locale: string;
tag?: string;
author?: string;
+ slugs?: string[];
};
export type Plugin = {
@@ -89,6 +90,7 @@ export type SearchParams = {
pageHandle: string | undefined;
pageSize: number;
locale: string;
+ slugs?: string[] | undefined;
};
export type ReinstallPluginsResponse = {
diff --git a/client/data/marketplace/use-es-query.ts b/client/data/marketplace/use-es-query.ts
index d5d06167f664f..7c1f9833b24cd 100644
--- a/client/data/marketplace/use-es-query.ts
+++ b/client/data/marketplace/use-es-query.ts
@@ -142,6 +142,7 @@ export const getESPluginsInfiniteQueryParams = (
pageHandle: pageParam + '',
pageSize,
locale: getWpLocaleBySlug( ( options.locale || locale ) as LanguageSlug ),
+ slugs: options.slugs,
} );
return { queryKey, queryFn, initialPageParam: 1 };
};
diff --git a/client/my-sites/plugins/categories/index.tsx b/client/my-sites/plugins/categories/index.tsx
index 66e2ce991cff8..c1f9933ee3ce1 100644
--- a/client/my-sites/plugins/categories/index.tsx
+++ b/client/my-sites/plugins/categories/index.tsx
@@ -18,6 +18,7 @@ export type Category = {
description?: string;
icon?: string;
separator?: boolean;
+ showOnlyActive?: boolean;
};
export type Plugin = {
diff --git a/client/my-sites/plugins/categories/use-categories.tsx b/client/my-sites/plugins/categories/use-categories.tsx
index 7a44384bbf456..13414457e65f2 100644
--- a/client/my-sites/plugins/categories/use-categories.tsx
+++ b/client/my-sites/plugins/categories/use-categories.tsx
@@ -74,6 +74,7 @@ export const ALLOWED_CATEGORIES = [
'javascript',
'community',
'captcha',
+ 'wpbeginner',
];
export const getCategories: () => Record< string, Category > = () => ( {
@@ -751,6 +752,15 @@ export const getCategories: () => Record< string, Category > = () => ( {
tags: [ 'captcha', 'invisible captcha', 'nocaptcha', 'CAPTCHA Code', 'anti-spam' ],
preview: [],
},
+ wpbeginner: {
+ menu: __( 'WPBeginner' ),
+ title: __( 'Must-have plugins from WPBeginner' ),
+ description: __( 'Add the best-loved plugins on WordPress.com' ),
+ slug: 'wpbeginner',
+ tags: [ 'wpbeginner', 'Awesome Motive' ],
+ preview: [],
+ showOnlyActive: true,
+ },
} );
/**
diff --git a/client/my-sites/plugins/constants.js b/client/my-sites/plugins/constants.js
index c07b20cfea5d9..c6000f887f475 100644
--- a/client/my-sites/plugins/constants.js
+++ b/client/my-sites/plugins/constants.js
@@ -79,3 +79,33 @@ export const ECOMMERCE_BUNDLED_PLUGINS = [
];
export const UNLISTED_PLUGINS = [ 'automated-db-schenker-shipping', 'wp-fusion-lite' ];
+
+export const WPBEGINNER_PLUGINS = [
+ 'optinmonster',
+ 'wpforms-lite',
+ 'google-analytics-for-wordpress',
+ 'all-in-one-seo-pack',
+ 'coming-soon',
+ 'wp-mail-smtp',
+ 'custom-facebook-feed',
+ 'duplicator',
+ 'insert-headers-and-footers',
+ 'pushengage',
+ 'searchwp-live-ajax-search',
+ 'rafflepress',
+ 'easy-digital-downloads',
+ 'affiliatewp-checkout-referrals',
+ 'stripe',
+ 'sugar-calendar-lite',
+ 'trustpulse-api',
+ 'charitable',
+ 'igotweb-wp-mp-links',
+ 'uncanny-automator',
+ 'pretty-link',
+ 'formidable',
+ 'woocommerce-wholesale-prices',
+ 'advanced-coupons-for-woocommerce-free',
+ 'thirstyaffiliates',
+ 'uncanny-learndash-toolkit',
+ 'nutrifox',
+];
diff --git a/client/my-sites/plugins/plugins-category-results-page/index.jsx b/client/my-sites/plugins/plugins-category-results-page/index.jsx
index 0ab418289e7c0..a714b1ba5f282 100644
--- a/client/my-sites/plugins/plugins-category-results-page/index.jsx
+++ b/client/my-sites/plugins/plugins-category-results-page/index.jsx
@@ -4,12 +4,14 @@ import { useCategories } from 'calypso/my-sites/plugins/categories/use-categorie
import PluginsBrowserList from 'calypso/my-sites/plugins/plugins-browser-list';
import { PluginsBrowserListVariant } from 'calypso/my-sites/plugins/plugins-browser-list/types';
import UpgradeNudge from 'calypso/my-sites/plugins/plugins-discovery-page/upgrade-nudge';
+import { WPBEGINNER_PLUGINS } from '../constants';
import usePlugins from '../use-plugins';
const PluginsCategoryResultsPage = ( { category, siteSlug, sites } ) => {
const { plugins, isFetching, fetchNextPage, pagination } = usePlugins( {
category,
infinite: true,
+ slugs: category === 'wpbeginner' ? WPBEGINNER_PLUGINS : undefined,
} );
const categories = useCategories();
diff --git a/client/my-sites/plugins/plugins-discovery-page/index.jsx b/client/my-sites/plugins/plugins-discovery-page/index.jsx
index 6fab821ee4174..c9af37f7c1072 100644
--- a/client/my-sites/plugins/plugins-discovery-page/index.jsx
+++ b/client/my-sites/plugins/plugins-discovery-page/index.jsx
@@ -1,9 +1,11 @@
import { useSelector } from 'react-redux';
import HostingActivateStatus from 'calypso/hosting/server-settings/hosting-activate-status';
+import { getQueryArgs } from 'calypso/lib/query-args';
import { TrialAcknowledgeModal } from 'calypso/my-sites/plans/trials/trial-acknowledge/acknowlege-modal';
import { WithOnclickTrialRequest } from 'calypso/my-sites/plans/trials/trial-acknowledge/with-onclick-trial-request';
import { isCompatiblePlugin } from 'calypso/my-sites/plugins/plugin-compatibility';
import { isUserLoggedIn } from 'calypso/state/current-user/selectors';
+import { WPBEGINNER_PLUGINS } from '../constants';
import EducationFooter from '../education-footer';
import CollectionListView from '../plugins-browser/collection-list-view';
import SingleListView, { SHORT_LIST_LENGTH } from '../plugins-browser/single-list-view';
@@ -47,6 +49,24 @@ export const PaidPluginsSection = ( props ) => {
/>
);
};
+export const FeaturedWPBeginnerSection = ( props ) => {
+ const category = 'wpbeginner';
+
+ const { plugins, isFetching } = usePlugins( {
+ category,
+ infinite: true,
+ slugs: WPBEGINNER_PLUGINS,
+ } );
+
+ return (
+
+ );
+};
const FeaturedPluginsSection = ( props ) => {
return (
@@ -89,6 +109,7 @@ const PluginsDiscoveryPage = ( props ) => {
} );
const isLoggedIn = useSelector( isUserLoggedIn );
+ const isWPBeginnerSpecial = getQueryArgs()?.ref === 'wpbeginner-special-lp';
const {
isTrialAcknowledgeModalOpen,
@@ -112,15 +133,22 @@ const PluginsDiscoveryPage = ( props ) => {
/>
) }
-
-
-
- { ! isLoggedIn && }
-
+ { isWPBeginnerSpecial ? (
+
+ ) : (
+ <>
+
+
+
+ { ! isLoggedIn && }
+
+ >
+ ) }
+
diff --git a/client/my-sites/plugins/search-categories/index.tsx b/client/my-sites/plugins/search-categories/index.tsx
index 369cab99c8065..9167408a051ce 100644
--- a/client/my-sites/plugins/search-categories/index.tsx
+++ b/client/my-sites/plugins/search-categories/index.tsx
@@ -100,7 +100,9 @@ const SearchCategories: FC< {
const displayCategories = ALLOWED_CATEGORIES.filter(
( v ) => [ 'paid', 'popular', 'featured' ].indexOf( v ) < 0
);
- const categories = Object.values( useCategories( displayCategories ) );
+ const categories = Object.values( useCategories( displayCategories ) ).filter(
+ ( item ) => ! item.showOnlyActive || item.slug === category
+ );
// Update the search box with the value from the url everytime it changes
// This allows the component to be refilled with a keyword
diff --git a/client/my-sites/plugins/use-plugins/index.ts b/client/my-sites/plugins/use-plugins/index.ts
index 0bc4a7d12cd21..54fb349043b57 100644
--- a/client/my-sites/plugins/use-plugins/index.ts
+++ b/client/my-sites/plugins/use-plugins/index.ts
@@ -33,11 +33,13 @@ const usePlugins = ( {
search,
infinite = false,
locale = '',
+ slugs,
}: {
category: string;
search?: string;
infinite?: boolean;
locale?: string;
+ slugs?: string[];
} ) => {
let plugins = [];
let isFetching = false;
@@ -53,6 +55,7 @@ const usePlugins = ( {
category,
tag,
searchTerm: search,
+ slugs,
};
// This is triggered for searches OR any other category than paid, featured