From c02cec518bbb66bd5de5149f8aaf3b9ad6077e8e Mon Sep 17 00:00:00 2001 From: tbradsha <32492176+tbradsha@users.noreply.github.com> Date: Thu, 6 Feb 2025 07:05:59 -0700 Subject: [PATCH 01/22] Map block: Prevent PHP fatal when content is empty (#41583) * Return early if content is empty * Add comment * Add changelog --- projects/plugins/jetpack/changelog/fix-map_block_fatal | 4 ++++ projects/plugins/jetpack/extensions/blocks/map/map.php | 5 +++++ 2 files changed, 9 insertions(+) create mode 100644 projects/plugins/jetpack/changelog/fix-map_block_fatal diff --git a/projects/plugins/jetpack/changelog/fix-map_block_fatal b/projects/plugins/jetpack/changelog/fix-map_block_fatal new file mode 100644 index 0000000000000..8bbe1827941b0 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-map_block_fatal @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Map block: Catch error if content is empty. diff --git a/projects/plugins/jetpack/extensions/blocks/map/map.php b/projects/plugins/jetpack/extensions/blocks/map/map.php index c349388e9c578..df4513207082a 100644 --- a/projects/plugins/jetpack/extensions/blocks/map/map.php +++ b/projects/plugins/jetpack/extensions/blocks/map/map.php @@ -154,6 +154,11 @@ function render_single_block_page() { /** This filter is already documented in core/wp-includes/post-template.php */ $content = apply_filters( 'the_content', $post->post_content ); + // Return early if empty to prevent DOMDocument::loadHTML fatal. + if ( empty( $content ) ) { + return; + } + /* Suppress warnings */ libxml_use_internal_errors( true ); @$post_html->loadHTML( $content ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged From 7c65315e488cfe721792741d75674b12b6c64d53 Mon Sep 17 00:00:00 2001 From: Igor Zinovyev Date: Thu, 6 Feb 2025 17:10:05 +0300 Subject: [PATCH 02/22] Update broken PHPDoc blocks. (#41595) * Updated PHPDoc blocks to be properly parsed by the docs CLI command. * Changelog. --- .../changelog/update-broken-phpdoc | 5 +++++ .../classic-theme-helper/src/social-menu.php | 22 +++++++++---------- .../lib/class.core-rest-api-endpoints.php | 3 ++- .../jetpack/changelog/update-broken-phpdoc | 5 +++++ .../jetpack/modules/shortcodes/youtube.php | 3 ++- .../modules/theme-tools/social-menu.php | 22 +++++++++---------- 6 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 projects/packages/classic-theme-helper/changelog/update-broken-phpdoc create mode 100644 projects/plugins/jetpack/changelog/update-broken-phpdoc diff --git a/projects/packages/classic-theme-helper/changelog/update-broken-phpdoc b/projects/packages/classic-theme-helper/changelog/update-broken-phpdoc new file mode 100644 index 0000000000000..6ebf00a6f6c08 --- /dev/null +++ b/projects/packages/classic-theme-helper/changelog/update-broken-phpdoc @@ -0,0 +1,5 @@ +Significance: patch +Type: fixed +Comment: Updated a comment, not changing any logic. + + diff --git a/projects/packages/classic-theme-helper/src/social-menu.php b/projects/packages/classic-theme-helper/src/social-menu.php index be89a1c68c36a..2c3e6660ad7da 100644 --- a/projects/packages/classic-theme-helper/src/social-menu.php +++ b/projects/packages/classic-theme-helper/src/social-menu.php @@ -23,17 +23,17 @@ function jetpack_social_menu_init() { return; } - /* - * Social Menu description. - * - * Rename the social menu description. - * - * @module theme-tools - * - * @since 3.9.0 - * - * @param string $social_menu_description Social Menu description - */ + /** + * Social Menu description. + * + * Rename the social menu description. + * + * @module theme-tools + * + * @since 3.9.0 + * + * @param string $social_menu_description Social Menu description + */ $social_menu_description = apply_filters( 'jetpack_social_menu_description', __( 'Social Menu', 'jetpack-classic-theme-helper' ) ); // Register a new menu location diff --git a/projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php b/projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php index 73dcd2b2882ef..874d5110e7206 100644 --- a/projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php +++ b/projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php @@ -1204,6 +1204,7 @@ public static function is_site_verified_and_token( $request ) { || ( Jetpack::is_plugin_active( 'under-construction-page/under-construction.php' ) && isset( $ucp_options['status'] ) && 1 == $ucp_options['status'] ) // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual || ( Jetpack::is_plugin_active( 'ultimate-under-construction/ultimate-under-construction.php' ) && isset( $uuc_settings['enable'] ) && 1 == $uuc_settings['enable'] ) // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual || ( Jetpack::is_plugin_active( 'coming-soon/coming-soon.php' ) && isset( $csp4['status'] ) && ( 1 == $csp4['status'] || 2 == $csp4['status'] ) ) // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual + || /** * Allow plugins to mark a site as "under construction". * @@ -1211,7 +1212,7 @@ public static function is_site_verified_and_token( $request ) { * * @param false bool Is the site under construction? Default to false. */ - || true === apply_filters( 'jetpack_is_under_construction_plugin', false ) + true === apply_filters( 'jetpack_is_under_construction_plugin', false ) ) { return new WP_Error( 'forbidden', __( 'Site is under construction and cannot be verified', 'jetpack' ) ); } diff --git a/projects/plugins/jetpack/changelog/update-broken-phpdoc b/projects/plugins/jetpack/changelog/update-broken-phpdoc new file mode 100644 index 0000000000000..cbd775e892e5d --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-broken-phpdoc @@ -0,0 +1,5 @@ +Significance: patch +Type: other +Comment: Updated a comment, not changing any logic. + + diff --git a/projects/plugins/jetpack/modules/shortcodes/youtube.php b/projects/plugins/jetpack/modules/shortcodes/youtube.php index bf0cb546ceeb3..d97eb29b42f87 100644 --- a/projects/plugins/jetpack/modules/shortcodes/youtube.php +++ b/projects/plugins/jetpack/modules/shortcodes/youtube.php @@ -623,6 +623,7 @@ function wpcom_youtube_oembed_fetch_url( $provider, $url ) { if ( ! is_admin() + && /** * Allow oEmbeds in Jetpack's Comment form. * @@ -632,7 +633,7 @@ function wpcom_youtube_oembed_fetch_url( $provider, $url ) { * * @param int $allow_oembed Option to automatically embed all plain text URLs. */ - && apply_filters( 'jetpack_comments_allow_oembed', true ) + apply_filters( 'jetpack_comments_allow_oembed', true ) // No need for this on WordPress.com, this is done for multiple shortcodes at a time there. && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) ) { diff --git a/projects/plugins/jetpack/modules/theme-tools/social-menu.php b/projects/plugins/jetpack/modules/theme-tools/social-menu.php index c4feb6bacdec0..c639085071c53 100644 --- a/projects/plugins/jetpack/modules/theme-tools/social-menu.php +++ b/projects/plugins/jetpack/modules/theme-tools/social-menu.php @@ -25,17 +25,17 @@ function jetpack_social_menu_init() { return; } - /* - * Social Menu description. - * - * Rename the social menu description. - * - * @module theme-tools - * - * @since 3.9.0 - * - * @param string $social_menu_description Social Menu description - */ + /** + * Social Menu description. + * + * Rename the social menu description. + * + * @module theme-tools + * + * @since 3.9.0 + * + * @param string $social_menu_description Social Menu description + */ $social_menu_description = apply_filters( 'jetpack_social_menu_description', __( 'Social Menu', 'jetpack' ) ); // Register a new menu location From 7991e1b9f29db74d8020fb6f41b4d13d180bb710 Mon Sep 17 00:00:00 2001 From: Calypso Bot Date: Thu, 6 Feb 2025 16:48:31 +0200 Subject: [PATCH 03/22] phan: Update custom stubs (#41588) Co-authored-by: matticbot --- .phan/stubs/akismet-stubs.php | 2 +- .phan/stubs/gutenberg-stubs.php | 2 +- .phan/stubs/woocommerce-internal-stubs.php | 2 +- .phan/stubs/woocommerce-payments-stubs.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.phan/stubs/akismet-stubs.php b/.phan/stubs/akismet-stubs.php index 74b8062fe05be..0ce1519bcf851 100644 --- a/.phan/stubs/akismet-stubs.php +++ b/.phan/stubs/akismet-stubs.php @@ -1,6 +1,6 @@ Date: Thu, 6 Feb 2025 10:08:26 -0500 Subject: [PATCH 04/22] actions: Request Garage as reviewer for Phan stubs PRs (#41603) --- .github/workflows/update-phan-stubs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-phan-stubs.yml b/.github/workflows/update-phan-stubs.yml index 46c2230c4ce2b..944b9e14f9523 100644 --- a/.github/workflows/update-phan-stubs.yml +++ b/.github/workflows/update-phan-stubs.yml @@ -63,7 +63,7 @@ jobs: git checkout -b update/phan-custom-stubs git commit -am 'phan: Update custom stubs' git push origin HEAD - gh pr create --title 'phan: Update custom stubs' --body 'This is an automatic update generated by a GitHub Action. If closed it will be recreated the next time the action runs.' --label '[Pri] Normal' --label '[Type] Janitorial' --label '[Status] Needs Review' + gh pr create --title 'phan: Update custom stubs' --body 'This is an automatic update generated by a GitHub Action. If closed it will be recreated the next time the action runs.' --label '[Pri] Normal' --label '[Type] Janitorial' --label '[Status] Needs Review' --reviewer Automattic/jetpack-garage - name: Update existing branch if: steps.changes.outputs.needed == 'true' && steps.changes.outputs.has-branch == 'true' From 13ea3bc21445521e1ba441e60f64fa5c6cfa23f1 Mon Sep 17 00:00:00 2001 From: Cris Busquets Date: Thu, 6 Feb 2025 17:47:31 +0100 Subject: [PATCH 05/22] Newsletter: Fix Dotcom account connection copy (#39882) * Improved copy * changelog --------- Co-authored-by: Brandon Kraft --- .../jetpack/_inc/client/components/settings-card/index.jsx | 5 ++++- .../changelog/fix-dotcom-account-connection-newsletter | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 projects/plugins/jetpack/changelog/fix-dotcom-account-connection-newsletter diff --git a/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx b/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx index 8cb3d077921bd..8a7f201613c1a 100644 --- a/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx +++ b/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx @@ -447,7 +447,10 @@ export const SettingsCard = inprops => { return ( Date: Thu, 6 Feb 2025 11:55:25 -0500 Subject: [PATCH 06/22] Add comment to vaultpress.php to ensure SAST false positives are easier to flag (#37326) Add comment to save others time trying to write a PoC for a non-exploitable issue --- projects/plugins/vaultpress/changelog/patch-37326 | 5 +++++ projects/plugins/vaultpress/vaultpress.php | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 projects/plugins/vaultpress/changelog/patch-37326 diff --git a/projects/plugins/vaultpress/changelog/patch-37326 b/projects/plugins/vaultpress/changelog/patch-37326 new file mode 100644 index 0000000000000..e1b629fb1bdd8 --- /dev/null +++ b/projects/plugins/vaultpress/changelog/patch-37326 @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Only adding inline documentation. No code changes. + + diff --git a/projects/plugins/vaultpress/vaultpress.php b/projects/plugins/vaultpress/vaultpress.php index 75b35de25834b..d0928cee2cfa4 100644 --- a/projects/plugins/vaultpress/vaultpress.php +++ b/projects/plugins/vaultpress/vaultpress.php @@ -1819,6 +1819,10 @@ function parse_request( $wp ) { die( 0 ); break; case 'exec': + /* + * Despite appearances, this code is not an arbitrary code execution vulnerability due to the + * $this->validate_api_signature() check above. Static analysis tools will probably flag this. + */ $code = $_POST['code']; if ( !$code ) $this->response( "No Code Found" ); From 854e36c413c7866c276521aaad24ee7543ed5bd0 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Thu, 6 Feb 2025 14:46:26 -0300 Subject: [PATCH 07/22] Jetpack SEO: process assistant results summary (#41585) * address primary buttons styles * lower delay to 1500 on all steps * update onStart signature and add includeResults on steps * add results collection and summary process, remove unnecessary props on hooks, add includeResults where needed along with label * changelog --- .../fix-jetpack-seo-assistant-results-summary | 4 + .../seo-assistant/assistant-wizard.tsx | 44 +++++++---- .../components/seo-assistant/style.scss | 22 +++--- .../components/seo-assistant/types.tsx | 13 +++- .../seo-assistant/use-completion-step.tsx | 78 +++++++++++++------ .../use-meta-description-step.tsx | 10 +-- .../seo-assistant/use-title-step.tsx | 10 +-- .../seo-assistant/use-welcome-step.tsx | 2 +- 8 files changed, 115 insertions(+), 68 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/fix-jetpack-seo-assistant-results-summary diff --git a/projects/plugins/jetpack/changelog/fix-jetpack-seo-assistant-results-summary b/projects/plugins/jetpack/changelog/fix-jetpack-seo-assistant-results-summary new file mode 100644 index 0000000000000..3973a3d558e0c --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-jetpack-seo-assistant-results-summary @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Jetpack SEO: add completion summary step diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/assistant-wizard.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/assistant-wizard.tsx index d7cc994da7a87..49e6369ede00e 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/assistant-wizard.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/assistant-wizard.tsx @@ -22,7 +22,7 @@ export default function AssistantWizard( { close } ) { stepsEndRef.current?.scrollIntoView( { behavior: 'smooth' } ); }; const keywordsInputRef = useRef( null ); - const [ , setResults ] = useState( {} ); + const [ results, setResults ] = useState( {} ); const [ lastStepValue, setLastStepValue ] = useState( '' ); useEffect( () => { @@ -52,9 +52,10 @@ export default function AssistantWizard( { close } ) { await currentStepData?.onStart( { fromSkip: ! lastStepValue, stepValue: lastStepValue, + results, } ); setIsBusy( false ); - }, [ currentStepData, lastStepValue ] ); + }, [ currentStepData, lastStepValue, results ] ); const handleNext = useCallback( () => { debug( 'handleNext, stepsCount', stepsCount ); @@ -97,22 +98,25 @@ export default function AssistantWizard( { close } ) { setIsBusy( true ); const stepValue = await steps[ currentStep ]?.onSubmit?.(); debug( 'stepValue', stepValue ); - const newResults = { - [ steps[ currentStep ].id ]: { - value: steps[ currentStep ].value.trim(), - type: steps[ currentStep ].type, - label: steps[ currentStep ].label, - }, - }; - debug( 'newResults', newResults ); - setResults( prev => ( { ...prev, ...newResults } ) ); + if ( steps[ currentStep ].includeInResults ) { + const newResults = { + [ steps[ currentStep ].id ]: { + value: stepValue?.trim?.(), + type: steps[ currentStep ].type, + label: steps[ currentStep ].label, + }, + }; + debug( 'newResults', newResults ); + setResults( prev => ( { ...prev, ...newResults } ) ); + } debug( 'set last step value', stepValue ); - setLastStepValue( stepValue.trim() ); + setLastStepValue( stepValue?.trim?.() ); if ( steps[ currentStep ]?.type === 'completion' ) { + debug( 'completion step, closing wizard' ); handleDone(); } else { - // always give half a second before moving forward + debug( 'step type', steps[ currentStep ]?.type ); handleNext(); } }, [ currentStep, handleDone, handleNext, steps ] ); @@ -139,6 +143,7 @@ export default function AssistantWizard( { close } ) { const handleBack = () => { if ( currentStep > 1 ) { + setIsBusy( true ); debug( 'moving back to ' + ( currentStep - 1 ) ); setCurrentStep( currentStep - 1 ); setCurrentStepData( steps[ currentStep - 1 ] ); @@ -148,8 +153,19 @@ export default function AssistantWizard( { close } ) { const handleSkip = useCallback( async () => { setIsBusy( true ); await steps[ currentStep ]?.onSkip?.(); + const step = steps[ currentStep ]; + if ( ! results[ step.id ] && step.includeInResults ) { + setResults( prev => ( { + ...prev, + [ step.id ]: { + value: '', + type: step.type, + label: step.label, + }, + } ) ); + } handleNext(); - }, [ currentStep, steps, handleNext ] ); + }, [ currentStep, steps, handleNext, results ] ); const handleRetry = useCallback( async () => { debug( 'handleRetry' ); diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/style.scss b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/style.scss index bd808045c8931..0acaea3025d52 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/style.scss +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/style.scss @@ -17,6 +17,15 @@ width: unset; } + .components-button.is-primary { + background-color: #3858E9; + } + + .components-button.is-secondary { + box-shadow: inset 0 0 0 1px #3858e9, 0 0 0 currentColor; + color: #3858e9; + } + &__header { flex: 0 0 auto; display: flex; @@ -154,10 +163,6 @@ box-shadow: none; outline: 0; } - - .components-button.is-primary { - background-color: #3858E9; - } } // submit and retry buttons @@ -204,15 +209,6 @@ align-items: center; gap: 16px; animation: assistantInputAppear 0.3s ease-out; - - .components-button.is-primary { - background-color: #3858E9; - } - - .components-button.is-secondary { - box-shadow: inset 0 0 0 1px #3858e9, 0 0 0 currentColor; - color: #3858e9; - } } &__completion { diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/types.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/types.tsx index cca9bcebce3e3..4556ec26f338e 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/types.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/types.tsx @@ -11,22 +11,29 @@ export interface Message { export type OptionMessage = Pick< Message, 'id' | 'content' >; +export interface Results { + [ key: string ]: { + value: string; + type: string; + label: string; + }; +} + export interface Step { id: string; title: string; label?: string; messages: Message[]; type: StepType; - onStart?: ( options?: { fromSkip: boolean; stepValue: string } ) => void; + onStart?: ( options?: { fromSkip: boolean; stepValue: string; results: Results } ) => void; onSubmit?: () => Promise< string >; onSkip?: () => void; value?: string; setValue?: | React.Dispatch< React.SetStateAction< string > > | React.Dispatch< React.SetStateAction< Array< string > > >; - setCompleted?: React.Dispatch< React.SetStateAction< boolean > >; - completed?: boolean; autoAdvance?: number; + includeInResults?: boolean; // Input step properties placeholder?: string; diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-completion-step.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-completion-step.tsx index 221b3a01b1751..50ad239eeb007 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-completion-step.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-completion-step.tsx @@ -1,16 +1,16 @@ import { createInterpolateElement, useCallback, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { useMessages } from './wizard-messages'; -import type { Step } from './types'; +import type { Step, Results } from './types'; export const useCompletionStep = (): Step => { - const [ keywords, setKeywords ] = useState( '' ); - const [ completed, setCompleted ] = useState( false ); + const [ value, setValue ] = useState( '' ); const { messages, setMessages, addMessage } = useMessages(); const startHandler = useCallback( - async ( { fromSkip } ) => { + async ( { fromSkip, results } ) => { const firstMessages = []; + if ( fromSkip ) { firstMessages.push( { content: __( 'Skipped!', 'jetpack' ), @@ -19,32 +19,62 @@ export const useCompletionStep = (): Step => { } ); } setMessages( firstMessages ); - await new Promise( resolve => setTimeout( resolve, 2000 ) ); + + await new Promise( resolve => setTimeout( resolve, 1500 ) ); + + const resultsString = Object.values( results ) + .map( ( result: Results[ string ] ) => `${ result.value ? 'βœ…' : '❌' } ${ result.label }` ) + .join( '
' ); + addMessage( { content: createInterpolateElement( - "Here's your updated checklist:
βœ… Title
βœ… Meta description

", + `Here's your updated checklist:
${ resultsString }

`, { br:
, } ), id: '1', } ); - addMessage( { - content: createInterpolateElement( - __( - 'SEO optimization complete! πŸŽ‰
Your blog post is now search-engine friendly.', - 'jetpack' + + const incomplete: { total: number; completed: number } = Object.values( results ).reduce( + ( acc: { total: number; completed: number }, result: Results[ string ] ) => { + const total = acc.total + 1; + const completed = acc.completed + ( result.value ? 1 : 0 ); + return { total, completed }; + }, + { total: 0, completed: 0 } + ) as { total: number; completed: number }; + + const incompleteString = + incomplete.completed === incomplete.total + ? '' + : `${ incomplete.completed } out of ${ incomplete.total }`; + + if ( incompleteString ) { + addMessage( { + content: createInterpolateElement( + `You've optimized ${ incompleteString } items! πŸŽ‰
Your post is looking great! Come back anytime to complete the rest.`, + { + strong: , + br:
, + } ), - { br:
, strong: } - ), - showIcon: false, - id: '3', - } ); - addMessage( { - content: __( 'Happy blogging! 😊', 'jetpack' ), - showIcon: false, - id: '4', - } ); + id: '2', + } ); + } else { + addMessage( { + content: createInterpolateElement( + __( + 'SEO optimization complete! πŸŽ‰
Your blog post is now search-engine friendly.
Happy blogging! 😊', + 'jetpack' + ), + { br:
, strong: } + ), + showIcon: false, + id: '3', + } ); + } + return 'completion'; }, [ setMessages, addMessage ] @@ -58,9 +88,7 @@ export const useCompletionStep = (): Step => { type: 'completion', onStart: startHandler, submitCtaLabel: __( 'Done!', 'jetpack' ), - completed, - setCompleted, - value: keywords, - setValue: setKeywords, + value, + setValue, }; }; diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx index ad37940d48b7d..4a37081440edb 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx @@ -9,7 +9,6 @@ export const useMetaDescriptionStep = (): Step => { const [ metaDescriptionOptions, setMetaDescriptionOptions ] = useState< OptionMessage[] >( [] ); const { messages, setMessages, addMessage, editLastMessage, setSelectedMessage } = useMessages(); const { editPost } = useDispatch( 'core/editor' ); - const [ completed, setCompleted ] = useState( false ); const handleMetaDescriptionSelect = useCallback( ( option: OptionMessage ) => { @@ -22,7 +21,6 @@ export const useMetaDescriptionStep = (): Step => { const handleMetaDescriptionSubmit = useCallback( async () => { await editPost( { meta: { advanced_seo_description: selectedMetaDescription } } ); addMessage( { content: __( 'Meta description updated! βœ…', 'jetpack' ) } ); - setCompleted( true ); return selectedMetaDescription; }, [ selectedMetaDescription, addMessage, editPost ] ); @@ -54,7 +52,7 @@ export const useMetaDescriptionStep = (): Step => { 'Explore breathtaking flower and plant photography in our Flora Guide, featuring tips and inspiration for gardening and plant enthusiasts to enhance their outdoor spaces.', }, ] ), - 3000 + 1500 ) ); } @@ -90,7 +88,7 @@ export const useMetaDescriptionStep = (): Step => { 'Explore breathtaking flower and plant photography in our Flora Guide, featuring tips and inspiration for gardening and plant enthusiasts to enhance their outdoor spaces.', }, ] ), - 3000 + 1500 ) ); @@ -101,6 +99,7 @@ export const useMetaDescriptionStep = (): Step => { return { id: 'meta', title: __( 'Add meta description', 'jetpack' ), + label: __( 'Meta description', 'jetpack' ), messages: messages, type: 'options', options: metaDescriptionOptions, @@ -112,7 +111,6 @@ export const useMetaDescriptionStep = (): Step => { onStart: handleMetaDescriptionGenerate, value: selectedMetaDescription, setValue: setSelectedMetaDescription, - completed, - setCompleted, + includeInResults: true, }; }; diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-title-step.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-title-step.tsx index 87f2764b82385..51d6b8171d3c6 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-title-step.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-title-step.tsx @@ -9,7 +9,6 @@ export const useTitleStep = (): Step => { const [ titleOptions, setTitleOptions ] = useState< OptionMessage[] >( [] ); const { editPost } = useDispatch( 'core/editor' ); const { messages, setMessages, addMessage, editLastMessage, setSelectedMessage } = useMessages(); - const [ completed, setCompleted ] = useState( false ); const [ prevStepValue, setPrevStepValue ] = useState(); const handleTitleSelect = useCallback( @@ -57,7 +56,7 @@ export const useTitleStep = (): Step => { 'Flora Guide: Beautiful Photos of Flowers and Plants for Gardening Enthusiasts', }, ] ), - 3000 + 1500 ) ); } @@ -107,7 +106,7 @@ export const useTitleStep = (): Step => { 'Flora Guide: Beautiful Photos of Flowers and Plants for Gardening Enthusiasts', }, ] ), - 2000 + 1500 ) ); setTitleOptions( [ ...titleOptions, ...newTitles ] ); @@ -117,13 +116,13 @@ export const useTitleStep = (): Step => { const handleTitleSubmit = useCallback( async () => { await editPost( { title: selectedTitle, meta: { jetpack_seo_html_title: selectedTitle } } ); addMessage( { content: __( 'Title updated! βœ…', 'jetpack' ) } ); - setCompleted( true ); return selectedTitle; }, [ selectedTitle, addMessage, editPost ] ); return { id: 'title', title: __( 'Optimise Title', 'jetpack' ), + label: __( 'Title', 'jetpack' ), messages, type: 'options', options: titleOptions, @@ -135,7 +134,6 @@ export const useTitleStep = (): Step => { onStart: handleTitleGenerate, value: selectedTitle, setValue: setSelectedTitle, - completed, - setCompleted, + includeInResults: true, }; }; diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-welcome-step.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-welcome-step.tsx index 5163f890b8dc0..6b0d1eb7f24b7 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-welcome-step.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-welcome-step.tsx @@ -26,6 +26,6 @@ export const useWelcomeStep = (): Step => { id: '2', }, ], - autoAdvance: 2000, + autoAdvance: 1500, }; }; From 347017a1ebd95d7fb8ba7a1d27c6a894bb832a82 Mon Sep 17 00:00:00 2001 From: Erick Danzer Date: Thu, 6 Feb 2025 12:52:03 -0700 Subject: [PATCH 08/22] Forms: fix invalid field ids (#41564) --- .../forms/changelog/fix-invalid-field-ids | 4 +++ .../contact-form/class-contact-form-field.php | 26 ++++++++++++++++--- .../jetpack/changelog/fix-invalid-field-ids | 4 +++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 projects/packages/forms/changelog/fix-invalid-field-ids create mode 100644 projects/plugins/jetpack/changelog/fix-invalid-field-ids diff --git a/projects/packages/forms/changelog/fix-invalid-field-ids b/projects/packages/forms/changelog/fix-invalid-field-ids new file mode 100644 index 0000000000000..ced045f8f6a66 --- /dev/null +++ b/projects/packages/forms/changelog/fix-invalid-field-ids @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Forms: Fix invalid html IDs. diff --git a/projects/packages/forms/src/contact-form/class-contact-form-field.php b/projects/packages/forms/src/contact-form/class-contact-form-field.php index 52e5f6385e6e6..85b78314041eb 100644 --- a/projects/packages/forms/src/contact-form/class-contact-form-field.php +++ b/projects/packages/forms/src/contact-form/class-contact-form-field.php @@ -655,7 +655,7 @@ public function render_textarea_field( $id, $label, $value, $class, $required, $ /** * Return the HTML for the radio field. * - * @param int $id - the ID. + * @param string $id - the ID (starts with 'g' - see constructor). * @param string $label - the label. * @param string $value - the value of the field. * @param string $class - the field class. @@ -670,11 +670,20 @@ public function render_radio_field( $id, $label, $value, $class, $required, $req $field_style = 'style="' . $this->option_styles . '"'; + $used_html_ids = array(); + foreach ( (array) $this->get_attribute( 'options' ) as $option_index => $option ) { $option = Contact_Form_Plugin::strip_tags( $option ); if ( is_string( $option ) && $option !== '' ) { $radio_value = $this->get_option_value( $this->get_attribute( 'values' ), $option_index, $option ); - $radio_id = "$id-$radio_value"; + $radio_id = $id . '-' . sanitize_html_class( $radio_value ); + + // If exact id was already used in this radio group, append option index. + // Multiple 'blue' options would give id-blue, id-blue-1, id-blue-2, etc. + if ( isset( $used_html_ids[ $radio_id ] ) ) { + $radio_id .= '-' . $option_index; + } + $used_html_ids[ $radio_id ] = true; $field .= "

"; $field .= "option_styles . '"'; + $used_html_ids = array(); + foreach ( (array) $this->get_attribute( 'options' ) as $option_index => $option ) { $option = Contact_Form_Plugin::strip_tags( $option ); if ( is_string( $option ) && $option !== '' ) { $checkbox_value = $this->get_option_value( $this->get_attribute( 'values' ), $option_index, $option ); - $checkbox_id = "$id-$checkbox_value"; + $checkbox_id = $id . '-' . sanitize_html_class( $checkbox_value ); + + // If exact id was already used in this checkbox group, append option index. + // Multiple 'blue' options would give id-blue, id-blue-1, id-blue-2, etc. + if ( isset( $used_html_ids[ $checkbox_id ] ) ) { + $checkbox_id .= '-' . $option_index; + } + $used_html_ids[ $checkbox_id ] = true; $field .= "

"; $field .= " Date: Thu, 6 Feb 2025 12:00:51 -0800 Subject: [PATCH 09/22] Forms: Fix form submissions across pages that have pagination applied (#41407) * Forms: Add new page support for forms This Pr adds support for form processing across multiple pages * changelog * sanitize with absint instead of sanitize_text_field * guard against invalid $postdata * Store entry_page and use in in there permalink * Consoludate all the non printable fields --- .../changelog/fix-form-submit-miulti-page | 4 ++ ...class-wpcom-rest-api-v2-endpoint-forms.php | 8 +--- .../forms/src/contact-form/class-admin.php | 15 ++++--- .../class-contact-form-plugin.php | 26 ++++++++++++- .../src/contact-form/class-contact-form.php | 39 +++++++++++++++---- .../changelog/fix-form-submit-miulti-page | 4 ++ 6 files changed, 72 insertions(+), 24 deletions(-) create mode 100644 projects/packages/forms/changelog/fix-form-submit-miulti-page create mode 100644 projects/plugins/jetpack/changelog/fix-form-submit-miulti-page diff --git a/projects/packages/forms/changelog/fix-form-submit-miulti-page b/projects/packages/forms/changelog/fix-form-submit-miulti-page new file mode 100644 index 0000000000000..bf6cbe84766ef --- /dev/null +++ b/projects/packages/forms/changelog/fix-form-submit-miulti-page @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Forms: Add support for having multiple forms accross paginated pages diff --git a/projects/packages/forms/src/class-wpcom-rest-api-v2-endpoint-forms.php b/projects/packages/forms/src/class-wpcom-rest-api-v2-endpoint-forms.php index ad29686fb12f2..0e9b8295363dd 100644 --- a/projects/packages/forms/src/class-wpcom-rest-api-v2-endpoint-forms.php +++ b/projects/packages/forms/src/class-wpcom-rest-api-v2-endpoint-forms.php @@ -159,13 +159,7 @@ public function get_responses( $request ) { array_diff_key( $filter_args, array( 'post_parent' => '' ) ) ); - $base_fields = array( - 'email_marketing_consent' => '', - 'entry_title' => '', - 'entry_permalink' => '', - 'feedback_id' => '', - ); - + $base_fields = Contact_Form_Plugin::NON_PRINTABLE_FIELDS; $data_defaults = array( '_feedback_author' => '', '_feedback_author_email' => '', diff --git a/projects/packages/forms/src/contact-form/class-admin.php b/projects/packages/forms/src/contact-form/class-admin.php index 0f2ec1379352c..77bb034a45c59 100644 --- a/projects/packages/forms/src/contact-form/class-admin.php +++ b/projects/packages/forms/src/contact-form/class-admin.php @@ -702,12 +702,6 @@ public function grunion_manage_post_column_from( $post ) { * @return void */ public function grunion_manage_post_column_response( $post ) { - $non_printable_keys = array( - 'email_marketing_consent', - 'entry_title', - 'entry_permalink', - 'feedback_id', - ); $post_content = get_post_field( 'post_content', $post->ID ); $content = explode( '', $post_content ); @@ -750,7 +744,12 @@ public function grunion_manage_post_column_response( $post ) { } } - $response_fields = array_diff_key( $response_fields, array_flip( $non_printable_keys ) ); + $url = get_permalink( $post->post_parent ); + if ( isset( $response_fields['entry_page'] ) ) { + $url = add_query_arg( 'page', $response_fields['entry_page'], $url ); + } + + $response_fields = array_diff_key( $response_fields, array_flip( array_keys( Contact_Form_Plugin::NON_PRINTABLE_FIELDS ) ) ); echo '

'; echo ''; } diff --git a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php index dc493a171a08f..b022a52e2a67c 100644 --- a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php +++ b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php @@ -52,6 +52,20 @@ class Contact_Form_Plugin { */ private $pde_email_address = ''; + /* + * Field keys that might be present in the entry json but we don't want to show to the admin + * since they not something that the visitor entered into the form. + * + * @var array + */ + const NON_PRINTABLE_FIELDS = array( + 'entry_title' => '', + 'email_marketing_consent' => '', + 'entry_permalink' => '', + 'entry_page' => '', + 'feedback_id' => '', + ); + /** * Initializing function. */ @@ -712,8 +726,16 @@ public function process_form_submission() { // Process the content to populate Contact_Form::$last if ( $post ) { + if ( str_contains( $post->post_content, '' ) ) { + $postdata = generate_postdata( $post ); + $page = isset( $_POST['page'] ) ? absint( wp_unslash( $_POST['page'] ) ) : null; // phpcs:Ignore WordPress.Security.NonceVerification.Missing + $paged = isset( $page ) ? $page : 1; + $content = isset( $postdata['pages'][ $paged - 1 ] ) ? $postdata['pages'][ $paged - 1 ] : $post->post_content; + } else { + $content = $post->post_content; + } /** This filter is already documented in core. wp-includes/post-template.php */ - apply_filters( 'the_content', $post->post_content ); + apply_filters( 'the_content', $content ); } } @@ -1148,7 +1170,7 @@ public function get_post_meta_for_csv_export( $post_id, $has_json_data = false ) $content_fields = self::parse_fields_from_content( $post_id ); $all_fields = isset( $content_fields['_feedback_all_fields'] ) ? $content_fields['_feedback_all_fields'] : array(); $md = $has_json_data - ? array_diff_key( $all_fields, array_flip( array( 'entry_title', 'email_marketing_consent', 'entry_permalink', 'feedback_id' ) ) ) + ? array_diff_key( $all_fields, array_flip( array_keys( self::NON_PRINTABLE_FIELDS ) ) ) : (array) get_post_meta( $post_id, '_feedback_extra_fields', true ); $md['-3_response_date'] = get_the_date( 'Y-m-d H:i:s', $post_id ); diff --git a/projects/packages/forms/src/contact-form/class-contact-form.php b/projects/packages/forms/src/contact-form/class-contact-form.php index 96bea7549ca73..764c7d52240de 100644 --- a/projects/packages/forms/src/contact-form/class-contact-form.php +++ b/projects/packages/forms/src/contact-form/class-contact-form.php @@ -82,7 +82,7 @@ class Contact_Form extends Contact_Form_Shortcode { * @param string $content - the content. */ public function __construct( $attributes, $content = null ) { - global $post; + global $post, $page; // Set up the default subject and recipient for this form. $default_to = ''; @@ -123,7 +123,7 @@ public function __construct( $attributes, $content = null ) { if ( ! isset( $attributes['id'] ) ) { $attributes['id'] = ''; } - $attributes['id'] = $attributes['id'] . '-' . ( count( self::$forms ) + 1 ); + $attributes['id'] = $attributes['id'] . '-' . ( count( self::$forms ) + 1 ) . '-' . $page; } $this->hash = sha1( wp_json_encode( $attributes ) ); @@ -249,8 +249,7 @@ public static function style_on() { * @return string HTML for the concat form. */ public static function parse( $attributes, $content ) { - global $post; - + global $post, $page; // $page is used in the contact-form submission redirect if ( Settings::is_syncing() ) { return ''; } @@ -347,6 +346,9 @@ public static function parse( $attributes, $content ) { } else { // Submit form to the post permalink $url = get_permalink(); + if ( $page ) { + $url = add_query_arg( 'page', $page, $url ); + } } // For SSL/TLS page. See RFC 3986 Section 4.2 @@ -364,7 +366,7 @@ public static function parse( $attributes, $content ) { * @param $post $GLOBALS['post'] Post global variable. * @param int $id Contact Form ID. */ - $url = apply_filters( 'grunion_contact_form_form_action', "{$url}#contact-form-{$id}", $GLOBALS['post'], $id ); + $url = apply_filters( 'grunion_contact_form_form_action', "{$url}#contact-form-{$id}", $GLOBALS['post'], $id, $page ); $has_submit_button_block = str_contains( $content, 'wp-block-jetpack-button' ); $form_classes = 'contact-form commentsblock'; $post_title = $post->post_title ?? ''; @@ -434,6 +436,10 @@ public static function parse( $attributes, $content ) { $r .= "\t\t\n"; $r .= "\t\t\n"; + if ( $page && $page > 1 ) { + $r .= "\t\t\n"; + } + if ( ! $has_submit_button_block ) { $r .= "\t

\n"; } @@ -1323,10 +1329,14 @@ public function process_submission() { $entry_values = array( 'entry_title' => the_title_attribute( 'echo=0' ), - 'entry_permalink' => esc_url( get_permalink( get_the_ID() ) ), + 'entry_permalink' => esc_url( self::get_permalink( get_the_ID() ) ), 'feedback_id' => $feedback_id, ); + if ( isset( $_POST['page'] ) ) { // phpcs:Ignore WordPress.Security.NonceVerification.Missing + $entry_values['entry_page'] = absint( wp_unslash( $_POST['page'] ) ); // phpcs:Ignore WordPress.Security.NonceVerification.Missing + } + $all_values = array_merge( $all_values, $entry_values ); /** This filter is already documented in \Automattic\Jetpack\Forms\ContactForm\Admin */ @@ -1338,7 +1348,7 @@ public function process_submission() { if ( $block_template || $block_template_part || $widget ) { $url = home_url( '/' ); } else { - $url = get_permalink( $post->ID ); + $url = self::get_permalink( $post->ID ); } // translators: the time of the form submission. @@ -1645,6 +1655,21 @@ public function process_submission() { wp_redirect( $redirect ); exit( 0 ); } + /** + * Get the permalink for the post ID that include the page query parameter if it was set. + * + * @param int $post_id The post ID. + * + * return string The permalink for the post ID. + */ + public static function get_permalink( $post_id ) { + $url = get_permalink( $post_id ); + $page = isset( $_POST['page'] ) ? absint( wp_unslash( $_POST['page'] ) ) : null; // phpcs:Ignore WordPress.Security.NonceVerification.Missing + if ( $page ) { + return add_query_arg( 'page', $page, $url ); + } + return $url; + } /** * Wrapper for wp_mail() that enables HTML messages with text alternatives diff --git a/projects/plugins/jetpack/changelog/fix-form-submit-miulti-page b/projects/plugins/jetpack/changelog/fix-form-submit-miulti-page new file mode 100644 index 0000000000000..444f11caf4071 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-form-submit-miulti-page @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + + Forms: Add support for having multiple forms accross paginated pages. From da84905ffde1b168a46afce4f53d298a7d03cb34 Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Thu, 6 Feb 2025 12:01:16 -0800 Subject: [PATCH 10/22] Forms: fix seperators not aligning consistently (#40967) * Fix: seperator block styling in the form block * changelog * Make the seperator earier to overwrite * Fix the different styles to have the desired width. * Remove any vertical margin * Update projects/packages/forms/changelog/fix-25025-form-seperator Co-authored-by: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> * Update projects/plugins/jetpack/changelog/fix-25025-form-seperator Co-authored-by: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> * Remove the is-style-default class * update to code to be more together. * Update to use 100px vs 150px since that is the most common default saparator * Also inherit the styles in the editor * bug fix --------- Co-authored-by: Miguel Lezama Co-authored-by: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> --- .../packages/forms/changelog/fix-25025-form-seperator | 4 ++++ .../forms/src/blocks/contact-form/editor.scss | 11 +++++++++++ .../packages/forms/src/contact-form/css/grunion.css | 10 ++++++++++ .../jetpack/changelog/fix-25025-form-seperator | 4 ++++ 4 files changed, 29 insertions(+) create mode 100644 projects/packages/forms/changelog/fix-25025-form-seperator create mode 100644 projects/plugins/jetpack/changelog/fix-25025-form-seperator diff --git a/projects/packages/forms/changelog/fix-25025-form-seperator b/projects/packages/forms/changelog/fix-25025-form-seperator new file mode 100644 index 0000000000000..6c872c241915f --- /dev/null +++ b/projects/packages/forms/changelog/fix-25025-form-seperator @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Improves the styling options of the separator block when placed inside the form block diff --git a/projects/packages/forms/src/blocks/contact-form/editor.scss b/projects/packages/forms/src/blocks/contact-form/editor.scss index d506b5bc8dc98..15b4e982be665 100644 --- a/projects/packages/forms/src/blocks/contact-form/editor.scss +++ b/projects/packages/forms/src/blocks/contact-form/editor.scss @@ -93,6 +93,17 @@ &[data-type='jetpack/field-consent'] { align-self: center; } + + &:where( .wp-block-jetpack-contact-form .wp-block-separator ){ + max-width: var( --wp--preset--spacing--80, 100px ); + margin-left: auto; + margin-right: auto; + } + &:where( .wp-block-jetpack-contact-form .wp-block-separator.is-style-wide ), + &:where( .wp-block-jetpack-contact-form .wp-block-separator.is-style-dots ) { + max-width: inherit; + } + } } diff --git a/projects/packages/forms/src/contact-form/css/grunion.css b/projects/packages/forms/src/contact-form/css/grunion.css index d189111812244..30f83ecd6565c 100644 --- a/projects/packages/forms/src/contact-form/css/grunion.css +++ b/projects/packages/forms/src/contact-form/css/grunion.css @@ -235,6 +235,16 @@ box-sizing: border-box; } +:where( .wp-block-jetpack-contact-form .wp-block-separator ) { + max-width: var( --wp--preset--spacing--80, 100px ); + margin-top: 0; + margin-bottom: 0; +} +:where( .wp-block-jetpack-contact-form .wp-block-separator.is-style-wide ), +:where( .wp-block-jetpack-contact-form .wp-block-separator.is-style-dots ) { + max-width: inherit; +} + /* Added circa Nov 2022: container class assigned to topmost block div */ .wp-block-jetpack-contact-form-container.alignfull .wp-block-jetpack-contact-form { padding-right: 0; diff --git a/projects/plugins/jetpack/changelog/fix-25025-form-seperator b/projects/plugins/jetpack/changelog/fix-25025-form-seperator new file mode 100644 index 0000000000000..9814b5a45b694 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-25025-form-seperator @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +Forms: Improve the styling of the separator block when placed inside the form block From 4b4b4e32aea893b5847f1a4cc71d274dd493631a Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Thu, 6 Feb 2025 12:01:39 -0800 Subject: [PATCH 11/22] Forms: Fixes date validation bug (#41611) * Forms: Fix the formatting if we use multiple date pickers on the page. * Forms: No need to enqueue the localize jquery ui since it gets loaded anyways. * changelog --- .../forms/changelog/fix-date-validation-bug | 4 ++++ .../contact-form/class-contact-form-field.php | 2 -- .../src/contact-form/js/grunion-frontend.js | 20 ++++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 projects/packages/forms/changelog/fix-date-validation-bug diff --git a/projects/packages/forms/changelog/fix-date-validation-bug b/projects/packages/forms/changelog/fix-date-validation-bug new file mode 100644 index 0000000000000..b812fbbf2ae5f --- /dev/null +++ b/projects/packages/forms/changelog/fix-date-validation-bug @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Forms: fixes the date format input if multiple date pickers are used with different date formats. diff --git a/projects/packages/forms/src/contact-form/class-contact-form-field.php b/projects/packages/forms/src/contact-form/class-contact-form-field.php index 85b78314041eb..4cd4819420d45 100644 --- a/projects/packages/forms/src/contact-form/class-contact-form-field.php +++ b/projects/packages/forms/src/contact-form/class-contact-form-field.php @@ -907,8 +907,6 @@ public function render_date_field( $id, $label, $value, $class, $required, $requ wp_enqueue_style( 'jp-jquery-ui-datepicker', plugins_url( '../../dist/contact-form/css/jquery-ui-datepicker.css', __FILE__ ), array( 'dashicons' ), '1.0' ); - // Using Core's built-in datepicker localization routine - wp_localize_jquery_ui_datepicker(); return $field; } diff --git a/projects/packages/forms/src/contact-form/js/grunion-frontend.js b/projects/packages/forms/src/contact-form/js/grunion-frontend.js index c10e153fc19e6..b615836bb8a18 100644 --- a/projects/packages/forms/src/contact-form/js/grunion-frontend.js +++ b/projects/packages/forms/src/contact-form/js/grunion-frontend.js @@ -1,13 +1,15 @@ jQuery( function ( $ ) { const $input = $( '.contact-form input.jp-contact-form-date' ); - const dateFormat = $input.attr( 'data-format' ) || 'yy-mm-dd'; - - $input.datepicker( { - dateFormat, - constrainInput: false, - showOptions: { direction: 'down' }, - onSelect: function () { - $( this ).focus(); - }, + $input.each( function () { + const el = $( this ); + const dateFormat = el.attr( 'data-format' ) || 'yy-mm-dd'; + el.datepicker( { + dateFormat, + constrainInput: false, + showOptions: { direction: 'down' }, + onSelect: function () { + $( this ).focus(); + }, + } ); } ); } ); From a8e7c90ab112e7278941e765fd2880a5e86c8745 Mon Sep 17 00:00:00 2001 From: Dylan Munson <65001528+CodeyGuyDylan@users.noreply.github.com> Date: Thu, 6 Feb 2025 13:05:57 -0700 Subject: [PATCH 12/22] Fix my jetpack protect card shield inconsistency (#41560) * Show Protect WAF as disabled when firewall is disabled * changelog * Ensure variable is defined --- .../protect-card/auto-firewall-status.tsx | 5 +++-- .../protect-card/use-protect-tooltip-copy.ts | 3 ++- .../fix-my-jetpack-protect-card-shield-inconsistency | 4 ++++ projects/packages/my-jetpack/global.d.ts | 1 + .../packages/my-jetpack/src/class-initializer.php | 11 +++++++---- 5 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 projects/packages/my-jetpack/changelog/fix-my-jetpack-protect-card-shield-inconsistency diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx index 9b1d94839270e..9b12175cf32c1 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx @@ -19,10 +19,11 @@ export const AutoFirewallStatus = () => { const { protect: { wafConfig: wafData }, } = getMyJetpackWindowInitialState(); - const { jetpack_waf_automatic_rules: isAutoFirewallEnabled } = wafData || {}; + const { jetpack_waf_automatic_rules: isAutoFirewallEnabled, waf_enabled: isWafEnabled } = + wafData || {}; if ( isPluginActive && isSiteConnected ) { - if ( isAutoFirewallEnabled ) { + if ( isAutoFirewallEnabled && isWafEnabled ) { return ; } diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts index 45f7f150190f9..662b37aff79ec 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts @@ -50,6 +50,7 @@ export function useProtectTooltipCopy(): TooltipContent { blocked_logins: blockedLoginsCount, brute_force_protection: hasBruteForceProtection, waf_supported: wafSupported, + waf_enabled: isWafEnabled, } = wafData || {}; const pluginsCount = fromScanPlugins.length || Object.keys( plugins ).length; @@ -247,7 +248,7 @@ export function useProtectTooltipCopy(): TooltipContent { ), }, autoFirewallTooltip: - ( hasProtectPaidPlan && ! isAutoFirewallEnabled ) || ! wafSupported + ( hasProtectPaidPlan && ( ! isAutoFirewallEnabled || ! isWafEnabled ) ) || ! wafSupported ? { title: __( 'Auto-Firewall: Inactive', 'jetpack-my-jetpack' ), text: wafSupported diff --git a/projects/packages/my-jetpack/changelog/fix-my-jetpack-protect-card-shield-inconsistency b/projects/packages/my-jetpack/changelog/fix-my-jetpack-protect-card-shield-inconsistency new file mode 100644 index 0000000000000..48b9490da9170 --- /dev/null +++ b/projects/packages/my-jetpack/changelog/fix-my-jetpack-protect-card-shield-inconsistency @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fix bug where firewall was displayed as active if automatic rules were enabled but firewall was off diff --git a/projects/packages/my-jetpack/global.d.ts b/projects/packages/my-jetpack/global.d.ts index 117857b70ec97..eb8fc175024ce 100644 --- a/projects/packages/my-jetpack/global.d.ts +++ b/projects/packages/my-jetpack/global.d.ts @@ -288,6 +288,7 @@ interface Window { jetpack_waf_share_debug_data: boolean; standalone_mode: boolean; waf_supported: boolean; + waf_enabled: boolean; }; }; videopress: { diff --git a/projects/packages/my-jetpack/src/class-initializer.php b/projects/packages/my-jetpack/src/class-initializer.php index ec2a40c7af88b..b8ee063b144f4 100644 --- a/projects/packages/my-jetpack/src/class-initializer.php +++ b/projects/packages/my-jetpack/src/class-initializer.php @@ -237,8 +237,9 @@ public static function enqueue_scripts() { $scan_data = Products\Protect::get_protect_data(); self::update_historically_active_jetpack_modules(); - $waf_config = array(); - $waf_supported = false; + $waf_config = array(); + $waf_supported = false; + $is_waf_enabled = false; $sandboxed_domain = ''; $is_dev_version = false; @@ -248,8 +249,9 @@ public static function enqueue_scripts() { } if ( class_exists( 'Automattic\Jetpack\Waf\Waf_Runner' ) ) { - $waf_config = Waf_Runner::get_config(); - $waf_supported = Waf_Runner::is_supported_environment(); + $waf_config = Waf_Runner::get_config(); + $is_waf_enabled = Waf_Runner::is_enabled(); + $waf_supported = Waf_Runner::is_supported_environment(); } wp_localize_script( @@ -313,6 +315,7 @@ public static function enqueue_scripts() { $waf_config, array( 'waf_supported' => $waf_supported, + 'waf_enabled' => $is_waf_enabled, ), array( 'blocked_logins' => (int) get_site_option( 'jetpack_protect_blocked_attempts', 0 ) ) ), From 6ba505ac680148f67d0c5022d28f3b790ff65d18 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Thu, 6 Feb 2025 18:20:15 -0300 Subject: [PATCH 13/22] Jetpack SEO: fix assistant chat options (#41616) * fix and compensate spacings/gaps on step messages * changelog --- .../fix-jetpack-seo-assistant-chat-spacings | 4 +++ .../components/seo-assistant/style.scss | 27 +++++++++++++++++-- .../seo-assistant/wizard-messages.tsx | 1 + 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/fix-jetpack-seo-assistant-chat-spacings diff --git a/projects/plugins/jetpack/changelog/fix-jetpack-seo-assistant-chat-spacings b/projects/plugins/jetpack/changelog/fix-jetpack-seo-assistant-chat-spacings new file mode 100644 index 0000000000000..c0b75e32269f6 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-jetpack-seo-assistant-chat-spacings @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Jetpack SEO: fix gap/spacing between chat bubbles and options diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/style.scss b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/style.scss index 0acaea3025d52..8b1b0d1c86dab 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/style.scss +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/style.scss @@ -63,6 +63,7 @@ &__content { flex: 1 1 auto; + gap: 8px; display: flex; flex-direction: column; height: 100%; @@ -78,11 +79,24 @@ flex: 1 1 auto; display: flex; flex-direction: column; - gap: 16px; - padding: 8px 8px 8px 0; + gap: 8px; + padding: 0 8px; overflow-y: auto; scroll-behavior: smooth; align-items: flex-start; + + // weirdly placed here, this makes the first option have a top margin + & > .assistant-wizard__message.is-option { + margin-top: 8px; + } + + & > .assistant-wizard__message.is-option ~ .assistant-wizard__message.is-option { + margin-top: 0px; + } + + & > .assistant-wizard__message.is-option:last-of-type { + margin-bottom: 8px; + } } &__message { @@ -94,6 +108,11 @@ align-items: center; max-width: 255px; + &:not( .is-option ) { + margin-top: 8px; + margin-bottom: 8px; + } + .assistant-wizard__message-icon { flex-shrink: 0; align-self: baseline; @@ -122,6 +141,10 @@ display: none; } } + + &.is-option { + align-self: flex-start; + } } &__input-container { diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/wizard-messages.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/wizard-messages.tsx index cbfbdd0f2e91d..a8f205478a7c4 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/wizard-messages.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/wizard-messages.tsx @@ -69,6 +69,7 @@ export const MessageBubble = ( { message, onSelect = ( m: Message ) => m } ) =>
From 4f69df25b286d8198486c803c42a03a76d8c78d5 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini <26530524+zaguiini@users.noreply.github.com> Date: Thu, 6 Feb 2025 18:45:15 -0300 Subject: [PATCH 14/22] Post actions: Rename "Copy" to "Duplicate" (#34844) * Post actions: rename Copy action to Duplicate * Add CHANGELOG entry * Fix syntax issue added during merge conflict resolution --------- Co-authored-by: Brandon Kraft --- .../jetpack/changelog/enhance-rename-copy-post-duplicate | 4 ++++ projects/plugins/jetpack/modules/copy-post.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/enhance-rename-copy-post-duplicate diff --git a/projects/plugins/jetpack/changelog/enhance-rename-copy-post-duplicate b/projects/plugins/jetpack/changelog/enhance-rename-copy-post-duplicate new file mode 100644 index 0000000000000..bdf2dcf1ee879 --- /dev/null +++ b/projects/plugins/jetpack/changelog/enhance-rename-copy-post-duplicate @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Post actions: rename Copy action to Duplicate, which is clearer diff --git a/projects/plugins/jetpack/modules/copy-post.php b/projects/plugins/jetpack/modules/copy-post.php index 38afc654a5a24..88f3e9c26f9a9 100644 --- a/projects/plugins/jetpack/modules/copy-post.php +++ b/projects/plugins/jetpack/modules/copy-post.php @@ -344,8 +344,8 @@ public function add_row_action( $actions, $post ) { 'jetpack-copy' => sprintf( '%3$s %4$s', esc_url( $edit_url ), - esc_attr__( 'Copy this post with Jetpack', 'jetpack' ), - esc_html__( 'Copy', 'jetpack' ), + esc_attr__( 'Duplicate this post with Jetpack.', 'jetpack' ), + esc_html__( 'Duplicate', 'jetpack' ), $jetpack_logo->get_jp_emblem() ), ); From 0d94cb4d28b583c57e3be2660c50e2aee5ddb4cd Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Fri, 7 Feb 2025 09:12:04 +0700 Subject: [PATCH 15/22] External Media: Add Import button in Media Library (#41544) --- .../feat-external-media-import-button | 4 +++ .../admin/external-media-import-button.js | 23 +++++++++++++++ .../admin/external-media-import-button.scss | 12 ++++++++ .../features/admin/external-media-import.php | 29 +++++++++++++++++++ .../packages/external-media/webpack.config.js | 4 +++ 5 files changed, 72 insertions(+) create mode 100644 projects/packages/external-media/changelog/feat-external-media-import-button create mode 100644 projects/packages/external-media/src/features/admin/external-media-import-button.js create mode 100644 projects/packages/external-media/src/features/admin/external-media-import-button.scss diff --git a/projects/packages/external-media/changelog/feat-external-media-import-button b/projects/packages/external-media/changelog/feat-external-media-import-button new file mode 100644 index 0000000000000..628b477a5f482 --- /dev/null +++ b/projects/packages/external-media/changelog/feat-external-media-import-button @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +External Media: Add Import button in Media Library diff --git a/projects/packages/external-media/src/features/admin/external-media-import-button.js b/projects/packages/external-media/src/features/admin/external-media-import-button.js new file mode 100644 index 0000000000000..c852b3c9ea88c --- /dev/null +++ b/projects/packages/external-media/src/features/admin/external-media-import-button.js @@ -0,0 +1,23 @@ +import { __ } from '@wordpress/i18n'; + +document.addEventListener( 'DOMContentLoaded', function () { + const addNewButton = document.querySelector( 'a.page-title-action' ); + if ( addNewButton ) { + const buttonContainer = document.createElement( 'div' ); + buttonContainer.className = 'wpcom-media-library-action-buttons'; + + const importButton = document.createElement( 'a' ); + importButton.className = 'button'; + importButton.role = 'button'; + importButton.innerHTML = __( 'Import Media', 'jetpack-external-media' ); + importButton.href = window.JETPACK_EXTERNAL_MEDIA_IMPORT_BUTTON?.href; + + const parentNode = addNewButton.parentNode; + const nextSibling = addNewButton.nextSibling; + + buttonContainer.appendChild( addNewButton ); + buttonContainer.appendChild( importButton ); + + parentNode.insertBefore( buttonContainer, nextSibling ); + } +} ); diff --git a/projects/packages/external-media/src/features/admin/external-media-import-button.scss b/projects/packages/external-media/src/features/admin/external-media-import-button.scss new file mode 100644 index 0000000000000..df9db337f0ab9 --- /dev/null +++ b/projects/packages/external-media/src/features/admin/external-media-import-button.scss @@ -0,0 +1,12 @@ +.wpcom-media-library-action-buttons { + display: inline-flex; + flex-wrap: wrap; + gap: 4px; + + > a { + position: relative; + top: -3px; + margin-left: 0; + vertical-align: baseline; + } +} diff --git a/projects/packages/external-media/src/features/admin/external-media-import.php b/projects/packages/external-media/src/features/admin/external-media-import.php index c5a43769b7f09..972f8b7b9c764 100644 --- a/projects/packages/external-media/src/features/admin/external-media-import.php +++ b/projects/packages/external-media/src/features/admin/external-media-import.php @@ -47,10 +47,39 @@ function add_jetpack_external_media_import_page() { __NAMESPACE__ . '\render_jetpack_external_media_import_page' ); + add_action( 'load-upload.php', __NAMESPACE__ . '\enqueue_jetpack_external_media_import_button' ); add_action( "load-$external_media_import_page_hook", __NAMESPACE__ . '\enqueue_jetpack_external_media_import_page' ); } add_action( 'admin_menu', __NAMESPACE__ . '\add_jetpack_external_media_import_page' ); +/** + * Enqueue the assets of the Jetpack external media import button. + */ +function enqueue_jetpack_external_media_import_button() { + $assets_base_path = 'build/'; + $asset_name = 'jetpack-external-media-import-button'; + + Assets::register_script( + $asset_name, + $assets_base_path . "$asset_name/$asset_name.js", + External_Media::BASE_FILE, + array( + 'in_footer' => true, + 'textdomain' => 'jetpack-external-media', + 'css_path' => $assets_base_path . "$asset_name/$asset_name.css", + ) + ); + + Assets::enqueue_script( $asset_name ); + wp_localize_script( + $asset_name, + 'JETPACK_EXTERNAL_MEDIA_IMPORT_BUTTON', + array( + 'href' => admin_url( 'upload.php?page=jetpack_external_media_import_page&untangling-media=true' ), + ) + ); +} + /** * Enqueue the assets of the Jetpack external media page. */ diff --git a/projects/packages/external-media/webpack.config.js b/projects/packages/external-media/webpack.config.js index abfb18bdd1654..992136af6494e 100644 --- a/projects/packages/external-media/webpack.config.js +++ b/projects/packages/external-media/webpack.config.js @@ -5,6 +5,10 @@ module.exports = [ { entry: { 'jetpack-external-media-editor': './src/features/editor/index.js', + 'jetpack-external-media-import-button': [ + './src/features/admin/external-media-import-button.js', + './src/features/admin/external-media-import-button.scss', + ], 'jetpack-external-media-import-page': './src/features/admin/external-media-import.js', }, mode: jetpackWebpackConfig.mode, From 048f9fb4379db1b33c22f2dc79bf1eab01f9be73 Mon Sep 17 00:00:00 2001 From: arthur791004 Date: Fri, 7 Feb 2025 10:46:46 +0800 Subject: [PATCH 16/22] External Media: Add track events to the Import page and modal (#41592) * External Media: Add track event to Import now button * External Media: Add track event to Import media button * changelog --- .../feat-external-media-track-events | 4 + .../features/admin/external-media-import.js | 7 +- .../src/shared/media-browser/index.js | 80 ++++++++++--------- .../media-browser-select-button.js | 24 ++++++ .../shared/media-browser/use-page-source.js | 18 +++++ .../google-photos/google-photos-media.js | 2 + .../shared/sources/jetpack-app-media/index.js | 1 + .../src/shared/sources/openverse/index.js | 1 + .../src/shared/sources/pexels/index.js | 1 + 9 files changed, 100 insertions(+), 38 deletions(-) create mode 100644 projects/packages/external-media/changelog/feat-external-media-track-events create mode 100644 projects/packages/external-media/src/shared/media-browser/media-browser-select-button.js create mode 100644 projects/packages/external-media/src/shared/media-browser/use-page-source.js diff --git a/projects/packages/external-media/changelog/feat-external-media-track-events b/projects/packages/external-media/changelog/feat-external-media-track-events new file mode 100644 index 0000000000000..9c86bd2e0a665 --- /dev/null +++ b/projects/packages/external-media/changelog/feat-external-media-track-events @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +External Media: Add track events to the Import page and modal diff --git a/projects/packages/external-media/src/features/admin/external-media-import.js b/projects/packages/external-media/src/features/admin/external-media-import.js index d6feba02f7716..f1abfe45f3722 100644 --- a/projects/packages/external-media/src/features/admin/external-media-import.js +++ b/projects/packages/external-media/src/features/admin/external-media-import.js @@ -1,3 +1,4 @@ +import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; import { sprintf, __ } from '@wordpress/i18n'; import { useState, useEffect } from 'react'; import { createPortal } from 'react-dom'; @@ -22,6 +23,7 @@ const Notice = ( { message, onDismiss } ) => ( const JetpackExternalMediaImport = () => { const [ selectedSource, setSelectedSource ] = useState( null ); const [ noticeMessage, setNoticeMessage ] = useState( '' ); + const { tracks } = useAnalytics(); const ExternalLibrary = getExternalLibrary( selectedSource ); const selectButtonText = ( selectedImages, isCopying ) => { @@ -77,6 +79,9 @@ const JetpackExternalMediaImport = () => { const slug = event.target.dataset.slug; if ( slug ) { setSelectedSource( slug ); + tracks.recordEvent( 'jetpack_external_media_import_media_page_import_click', { + media_source: slug, + } ); } }; @@ -89,7 +94,7 @@ const JetpackExternalMediaImport = () => { element.removeEventListener( 'click', handleClick ); } }; - }, [] ); + }, [ tracks ] ); return ( <> diff --git a/projects/packages/external-media/src/shared/media-browser/index.js b/projects/packages/external-media/src/shared/media-browser/index.js index e9bd6bd4551aa..3365e500f6a1c 100644 --- a/projects/packages/external-media/src/shared/media-browser/index.js +++ b/projects/packages/external-media/src/shared/media-browser/index.js @@ -1,9 +1,12 @@ -import { Button, Spinner, Composite } from '@wordpress/components'; +import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; +import { Spinner, Composite } from '@wordpress/components'; import { useCallback, useState, useRef, useEffect } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import clsx from 'clsx'; import React from 'react'; +import MediaBrowserSelectButton from './media-browser-select-button'; import MediaItem from './media-item'; +import usePageSource from './use-page-source'; import './style.scss'; const MAX_SELECTED = 10; @@ -13,6 +16,7 @@ const MAX_SELECTED = 10; * * @param {object} props - The component props * @param {object[]} props.media - The list of media + * @param {string} props.mediaSource - The source of media * @param {boolean} props.isCopying - Whether the media browser is copying the media * @param {boolean} props.isLoading - Whether the media browser is loading * @param {boolean} props.imageOnly - Whether to skip non-media items @@ -22,12 +26,13 @@ const MAX_SELECTED = 10; * @param {Function} props.setPath - To set the path for the folder item * @param {Function} props.nextPage - To get the next path * @param {Function} props.onCopy - To handle the copy - * @param {string} props.selectButtonText - The text of the selection button + * @param {Function} props.selectButtonText - To get the select button text * @param {boolean} props.shouldProxyImg - Whether to use the proxy for the media URL * @return {React.ReactElement} - JSX element */ function MediaBrowser( { media, + mediaSource, isCopying, isLoading, imageOnly, @@ -42,6 +47,8 @@ function MediaBrowser( { } ) { const [ selected, setSelected ] = useState( [] ); const gridEl = useRef( null ); + const { tracks } = useAnalytics(); + const pageSource = usePageSource(); const select = useCallback( newlySelected => { @@ -65,45 +72,29 @@ function MediaBrowser( { ); const onCopyAndInsert = useCallback( () => { + tracks.recordEvent( 'jetpack_external_media_modal_cta_click', { + page_source: pageSource, + media_source: mediaSource, + media_count: selected.length, + multiple: !! multiple, + } ); + onCopy( selected ); - }, [ selected, onCopy ] ); + }, [ tracks, pageSource, mediaSource, selected, multiple, onCopy ] ); const hasMediaItems = media.filter( item => item.type !== 'folder' ).length > 0; - const classes = clsx( { - 'jetpack-external-media-browser__media': true, - 'jetpack-external-media-browser__media__loading': isLoading, - } ); - const wrapper = clsx( { - 'jetpack-external-media-browser': true, - [ className ]: true, - } ); - - // Using _event to avoid eslint errors. Can change to event if it's in use again. - const handleMediaItemClick = ( _event, { item } ) => { - select( item ); - }; - const SelectButton = selectProps => { - const disabled = selected.length === 0 || isCopying; + const getSelectButtonLabel = () => { const defaultLabel = isCopying ? __( 'Inserting…', 'jetpack-external-media' ) : __( 'Select', 'jetpack-external-media', /* dummy arg to avoid bad minification */ 0 ); - const label = selectProps?.labelText - ? selectProps?.labelText( selected.length, isCopying ) - : defaultLabel; - - return ( -
- -
- ); + + return selectButtonText ? selectButtonText( selected.length, isCopying ) : defaultLabel; + }; + + // Using _event to avoid eslint errors. Can change to event if it's in use again. + const handleMediaItemClick = ( _event, { item } ) => { + select( item ); }; // Infinite scroll @@ -126,11 +117,19 @@ function MediaBrowser( { }, [ pageHandle, isLoading, gridEl ] ); // eslint-disable-line react-hooks/exhaustive-deps return ( -
+
} > @@ -161,7 +160,14 @@ function MediaBrowser( {
) } - { hasMediaItems && } + { hasMediaItems && ( + + ) }
); } diff --git a/projects/packages/external-media/src/shared/media-browser/media-browser-select-button.js b/projects/packages/external-media/src/shared/media-browser/media-browser-select-button.js new file mode 100644 index 0000000000000..a612a7daef41a --- /dev/null +++ b/projects/packages/external-media/src/shared/media-browser/media-browser-select-button.js @@ -0,0 +1,24 @@ +import { Button } from '@wordpress/components'; +import React from 'react'; + +/** + * MediaBrowserSelectButton component + * + * @param {object} props - The component props + * @param {string} props.label - The label of the button + * @param {boolean} props.isLoading - Whether the button is loading + * @param {boolean} props.disabled - Whether the button is disabled + * @param {Function} props.onClick - To handle the click + * @return {React.ReactElement} - JSX element + */ +const MediaBrowserSelectButton = ( { label, isLoading, disabled, onClick } ) => { + return ( +
+ +
+ ); +}; + +export default MediaBrowserSelectButton; diff --git a/projects/packages/external-media/src/shared/media-browser/use-page-source.js b/projects/packages/external-media/src/shared/media-browser/use-page-source.js new file mode 100644 index 0000000000000..465bf48293ee4 --- /dev/null +++ b/projects/packages/external-media/src/shared/media-browser/use-page-source.js @@ -0,0 +1,18 @@ +import { useSelect } from '@wordpress/data'; + +const usePageSource = () => { + const isSiteEditor = useSelect( select => !! select( 'core/edit-site' ), [] ); + const postType = useSelect( select => select( 'core/editor' )?.getCurrentPostType(), [] ); + + if ( ! postType ) { + return 'jetpack-external-media-import-page'; + } + + if ( isSiteEditor ) { + return 'site-editor'; + } + + return postType === 'page' ? 'page-editor' : 'post-editor'; +}; + +export default usePageSource; diff --git a/projects/packages/external-media/src/shared/sources/google-photos/google-photos-media.js b/projects/packages/external-media/src/shared/sources/google-photos/google-photos-media.js index e334c03a156e7..5fcb598812966 100644 --- a/projects/packages/external-media/src/shared/sources/google-photos/google-photos-media.js +++ b/projects/packages/external-media/src/shared/sources/google-photos/google-photos-media.js @@ -11,6 +11,7 @@ import { DATE_RANGE_ANY, } from '../../constants'; import MediaBrowser from '../../media-browser'; +import { MediaSource } from '../../media-service/types'; import { getExternalMediaApiUrl } from '../api'; import Breadcrumbs from './breadcrumbs'; import GoogleFilterOption from './filter-option'; @@ -183,6 +184,7 @@ function GooglePhotosMedia( props ) { className="jetpack-external-media-browser__google" key={ listUrl } media={ media } + mediaSource={ MediaSource.GooglePhotos } imageOnly={ imageOnly } isCopying={ isCopying } isLoading={ isLoading } diff --git a/projects/packages/external-media/src/shared/sources/jetpack-app-media/index.js b/projects/packages/external-media/src/shared/sources/jetpack-app-media/index.js index a4a2cd0643639..640b38de3781b 100644 --- a/projects/packages/external-media/src/shared/sources/jetpack-app-media/index.js +++ b/projects/packages/external-media/src/shared/sources/jetpack-app-media/index.js @@ -136,6 +136,7 @@ function JetpackAppMedia( props ) { key={ 'jetpack-app-media' } className="jetpack-external-media-browser__jetpack_app_media_browser" media={ media } + mediaSource={ MediaSource.JetpackAppMedia } isCopying={ isCopying } isLoading={ false } nextPage={ getNextPage } diff --git a/projects/packages/external-media/src/shared/sources/openverse/index.js b/projects/packages/external-media/src/shared/sources/openverse/index.js index 331c648b817e3..86675f7cc59b0 100644 --- a/projects/packages/external-media/src/shared/sources/openverse/index.js +++ b/projects/packages/external-media/src/shared/sources/openverse/index.js @@ -67,6 +67,7 @@ function OpenverseMedia( props ) { getNextPage( searchQuery ) } diff --git a/projects/packages/external-media/src/shared/sources/pexels/index.js b/projects/packages/external-media/src/shared/sources/pexels/index.js index 83b142ed04fcf..23c162874afe5 100644 --- a/projects/packages/external-media/src/shared/sources/pexels/index.js +++ b/projects/packages/external-media/src/shared/sources/pexels/index.js @@ -68,6 +68,7 @@ function PexelsMedia( props ) { getNextPage( searchQuery ) } From 0831a4208eb5ff7279fa105ce1edec7f4ad8f165 Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Fri, 7 Feb 2025 13:32:59 +0700 Subject: [PATCH 17/22] Media Library: add track events for upload from URL feature (#41620) --- .../packages/external-media/changelog/media-tracks | 4 ++++ .../src/shared/media-browser/index.js | 4 ++-- .../src/shared/media-browser/use-page-source.js | 14 ++++---------- .../jetpack-mu-wpcom/changelog/media-tracks | 4 ++++ .../wpcom-media-url-upload-form/index.jsx | 9 +++++++-- .../wpcom-media/wpcom-media-url-upload.php | 8 ++++---- 6 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 projects/packages/external-media/changelog/media-tracks create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/media-tracks diff --git a/projects/packages/external-media/changelog/media-tracks b/projects/packages/external-media/changelog/media-tracks new file mode 100644 index 0000000000000..48205a668b670 --- /dev/null +++ b/projects/packages/external-media/changelog/media-tracks @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Media Library: add track events for upload from URL feature diff --git a/projects/packages/external-media/src/shared/media-browser/index.js b/projects/packages/external-media/src/shared/media-browser/index.js index 3365e500f6a1c..7ba3619044401 100644 --- a/projects/packages/external-media/src/shared/media-browser/index.js +++ b/projects/packages/external-media/src/shared/media-browser/index.js @@ -72,8 +72,8 @@ function MediaBrowser( { ); const onCopyAndInsert = useCallback( () => { - tracks.recordEvent( 'jetpack_external_media_modal_cta_click', { - page_source: pageSource, + tracks.recordEvent( 'jetpack_external_media_modal_submit', { + page: pageSource, media_source: mediaSource, media_count: selected.length, multiple: !! multiple, diff --git a/projects/packages/external-media/src/shared/media-browser/use-page-source.js b/projects/packages/external-media/src/shared/media-browser/use-page-source.js index 465bf48293ee4..70cfa13b6e7e6 100644 --- a/projects/packages/external-media/src/shared/media-browser/use-page-source.js +++ b/projects/packages/external-media/src/shared/media-browser/use-page-source.js @@ -1,18 +1,12 @@ import { useSelect } from '@wordpress/data'; const usePageSource = () => { - const isSiteEditor = useSelect( select => !! select( 'core/edit-site' ), [] ); - const postType = useSelect( select => select( 'core/editor' )?.getCurrentPostType(), [] ); + const isEditor = useSelect( select => !! select( 'core/editor' ), [] ); - if ( ! postType ) { - return 'jetpack-external-media-import-page'; + if ( isEditor ) { + return 'editor'; } - - if ( isSiteEditor ) { - return 'site-editor'; - } - - return postType === 'page' ? 'page-editor' : 'post-editor'; + return 'media-library'; }; export default usePageSource; diff --git a/projects/packages/jetpack-mu-wpcom/changelog/media-tracks b/projects/packages/jetpack-mu-wpcom/changelog/media-tracks new file mode 100644 index 0000000000000..48205a668b670 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/media-tracks @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Media Library: add track events for upload from URL feature diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-media/wpcom-media-url-upload-form/index.jsx b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-media/wpcom-media-url-upload-form/index.jsx index ab965659d96e3..bc6d37c441b79 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-media/wpcom-media-url-upload-form/index.jsx +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-media/wpcom-media-url-upload-form/index.jsx @@ -1,10 +1,11 @@ import { __ } from '@wordpress/i18n'; import clsx from 'clsx'; import { useState } from 'react'; +import { wpcomTrackEvent } from '../../../common/tracks'; import './style.scss'; -const WpcomMediaUrlUploadForm = ( { ajaxUrl, action, nonce, isEditor } ) => { +const WpcomMediaUrlUploadForm = ( { ajaxUrl, action, nonce, page } ) => { const [ url, setUrl ] = useState( '' ); const [ show, setShow ] = useState( false ); @@ -25,6 +26,10 @@ const WpcomMediaUrlUploadForm = ( { ajaxUrl, action, nonce, isEditor } ) => { } e.preventDefault(); + wpcomTrackEvent( 'wpcom_media_upload_from_url_submit', { + page, + } ); + const formData = new FormData(); formData.append( 'action', action ); formData.append( 'url', url ); @@ -48,7 +53,7 @@ const WpcomMediaUrlUploadForm = ( { ajaxUrl, action, nonce, isEditor } ) => { .collection.add( attachmentToAdd ); }; - if ( isEditor ) { + if ( page === 'editor' ) { const mediaLibraryTab = window.wp.media.frame.state( 'library' ); mediaLibraryTab.trigger( 'open' ); diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-media/wpcom-media-url-upload.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-media/wpcom-media-url-upload.php index 82afc12ae90a3..930151c080d2e 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-media/wpcom-media-url-upload.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-media/wpcom-media-url-upload.php @@ -23,10 +23,10 @@ function wpcom_media_url_upload() { $data = wp_json_encode( array( - 'ajaxUrl' => admin_url( 'admin-ajax.php' ), - 'action' => 'wpcom_media_url_upload', - 'nonce' => wp_create_nonce( 'wpcom_media_url_upload' ), - 'isEditor' => $pagenow !== 'upload.php', + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'action' => 'wpcom_media_url_upload', + 'nonce' => wp_create_nonce( 'wpcom_media_url_upload' ), + 'page' => $pagenow === 'upload.php' ? 'media-library' : 'editor', ) ); From 60e22d6b826a4c80f81f2afcde94d013cec92e8d Mon Sep 17 00:00:00 2001 From: Juanma Rodriguez Escriche Date: Fri, 7 Feb 2025 09:00:15 +0100 Subject: [PATCH 18/22] Update/dont sync set object terms action for blacklisted taxonomies (#41597) * Don't send term relationships for full sync posts actions since they are not being processed * changelog * Fixed next-version typo * Remove test no longer valid * changelog --- ...ct-terms-action-for-blacklisted-taxonomies | 4 ++++ .../packages/sync/src/modules/class-posts.php | 23 ++++++++++++++++++- .../modules/class-woocommerce-hpos-orders.php | 2 +- ...ct-terms-action-for-blacklisted-taxonomies | 4 ++++ ...st_class.jetpack-sync-full-immediately.php | 18 --------------- 5 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 projects/packages/sync/changelog/update-dont-sync-set-object-terms-action-for-blacklisted-taxonomies create mode 100644 projects/plugins/jetpack/changelog/update-dont-sync-set-object-terms-action-for-blacklisted-taxonomies diff --git a/projects/packages/sync/changelog/update-dont-sync-set-object-terms-action-for-blacklisted-taxonomies b/projects/packages/sync/changelog/update-dont-sync-set-object-terms-action-for-blacklisted-taxonomies new file mode 100644 index 0000000000000..2b074cde382a9 --- /dev/null +++ b/projects/packages/sync/changelog/update-dont-sync-set-object-terms-action-for-blacklisted-taxonomies @@ -0,0 +1,4 @@ +Significance: minor +Type: deprecated + +Sync: Full sync for posts not sending term relationships diff --git a/projects/packages/sync/src/modules/class-posts.php b/projects/packages/sync/src/modules/class-posts.php index 54c07996855ba..6d20fafe732fa 100644 --- a/projects/packages/sync/src/modules/class-posts.php +++ b/projects/packages/sync/src/modules/class-posts.php @@ -228,7 +228,7 @@ public function init_before_send() { // Full sync. $sync_module = Modules::get_module( 'full-sync' ); if ( $sync_module instanceof Full_Sync_Immediately ) { - add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'add_term_relationships' ) ); + add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'build_full_sync_action_array' ) ); } else { add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'expand_posts_with_metadata_and_terms' ) ); } @@ -779,6 +779,25 @@ public function send_published( $post_ID, $post ) { } } + /** + * Build the full sync action object for Posts. + * + * @access public + * + * @param array $args An array with the posts and the previous end. + * + * @return array An array with the posts, postmeta and the previous end. + */ + public function build_full_sync_action_array( $args ) { + list( $filtered_posts, $previous_end ) = $args; + return array( + $filtered_posts['objects'], + $filtered_posts['meta'], + array(), // WPCOM does not process term relationships in full sync posts actions for a while now, let's skip them. + $previous_end, + ); + } + /** * Add term relationships to post objects within a hook before they are serialized and sent to the server. * This is used in Full Sync Immediately @@ -787,8 +806,10 @@ public function send_published( $post_ID, $post ) { * * @param array $args The hook parameters. * @return array $args The expanded hook parameters. + * @deprecated since $$next-version$$ */ public function add_term_relationships( $args ) { + _deprecated_function( __METHOD__, '$$next-version$$' ); list( $filtered_posts, $previous_interval_end ) = $args; return array( diff --git a/projects/packages/sync/src/modules/class-woocommerce-hpos-orders.php b/projects/packages/sync/src/modules/class-woocommerce-hpos-orders.php index 13a892dea24c2..2bc63511b98fa 100644 --- a/projects/packages/sync/src/modules/class-woocommerce-hpos-orders.php +++ b/projects/packages/sync/src/modules/class-woocommerce-hpos-orders.php @@ -231,7 +231,7 @@ public function get_objects_by_id( $object_type, $ids ) { * @deprecated since $$next-version$$ */ public function expand_order_objects( $args ) { - _deprecated_function( __METHOD__, 'next-version' ); + _deprecated_function( __METHOD__, '$$next-version$$' ); list( $order_ids, $previous_end ) = $args; return array( 'orders' => $this->get_objects_by_id( 'order', $order_ids ), diff --git a/projects/plugins/jetpack/changelog/update-dont-sync-set-object-terms-action-for-blacklisted-taxonomies b/projects/plugins/jetpack/changelog/update-dont-sync-set-object-terms-action-for-blacklisted-taxonomies new file mode 100644 index 0000000000000..76e66c9980175 --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-dont-sync-set-object-terms-action-for-blacklisted-taxonomies @@ -0,0 +1,4 @@ +Significance: minor +Type: other + +Sync: Full sync for posts not sending term relationships diff --git a/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-full-immediately.php b/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-full-immediately.php index 4a83a9127a939..93adbe246721a 100644 --- a/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-full-immediately.php +++ b/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-full-immediately.php @@ -679,24 +679,6 @@ public function test_full_sync_doesnt_sends_forbiden_private_or_public_post_meta $this->assertEquals( 'foo5', $this->server_replica_storage->get_metadata( 'post', $post_id, 'a_public_meta', true ) ); } - public function test_full_sync_sends_all_post_terms() { - $post_id = self::factory()->post->create(); - wp_set_object_terms( $post_id, 'tag', 'post_tag' ); - - $this->sender->do_sync(); - $terms = get_the_terms( $post_id, 'post_tag' ); - - $this->assertEqualsObject( $terms, $this->server_replica_storage->get_the_terms( $post_id, 'post_tag' ), 'Initial sync doesn\'t work' ); - // reset the storage, check value, and do full sync - storage should be set! - $this->server_replica_storage->reset(); - - $this->assertFalse( $this->server_replica_storage->get_the_terms( $post_id, 'post_tag' ), 'Not empty' ); - $this->full_sync->start(); - $this->sender->do_full_sync(); - - $this->assertEqualsObject( $terms, $this->server_replica_storage->get_the_terms( $post_id, 'post_tag' ), 'Full sync doesn\'t work' ); - } - public function test_full_sync_sends_all_comment_meta() { $post_id = self::factory()->post->create(); $comment_ids = self::factory()->comment->create_post_comments( $post_id ); From 71ea4c66fa785ad140dafbfbdf4c4492df7a3e15 Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Fri, 7 Feb 2025 16:18:12 +0700 Subject: [PATCH 19/22] Media Library: don't show storage info on Atomic upload.php's uploader (#41625) --- .../plugins/wpcomsh/changelog/wpcomsh-update-media-notice | 4 ++++ projects/plugins/wpcomsh/notices/storage-notices.php | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 projects/plugins/wpcomsh/changelog/wpcomsh-update-media-notice diff --git a/projects/plugins/wpcomsh/changelog/wpcomsh-update-media-notice b/projects/plugins/wpcomsh/changelog/wpcomsh-update-media-notice new file mode 100644 index 0000000000000..69cc7c5cb8c32 --- /dev/null +++ b/projects/plugins/wpcomsh/changelog/wpcomsh-update-media-notice @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Media Library: don't show storage info on Atomic upload.php's uploader diff --git a/projects/plugins/wpcomsh/notices/storage-notices.php b/projects/plugins/wpcomsh/notices/storage-notices.php index 8ceeb3297c3b3..09ee42a365989 100644 --- a/projects/plugins/wpcomsh/notices/storage-notices.php +++ b/projects/plugins/wpcomsh/notices/storage-notices.php @@ -59,9 +59,15 @@ function wpcomsh_storage_notices() { add_action( 'admin_notices', 'wpcomsh_storage_notices' ); /** - * Display disk space usage on /wp-admin/upload.php + * Display disk space usage on the uploader */ function wpcomsh_display_disk_space_usage() { + global $pagenow; + + if ( $pagenow === 'upload.php' ) { + return; + } + $site_info = wpcomsh_get_at_site_info(); if ( empty( $site_info['space_used'] ) || empty( $site_info['space_quota'] ) ) { From 00c4981cef6d782a3e9b67d124966ade92318827 Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Fri, 7 Feb 2025 16:36:14 +0700 Subject: [PATCH 20/22] Media Library: add track event to the Import Media button (#41626) --- .../external-media/changelog/media-library-tracks-2 | 4 ++++ .../src/features/admin/external-media-import-button.js | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 projects/packages/external-media/changelog/media-library-tracks-2 diff --git a/projects/packages/external-media/changelog/media-library-tracks-2 b/projects/packages/external-media/changelog/media-library-tracks-2 new file mode 100644 index 0000000000000..5c6cbae94d559 --- /dev/null +++ b/projects/packages/external-media/changelog/media-library-tracks-2 @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Media Library: add track event to the Import Media button diff --git a/projects/packages/external-media/src/features/admin/external-media-import-button.js b/projects/packages/external-media/src/features/admin/external-media-import-button.js index c852b3c9ea88c..22145691a40f7 100644 --- a/projects/packages/external-media/src/features/admin/external-media-import-button.js +++ b/projects/packages/external-media/src/features/admin/external-media-import-button.js @@ -1,3 +1,4 @@ +import jetpackAnalytics from '@automattic/jetpack-analytics'; import { __ } from '@wordpress/i18n'; document.addEventListener( 'DOMContentLoaded', function () { @@ -11,6 +12,11 @@ document.addEventListener( 'DOMContentLoaded', function () { importButton.role = 'button'; importButton.innerHTML = __( 'Import Media', 'jetpack-external-media' ); importButton.href = window.JETPACK_EXTERNAL_MEDIA_IMPORT_BUTTON?.href; + importButton.onclick = function () { + jetpackAnalytics.tracks.recordEvent( 'jetpack_external_media_import_media_button_click', { + page: 'media-library', + } ); + }; const parentNode = addNewButton.parentNode; const nextSibling = addNewButton.nextSibling; From c7e6c1f572c83f1fad74e299a3cbeb9712df4c90 Mon Sep 17 00:00:00 2001 From: Kolja Zuelsdorf Date: Fri, 7 Feb 2025 11:43:05 +0100 Subject: [PATCH 21/22] Added a debug tool to send API requests to WPcom via the Jetpack connection. (#41154) * Added a debug tool to send API requests to WPcom via the Jetpack connection. This is to help with testing singular API requests that are in development and supposed to be called by Jetpack over the connection. * Added changelog. * Ensured that the connection classes actually exist (so that a helper can be set up) before using this helper. Moved module initialization to static method, so we don't have an empty object to construct. * Update phan baseline. * Update phan baseline. * Don't add extra IP for public API. * Extended request faker to allow different HTTP methods other than GET. --- .../plugins/debug-helper/.phan/baseline.php | 1 + .../debug-add-wpcom-api-request-faker | 4 + .../class-wpcom-api-request-faker-module.php | 185 ++++++++++++++++++ projects/plugins/debug-helper/plugin.php | 5 + 4 files changed, 195 insertions(+) create mode 100644 projects/plugins/debug-helper/changelog/debug-add-wpcom-api-request-faker create mode 100644 projects/plugins/debug-helper/modules/class-wpcom-api-request-faker-module.php diff --git a/projects/plugins/debug-helper/.phan/baseline.php b/projects/plugins/debug-helper/.phan/baseline.php index b7c093aa21e3b..724eaf018ac4c 100644 --- a/projects/plugins/debug-helper/.phan/baseline.php +++ b/projects/plugins/debug-helper/.phan/baseline.php @@ -45,6 +45,7 @@ 'modules/class-scan-helper.php' => ['PhanNoopNew', 'PhanSuspiciousValueComparison', 'PhanTypeConversionFromArray', 'PhanTypeMismatchReturnProbablyReal'], 'modules/class-sync-data-settings-tester.php' => ['PhanNoopNew', 'PhanTypePossiblyInvalidDimOffset', 'PhanUndeclaredClass'], 'modules/class-waf-helper.php' => ['PhanNoopNew', 'PhanPluginSimplifyExpressionBool', 'PhanTypeMismatchReturnProbablyReal', 'PhanUndeclaredClassConstant', 'PhanUndeclaredClassMethod'], + 'modules/class-wpcom-api-request-faker-module.php' => ['PhanUndeclaredClassMethod'], 'modules/class-wpcom-api-request-tracker-module.php' => ['PhanNoopNew', 'PhanTypeMismatchArgument'], 'modules/class-xmlrpc-blocker.php' => ['PhanNoopNew'], 'modules/class-xmlrpc-logger.php' => ['PhanNoopNew', 'PhanUndeclaredFunction'], diff --git a/projects/plugins/debug-helper/changelog/debug-add-wpcom-api-request-faker b/projects/plugins/debug-helper/changelog/debug-add-wpcom-api-request-faker new file mode 100644 index 0000000000000..6b85711b72655 --- /dev/null +++ b/projects/plugins/debug-helper/changelog/debug-add-wpcom-api-request-faker @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Debug Helper: Added WPcom API request sending functionality to help testing specific requests manually. diff --git a/projects/plugins/debug-helper/modules/class-wpcom-api-request-faker-module.php b/projects/plugins/debug-helper/modules/class-wpcom-api-request-faker-module.php new file mode 100644 index 0000000000000..b50bdf0e6d16f --- /dev/null +++ b/projects/plugins/debug-helper/modules/class-wpcom-api-request-faker-module.php @@ -0,0 +1,185 @@ +Error: This helper requires a jetpack connection to work. Please ensure that you have set one up before using.

'; + return; + } + + // Handle the form submit + if ( ! empty( $_POST ) ) { + if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ?? '' ) ), 'wpcom-api-request-faker' ) ) { + echo '

Wrong nonce, aborting.

'; + return; + } + + $is_connected = ( new Connection_Manager() )->is_connected(); + if ( ! $is_connected ) { + echo '

Site is not connected, please establish a jetpack connection first

'; + return; + } + + $api_url = '/' . sanitize_text_field( wp_unslash( $_POST['url'] ?? '' ) ); + $version = sanitize_text_field( wp_unslash( $_POST['version'] ?? '2' ) ); + $method = sanitize_text_field( wp_unslash( $_POST['method'] ?? 'get' ) ); + + $body = null; + if ( 'post' === $_POST['method'] || 'put' === $_POST['method'] ) { + $body = json_decode( sanitize_text_field( wp_unslash( $_POST['body'] ?? '' ) ), true ); + } + + $response = Client::wpcom_json_api_request_as_blog( + $api_url, + $version, + array( 'method' => sanitize_text_field( wp_unslash( $_POST['method'] ?? '2' ) ) ), + $body, + 'wpcom' + ); + + $response_code = wp_remote_retrieve_response_code( $response ); + + // Display error or response + if ( is_wp_error( $response ) || 200 !== $response_code || empty( $response['body'] ) ) { + ?> +

Something went wrong, here is the error (http code )

+ +
+ +

Response for

+ ' . esc_html( var_export( $looks_like_json ? json_decode( $body, true ) : $body, true ) ) . ''; + } + } + + ?> +
+ +

WPcom API Request Faker

+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ / + +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+ +
+
+
+
+ 'WPCOM API Request Tracker', 'description' => 'Displays the number of requests to WPCOM API endpoints for the current page request.', ), + 'wpcom-api-request-faker' => array( + 'file' => 'class-wpcom-api-request-faker-module.php', + 'name' => 'WPCOM API Request Faker', + 'description' => 'Send custom requests to the WPcom API, authorized via your Jetpack connection.', + ), 'xmlrpc-logger' => array( 'file' => 'class-xmlrpc-logger.php', 'name' => 'XMLRPC Logger', From 3c6ee841f63609aa260915ee3e496b0429cff2aa Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 7 Feb 2025 11:30:10 +0000 Subject: [PATCH 22/22] Fix: Warnings on any theme using customizer colors. (#41136) * Fix: Warnings on any theme using customizer colors. * changelog --- .../fix-warnings-on-any-theme-using-customizer-colors | 4 ++++ projects/plugins/wpcomsh/custom-colors/colors.php | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 projects/plugins/wpcomsh/changelog/fix-warnings-on-any-theme-using-customizer-colors diff --git a/projects/plugins/wpcomsh/changelog/fix-warnings-on-any-theme-using-customizer-colors b/projects/plugins/wpcomsh/changelog/fix-warnings-on-any-theme-using-customizer-colors new file mode 100644 index 0000000000000..da207bfd8be38 --- /dev/null +++ b/projects/plugins/wpcomsh/changelog/fix-warnings-on-any-theme-using-customizer-colors @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fix: Undefined array key warnings on any customizer theme with colors. diff --git a/projects/plugins/wpcomsh/custom-colors/colors.php b/projects/plugins/wpcomsh/custom-colors/colors.php index b2a4405482e8d..15f56c8ee244c 100644 --- a/projects/plugins/wpcomsh/custom-colors/colors.php +++ b/projects/plugins/wpcomsh/custom-colors/colors.php @@ -1421,8 +1421,12 @@ public static function override_themecolors() { $colors = $opts['colors']; - $colors['border'] = $colors['fg1']; - $colors['url'] = $colors['link']; + if ( isset( $colors['fg1'] ) ) { + $colors['border'] = $colors['fg1']; + } + if ( isset( $colors['link'] ) ) { + $colors['url'] = $colors['link']; + } if ( isset( $colors['txt'] ) ) { $colors['text'] = $colors['txt']; }