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