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/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/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/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/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..22145691a40f7
--- /dev/null
+++ b/projects/packages/external-media/src/features/admin/external-media-import-button.js
@@ -0,0 +1,29 @@
+import jetpackAnalytics from '@automattic/jetpack-analytics';
+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;
+ importButton.onclick = function () {
+ jetpackAnalytics.tracks.recordEvent( 'jetpack_external_media_import_media_button_click', {
+ page: 'media-library',
+ } );
+ };
+
+ 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.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/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/src/shared/media-browser/index.js b/projects/packages/external-media/src/shared/media-browser/index.js
index e9bd6bd4551aa..7ba3619044401 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_submit', {
+ page: 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..70cfa13b6e7e6
--- /dev/null
+++ b/projects/packages/external-media/src/shared/media-browser/use-page-source.js
@@ -0,0 +1,12 @@
+import { useSelect } from '@wordpress/data';
+
+const usePageSource = () => {
+ const isEditor = useSelect( select => !! select( 'core/editor' ), [] );
+
+ if ( isEditor ) {
+ return 'editor';
+ }
+ return 'media-library';
+};
+
+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 ) }
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,
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/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/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/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/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/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 '';
@@ -774,7 +773,7 @@ public function grunion_manage_post_column_response( $post ) {
echo '
' . esc_html( $content_fields['_feedback_ip'] ) . '
';
}
echo '
' . esc_html__( 'Source', 'jetpack-forms' ) . '
';
- echo '
';
+ echo '
';
echo '
';
}
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..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
@@ -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 .= " '',
+ '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/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/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();
+ },
+ } );
} );
} );
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',
)
);
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 ) )
),
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/boost/app/lib/minify/functions-service.php b/projects/plugins/boost/app/lib/minify/functions-service.php
index 798e986d6fad0..ccdf9e8513a06 100644
--- a/projects/plugins/boost/app/lib/minify/functions-service.php
+++ b/projects/plugins/boost/app/lib/minify/functions-service.php
@@ -68,17 +68,26 @@ function jetpack_boost_check_404_handler( $request_uri ) {
* This function is used to test if is_404() is working in wp-content/
* It sends a request to a non-existent URL, that will execute the 404 handler
* in jetpack_boost_check_404_handler().
+ * Define the constant JETPACK_BOOST_DISABLE_404_TESTER to disable this.
*
- * This function is called when the Minify_CSS or Minify_JS module is activated.
+ * This function is called when the Minify_CSS or Minify_JS module is activated, and once per day.
*/
function jetpack_boost_404_tester() {
+ if ( defined( 'JETPACK_BOOST_DISABLE_404_TESTER' ) && JETPACK_BOOST_DISABLE_404_TESTER ) {
+ return;
+ }
+
+ $minification_enabled = '';
wp_remote_get( home_url( '/wp-content/boost-cache/static/testing_404' ) );
if ( file_exists( Config::get_static_cache_dir_path() . '/404' ) ) {
wp_delete_file( Config::get_static_cache_dir_path() . '/404' );
- update_site_option( 'jetpack_boost_static_minification', 1 );
+ $minification_enabled = 1;
} else {
- update_site_option( 'jetpack_boost_static_minification', 0 );
+ $minification_enabled = 0;
}
+ update_site_option( 'jetpack_boost_static_minification', $minification_enabled );
+
+ return $minification_enabled;
}
add_action( 'jetpack_boost_404_tester_cron', 'jetpack_boost_404_tester' );
diff --git a/projects/plugins/boost/app/modules/optimizations/minify/class-minify-css.php b/projects/plugins/boost/app/modules/optimizations/minify/class-minify-css.php
index 2144a5072c269..efeec1c0ac79f 100644
--- a/projects/plugins/boost/app/modules/optimizations/minify/class-minify-css.php
+++ b/projects/plugins/boost/app/modules/optimizations/minify/class-minify-css.php
@@ -3,12 +3,13 @@
namespace Automattic\Jetpack_Boost\Modules\Optimizations\Minify;
use Automattic\Jetpack_Boost\Contracts\Changes_Page_Output;
+use Automattic\Jetpack_Boost\Contracts\Has_Activate;
use Automattic\Jetpack_Boost\Contracts\Has_Deactivate;
use Automattic\Jetpack_Boost\Contracts\Optimization;
use Automattic\Jetpack_Boost\Contracts\Pluggable;
use Automattic\Jetpack_Boost\Lib\Minify\Concatenate_CSS;
-class Minify_CSS implements Pluggable, Changes_Page_Output, Optimization, Has_Deactivate {
+class Minify_CSS implements Pluggable, Changes_Page_Output, Optimization, Has_Activate, Has_Deactivate {
public static $default_excludes = array( 'admin-bar', 'dashicons', 'elementor-app' );
@@ -49,6 +50,10 @@ public function init_minify() {
$wp_styles->allow_gzip_compression = true; // @todo - used constant ALLOW_GZIP_COMPRESSION = true if not defined.
}
+ public static function activate() {
+ jetpack_boost_404_tester();
+ }
+
public static function deactivate() {
jetpack_boost_page_optimize_cleanup_cache( 'css' );
}
diff --git a/projects/plugins/boost/app/modules/optimizations/minify/class-minify-js.php b/projects/plugins/boost/app/modules/optimizations/minify/class-minify-js.php
index 0a9bfb319a011..76998a6c4e824 100644
--- a/projects/plugins/boost/app/modules/optimizations/minify/class-minify-js.php
+++ b/projects/plugins/boost/app/modules/optimizations/minify/class-minify-js.php
@@ -3,12 +3,13 @@
namespace Automattic\Jetpack_Boost\Modules\Optimizations\Minify;
use Automattic\Jetpack_Boost\Contracts\Changes_Page_Output;
+use Automattic\Jetpack_Boost\Contracts\Has_Activate;
use Automattic\Jetpack_Boost\Contracts\Has_Deactivate;
use Automattic\Jetpack_Boost\Contracts\Optimization;
use Automattic\Jetpack_Boost\Contracts\Pluggable;
use Automattic\Jetpack_Boost\Lib\Minify\Concatenate_JS;
-class Minify_JS implements Pluggable, Changes_Page_Output, Optimization, Has_Deactivate {
+class Minify_JS implements Pluggable, Changes_Page_Output, Optimization, Has_Activate, Has_Deactivate {
public static $default_excludes = array( 'jquery', 'jquery-core', 'underscore', 'backbone' );
@@ -49,6 +50,10 @@ public function init_minify() {
$wp_scripts->allow_gzip_compression = true; // @todo - used constant ALLOW_GZIP_COMPRESSION = true if not defined.
}
+ public static function activate() {
+ jetpack_boost_404_tester();
+ }
+
public static function deactivate() {
jetpack_boost_page_optimize_cleanup_cache( 'js' );
}
diff --git a/projects/plugins/boost/changelog/fix-boost-compatibility-depay-woocommerce b/projects/plugins/boost/changelog/fix-boost-compatibility-depay-woocommerce
new file mode 100644
index 0000000000000..6044234689c32
--- /dev/null
+++ b/projects/plugins/boost/changelog/fix-boost-compatibility-depay-woocommerce
@@ -0,0 +1,4 @@
+Significance: patch
+Type: added
+
+Concatenate JS: Add compatibility with "Depay Payments for WooCommerce".
diff --git a/projects/plugins/boost/changelog/update-boost-404-test-activate b/projects/plugins/boost/changelog/update-boost-404-test-activate
new file mode 100644
index 0000000000000..535387eb3008f
--- /dev/null
+++ b/projects/plugins/boost/changelog/update-boost-404-test-activate
@@ -0,0 +1,5 @@
+Significance: patch
+Type: fixed
+Comment: Minification: run the 404 tester when activating either of the minification/concatenation modules.
+
+
diff --git a/projects/plugins/boost/compatibility/js-concatenate.php b/projects/plugins/boost/compatibility/js-concatenate.php
index b4aca14772b45..0e3157c94860c 100644
--- a/projects/plugins/boost/compatibility/js-concatenate.php
+++ b/projects/plugins/boost/compatibility/js-concatenate.php
@@ -12,6 +12,8 @@ function maybe_do_not_concat( $do_concat, $handle ) {
'tribe-tickets-provider',
// Plugin: `woocommerce-shipping`
'woocommerce-shipping-checkout-address-validation',
+ // Plugin: `depay-payments-for-woocommerce`
+ 'DEPAY_WC_WIDGETS',
);
if ( in_array( $handle, $excluded_handles, true ) ) {
diff --git a/projects/plugins/boost/tests/php/lib/minify/test-functions-service.php b/projects/plugins/boost/tests/php/lib/minify/test-functions-service.php
new file mode 100644
index 0000000000000..2cf2d253b4f5b
--- /dev/null
+++ b/projects/plugins/boost/tests/php/lib/minify/test-functions-service.php
@@ -0,0 +1,99 @@
+andReturn( true );
+
+ // Clean up any test files before each test
+ if ( file_exists( Config::get_static_cache_dir_path() . '/404' ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_exists
+ unlink( Config::get_static_cache_dir_path() . '/404' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
+ }
+ require_once __DIR__ . '/../../../../app/lib/minify/loader.php';
+ }
+
+ public function test_404_tester_when_404_file_exists() {
+ // Create mock 404 file
+ $cache_dir = Config::get_static_cache_dir_path();
+ if ( ! is_dir( $cache_dir ) ) {
+ mkdir( $cache_dir, 0775, true ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir
+ }
+ file_put_contents( $cache_dir . '/404', '1' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
+
+ Functions\expect( 'home_url' )
+ ->once()
+ ->with( '/wp-content/boost-cache/static/testing_404' )
+ ->andReturn( 'http://example.com/wp-content/boost-cache/static/testing_404' );
+
+ Functions\expect( 'wp_remote_get' )
+ ->once()
+ ->with( 'http://example.com/wp-content/boost-cache/static/testing_404' );
+
+ Functions\expect( 'wp_delete_file' )
+ ->once()
+ ->with( $cache_dir . '/404' );
+
+ Functions\expect( 'update_site_option' )
+ ->once()
+ ->with( 'jetpack_boost_static_minification', 1 );
+
+ $this->assertEquals( 1, jetpack_boost_404_tester() );
+ }
+
+ public function test_404_tester_when_404_file_does_not_exist() {
+ Functions\expect( 'home_url' )
+ ->once()
+ ->with( '/wp-content/boost-cache/static/testing_404' )
+ ->andReturn( 'http://example.com/wp-content/boost-cache/static/testing_404' );
+
+ Functions\expect( 'wp_remote_get' )
+ ->once()
+ ->with( 'http://example.com/wp-content/boost-cache/static/testing_404' );
+
+ Functions\expect( 'update_site_option' )
+ ->once()
+ ->with( 'jetpack_boost_static_minification', 0 );
+
+ $this->assertEquals( 0, jetpack_boost_404_tester() );
+ }
+
+ public function test_404_tester_disabled_by_constant() {
+ if ( ! defined( 'JETPACK_BOOST_DISABLE_404_TESTER' ) ) {
+ define( 'JETPACK_BOOST_DISABLE_404_TESTER', true );
+ }
+
+ Functions\expect( 'wp_remote_get' )->never();
+ Functions\expect( 'update_site_option' )->never();
+
+ $this->assertEquals( '', jetpack_boost_404_tester() );
+ }
+
+ protected function tear_down() {
+ parent::tear_down();
+
+ // Clean up any test files after each test
+ if ( file_exists( Config::get_static_cache_dir_path() . '/404' ) ) {
+ unlink( Config::get_static_cache_dir_path() . '/404' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
+ }
+
+ $path = '/tmp/wordpress/wp-content/boost-cache/static/';
+ while ( $path !== '/tmp' ) {
+ if ( is_dir( $path ) ) {
+ rmdir( $path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir
+ }
+ $path = dirname( $path );
+ }
+ }
+}
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 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',
diff --git a/projects/plugins/jetpack/changelog/add-sharing-safeguard-post b/projects/plugins/jetpack/changelog/add-sharing-safeguard-post
new file mode 100644
index 0000000000000..c8ba163b65325
--- /dev/null
+++ b/projects/plugins/jetpack/changelog/add-sharing-safeguard-post
@@ -0,0 +1,4 @@
+Significance: patch
+Type: bugfix
+
+Sharing: Fix possible warnings related to plugin compatibility.
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/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
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.
diff --git a/projects/plugins/jetpack/changelog/fix-invalid-field-ids b/projects/plugins/jetpack/changelog/fix-invalid-field-ids
new file mode 100644
index 0000000000000..b7c244efa48bf
--- /dev/null
+++ b/projects/plugins/jetpack/changelog/fix-invalid-field-ids
@@ -0,0 +1,4 @@
+Significance: patch
+Type: bugfix
+
+Forms: Fix invalid html IDs.
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/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/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 } ) =>
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()
),
);
diff --git a/projects/plugins/jetpack/modules/sharedaddy/sharing-service.php b/projects/plugins/jetpack/modules/sharedaddy/sharing-service.php
index db9a37dac1216..d726ec387adc3 100644
--- a/projects/plugins/jetpack/modules/sharedaddy/sharing-service.php
+++ b/projects/plugins/jetpack/modules/sharedaddy/sharing-service.php
@@ -960,7 +960,8 @@ function sharing_display( $text = '', $echo = false ) {
return $text;
}
- if ( empty( $post ) ) {
+ // We require the post to not be empty and be an actual WordPress post object. If it's not - we just return.
+ if ( empty( $post ) || ! $post instanceof \WP_Post ) {
return $text;
}
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 );
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/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/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'];
}
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'] ) ) {
diff --git a/tools/cli/helpers/doc-parser/src/class-doc-parser.php b/tools/cli/helpers/doc-parser/src/class-doc-parser.php
index f4014d0cdff8c..83d7002d6b79d 100644
--- a/tools/cli/helpers/doc-parser/src/class-doc-parser.php
+++ b/tools/cli/helpers/doc-parser/src/class-doc-parser.php
@@ -243,9 +243,7 @@ function ( Node $node ) {
if ( ! empty( $entry->text ) ) {
$block['doc']['description'] .=
- '
'
- . str_replace( array( "\r\n", "\n", "\r" ), '
', $entry->text )
- . '
';
+ str_replace( array( "\r\n", "\n", "\r" ), ' ', $entry->text );
}
}