From 6c06f5bd46f11caff775bd72c1853901c1c790e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Tue, 21 Feb 2023 13:55:40 -0300 Subject: [PATCH 01/38] Jetpack: log event when converting video block to the new VideoPress video block (#29071) * log v5 -> v6 manual conversion event * changelog --- .../changelog/update-jetpack-log-v5-v6-conversions | 4 ++++ .../v6-transform/components/transform-control/index.js | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 projects/plugins/jetpack/changelog/update-jetpack-log-v5-v6-conversions diff --git a/projects/plugins/jetpack/changelog/update-jetpack-log-v5-v6-conversions b/projects/plugins/jetpack/changelog/update-jetpack-log-v5-v6-conversions new file mode 100644 index 0000000000000..07e39aa86e936 --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-jetpack-log-v5-v6-conversions @@ -0,0 +1,4 @@ +Significance: minor +Type: major + +Jetpack: log event when converting video block to the new VideoPress video block diff --git a/projects/plugins/jetpack/extensions/blocks/videopress/v6-transform/components/transform-control/index.js b/projects/plugins/jetpack/extensions/blocks/videopress/v6-transform/components/transform-control/index.js index 56acd237494ed..c95b62c35a488 100644 --- a/projects/plugins/jetpack/extensions/blocks/videopress/v6-transform/components/transform-control/index.js +++ b/projects/plugins/jetpack/extensions/blocks/videopress/v6-transform/components/transform-control/index.js @@ -1,10 +1,12 @@ /** * WordPress dependencies */ +import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { createBlock } from '@wordpress/blocks'; import { Button, Notice } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; +import { store as editorStore } from '@wordpress/editor'; import { __ } from '@wordpress/i18n'; /** * External dependencies @@ -22,17 +24,23 @@ import styles from './styles.module.scss'; * @returns {ReactElement} - Transform panel component. */ export default function TransformControl() { + const postId = useSelect( select => select( editorStore ).getCurrentPostId() ); + const { getBlocks } = useSelect( blockEditorStore ); const { replaceBlock } = useDispatch( blockEditorStore ); + const { tracks } = useAnalytics(); const handleTransformAll = () => { const blocks = getBlocks(); blocks.forEach( block => { const { clientId, name, attributes } = block; - if ( name === 'core/video' && isVideoPressBlockBasedOnAttributes( attributes ) ) { replaceBlock( clientId, createBlock( 'videopress/video', attributes ) ); + + tracks.recordEvent( 'jetpack_editor_videopress_block_manual_conversion_click', { + post_id: postId, + } ); } } ); }; From 27e5706af62189442b41a35fb8c75bfdc259b3d1 Mon Sep 17 00:00:00 2001 From: Jasper Kang Date: Wed, 22 Feb 2023 11:27:06 +1300 Subject: [PATCH 02/38] Stats Admin: move new stats toggling to package (#29064) * [not verified] refactor toggling new stats logic to stats-admin * [not verified] changelog * [not verified] fix project versions --- .../update-move-new-stats-toggling-to-package | 4 +++ projects/packages/stats-admin/package.json | 2 +- .../packages/stats-admin/src/class-main.php | 33 ++++++++++++++++++- ...lass.jetpack-core-api-module-endpoints.php | 21 ++---------- .../update-move-new-stats-toggling-to-package | 4 +++ 5 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 projects/packages/stats-admin/changelog/update-move-new-stats-toggling-to-package create mode 100644 projects/plugins/jetpack/changelog/update-move-new-stats-toggling-to-package diff --git a/projects/packages/stats-admin/changelog/update-move-new-stats-toggling-to-package b/projects/packages/stats-admin/changelog/update-move-new-stats-toggling-to-package new file mode 100644 index 0000000000000..8e7b3a44b400b --- /dev/null +++ b/projects/packages/stats-admin/changelog/update-move-new-stats-toggling-to-package @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Stats: moved New Stats toggling logic to stats-admin diff --git a/projects/packages/stats-admin/package.json b/projects/packages/stats-admin/package.json index fcd4ba532af72..8fce8c4b3fb31 100644 --- a/projects/packages/stats-admin/package.json +++ b/projects/packages/stats-admin/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-stats-admin", - "version": "0.6.1", + "version": "0.6.2-alpha", "description": "Stats Dashboard", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/stats-admin/#readme", "bugs": { diff --git a/projects/packages/stats-admin/src/class-main.php b/projects/packages/stats-admin/src/class-main.php index 45443d0dadcdf..caecb32dff786 100644 --- a/projects/packages/stats-admin/src/class-main.php +++ b/projects/packages/stats-admin/src/class-main.php @@ -7,6 +7,10 @@ namespace Automattic\Jetpack\Stats_Admin; +use Automattic\Jetpack\Connection\Manager; +use Automattic\Jetpack\Stats\Options as Stats_Options; +use Automattic\Jetpack\Tracking; + /** * Stats Main class. * @@ -18,7 +22,7 @@ class Main { /** * Stats version. */ - const VERSION = '0.6.1'; + const VERSION = '0.6.2-alpha'; /** * Singleton Main instance. @@ -49,4 +53,31 @@ public static function init() { private function __construct() { add_action( 'rest_api_init', array( new REST_Controller(), 'register_rest_routes' ) ); } + + /** + * Update New Stats status. + * + * @param bool $status true to enable or false to disable. + * @return bool + */ + public static function update_new_stats_status( $status ) { + $status = (bool) $status; + + $stats_options = array( + 'enable_odyssey_stats' => $status, + 'odyssey_stats_changed_at' => time(), + ); + $updated = Stats_Options::set_options( $stats_options ); + + // Track the event. + $event_name = 'calypso_stats_disabled'; + if ( $status ) { + $event_name = 'calypso_stats_enabled'; + } + $connection_manager = new Manager( 'jetpack' ); + $tracking = new Tracking( 'jetpack', $connection_manager ); + $tracking->record_user_event( $event_name, array_merge( $stats_options, array( 'updated' => $updated ) ) ); + + return $updated; + } } diff --git a/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php b/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php index 83a1ee6216d15..ac3971610dcf5 100644 --- a/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php +++ b/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php @@ -5,14 +5,11 @@ * @package automattic/jetpack */ -use Automattic\Jetpack\Connection\Manager; use Automattic\Jetpack\Connection\REST_Connector; use Automattic\Jetpack\Plugins_Installer; -use Automattic\Jetpack\Stats\Options as Stats_Options; use Automattic\Jetpack\Stats\WPCOM_Stats; +use Automattic\Jetpack\Stats_Admin\Main as Stats_Admin_Main; use Automattic\Jetpack\Status; -use Automattic\Jetpack\Tracking; - /** * This is the base class for every Core API endpoint Jetpack uses. */ @@ -883,20 +880,8 @@ public function update_data( $request ) { break; case 'enable_odyssey_stats': - $value = (bool) $value; - $stats_options = array( - 'enable_odyssey_stats' => $value, - 'odyssey_stats_changed_at' => time(), - ); - $updated = Stats_Options::set_options( $stats_options ); - // Track the event. - $event_name = 'calypso_stats_disabled'; - if ( $value ) { - $event_name = 'calypso_stats_enabled'; - } - $connection_manager = new Manager( 'jetpack' ); - $tracking = new Tracking( 'jetpack', $connection_manager ); - $tracking->record_user_event( $event_name, array_merge( $stats_options, array( 'updated' => $updated ) ) ); + $updated = Stats_Admin_Main::update_new_stats_status( $value ); + break; case 'akismet_show_user_comments_approved': diff --git a/projects/plugins/jetpack/changelog/update-move-new-stats-toggling-to-package b/projects/plugins/jetpack/changelog/update-move-new-stats-toggling-to-package new file mode 100644 index 0000000000000..9ed2ed7417f99 --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-move-new-stats-toggling-to-package @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Stats: moved New Stats toggling logic to stats-admin From 5cee8654b022234188d779ae5f65b18fa8b72182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Tue, 21 Feb 2023 20:24:21 -0300 Subject: [PATCH 03/38] Jetpack: log when transform from video block v5 to v6 (#29076) * log when transform v5 to v6 * changelog --- .../changelog/update-jetpack-log-v5-v6-transform | 4 ++++ .../extensions/blocks/videopress/editor.js | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/update-jetpack-log-v5-v6-transform diff --git a/projects/plugins/jetpack/changelog/update-jetpack-log-v5-v6-transform b/projects/plugins/jetpack/changelog/update-jetpack-log-v5-v6-transform new file mode 100644 index 0000000000000..fb200dab31555 --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-jetpack-log-v5-v6-transform @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Jetpack: log when transforming from video block v5 to v6 diff --git a/projects/plugins/jetpack/extensions/blocks/videopress/editor.js b/projects/plugins/jetpack/extensions/blocks/videopress/editor.js index 24528a54ccc28..86753d6c26733 100644 --- a/projects/plugins/jetpack/extensions/blocks/videopress/editor.js +++ b/projects/plugins/jetpack/extensions/blocks/videopress/editor.js @@ -1,6 +1,7 @@ /** * External dependencies */ +import analytics from '@automattic/jetpack-analytics'; import { isAtomicSite, isSimpleSite, @@ -13,8 +14,8 @@ import { parse } from '@wordpress/block-serialization-default-parser'; import { createBlock, getBlockType } from '@wordpress/blocks'; import { Button } from '@wordpress/components'; import { createHigherOrderComponent } from '@wordpress/compose'; -import { useDispatch } from '@wordpress/data'; -import { mediaUpload } from '@wordpress/editor'; +import { useDispatch, select } from '@wordpress/data'; +import { mediaUpload, store as editorStore } from '@wordpress/editor'; import { useContext, useEffect } from '@wordpress/element'; import { addFilter } from '@wordpress/hooks'; import { __ } from '@wordpress/i18n'; @@ -447,7 +448,16 @@ function addVideoPressCoreVideoTransform( settings, name ) { const guidFromSrc = pickGUIDFromUrl( src ); return guid || guidFromSrc; }, - transform: attrs => createBlock( 'videopress/video', attrs ), + transform: attrs => { + const postId = select( editorStore ).getCurrentPostId(); + analytics?.tracks?.recordEvent( + 'jetpack_editor_videopress_block_manual_transform_click', + { + post_id: postId, + } + ); + return createBlock( 'videopress/video', attrs ); + }, }, ], to: [ From 72ef3482932e1e1b27f481090ec48a7d18ed2ee5 Mon Sep 17 00:00:00 2001 From: Jasper Kang Date: Wed, 22 Feb 2023 12:57:50 +1300 Subject: [PATCH 04/38] Stats Admin: opt-in notice 30 days after Odyssey is disabled (#29065) * Show Opt-in notice 30 days after the new stats being disabled * changelog * fix logic and change ut * fix project versions --- .../changelog/fix-opt-in-stats-notice-30days | 4 +++ .../stats-admin/src/class-notices.php | 27 ++++++++++++++----- .../tests/php/test-stats-notices.php | 1 + 3 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 projects/packages/stats-admin/changelog/fix-opt-in-stats-notice-30days diff --git a/projects/packages/stats-admin/changelog/fix-opt-in-stats-notice-30days b/projects/packages/stats-admin/changelog/fix-opt-in-stats-notice-30days new file mode 100644 index 0000000000000..fbc22a93b83f6 --- /dev/null +++ b/projects/packages/stats-admin/changelog/fix-opt-in-stats-notice-30days @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Stats Admin: Opt-in notice should be shown a month after the customer has opted out of Odyssey Stats. diff --git a/projects/packages/stats-admin/src/class-notices.php b/projects/packages/stats-admin/src/class-notices.php index bfbfac3b77a49..54f7da8ffdef9 100644 --- a/projects/packages/stats-admin/src/class-notices.php +++ b/projects/packages/stats-admin/src/class-notices.php @@ -22,8 +22,9 @@ class Notices { const NOTICE_STATUS_DISMISSED = 'dismissed'; const NOTICE_STATUS_POSTPONED = 'postponed'; - const VIEWS_TO_SHOW_FEEDBACK = 3; - const POSTPONE_FEEDBACK_DAYS = 30; + const VIEWS_TO_SHOW_FEEDBACK = 3; + const POSTPONE_FEEDBACK_DAYS = 30; + const POSTPONE_OPT_IN_NOTICE_DAYS = 30; /** * Update the notice status. @@ -53,13 +54,25 @@ public function update_notice( $id, $status, $postponed_for = 0 ) { * @return array */ public function get_notices_to_show() { - $new_stats_enabled = Stats_Options::get_option( 'enable_odyssey_stats' ); - $stats_views = $this->get_new_stats_views(); + $new_stats_enabled = Stats_Options::get_option( 'enable_odyssey_stats' ); + $stats_views = $this->get_new_stats_views(); + $odyssey_stats_changed_at = intval( Stats_Options::get_option( 'odyssey_stats_changed_at' ) ); return array( - self::OPT_IN_NEW_STATS_NOTICE_ID => ! $new_stats_enabled && ! $this->is_notice_hidden( self::OPT_IN_NEW_STATS_NOTICE_ID ), - self::NEW_STATS_FEEDBACK_NOTICE_ID => $new_stats_enabled && $stats_views >= self::VIEWS_TO_SHOW_FEEDBACK && ! $this->is_notice_hidden( self::NEW_STATS_FEEDBACK_NOTICE_ID ), - self::OPT_OUT_NEW_STATS_NOTICE_ID => $new_stats_enabled && $stats_views < self::VIEWS_TO_SHOW_FEEDBACK && ! $this->is_notice_hidden( self::OPT_OUT_NEW_STATS_NOTICE_ID ), + // Show Opt-in notice 30 days after the new stats being disabled. + self::OPT_IN_NEW_STATS_NOTICE_ID => ! $new_stats_enabled + && $odyssey_stats_changed_at < time() - self::POSTPONE_OPT_IN_NOTICE_DAYS * DAY_IN_SECONDS + && ! $this->is_notice_hidden( self::OPT_IN_NEW_STATS_NOTICE_ID ), + + // Show feedback notice after 3 views of the new stats. + self::NEW_STATS_FEEDBACK_NOTICE_ID => $new_stats_enabled + && $stats_views >= self::VIEWS_TO_SHOW_FEEDBACK + && ! $this->is_notice_hidden( self::NEW_STATS_FEEDBACK_NOTICE_ID ), + + // Show opt-out notice before 3 views of the new stats, where 3 is included. + self::OPT_OUT_NEW_STATS_NOTICE_ID => $new_stats_enabled + && $stats_views < self::VIEWS_TO_SHOW_FEEDBACK + && ! $this->is_notice_hidden( self::OPT_OUT_NEW_STATS_NOTICE_ID ), ); } diff --git a/projects/packages/stats-admin/tests/php/test-stats-notices.php b/projects/packages/stats-admin/tests/php/test-stats-notices.php index 8f268d6ade130..c3164602d9b64 100644 --- a/projects/packages/stats-admin/tests/php/test-stats-notices.php +++ b/projects/packages/stats-admin/tests/php/test-stats-notices.php @@ -101,6 +101,7 @@ public function test_new_stats_feedback_notice_postponed_show_again() { */ public function test_opt_in_new_stats_notice_show() { Stats_Options::set_option( 'enable_odyssey_stats', false ); + Stats_Options::set_option( 'odyssey_stats_changed_at', time() - 31 * DAY_IN_SECONDS ); $this->assertTrue( self::$notices->get_notices_to_show()['opt_in_new_stats'] ); } From 99bc0e977ef7453162ed1c8e328ffed2f25949cd Mon Sep 17 00:00:00 2001 From: thingalon Date: Wed, 22 Feb 2023 14:52:54 +1100 Subject: [PATCH 05/38] [Boost] Fix layout of the upgrade CTA (#29063) * Fix the layout of the upgrade CTA * changelog --------- Co-authored-by: Mark George --- .../boost/app/assets/src/css/components/critical-css.scss | 1 + projects/plugins/boost/changelog/boost-fix-cta | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 projects/plugins/boost/changelog/boost-fix-cta diff --git a/projects/plugins/boost/app/assets/src/css/components/critical-css.scss b/projects/plugins/boost/app/assets/src/css/components/critical-css.scss index 058f8f17e70bc..b02157b8a575d 100644 --- a/projects/plugins/boost/app/assets/src/css/components/critical-css.scss +++ b/projects/plugins/boost/app/assets/src/css/components/critical-css.scss @@ -177,6 +177,7 @@ border: 2px solid $jetpack-green; border-radius: $border-radius; background-color: #ffffff; + text-align: left; cursor: pointer; p { diff --git a/projects/plugins/boost/changelog/boost-fix-cta b/projects/plugins/boost/changelog/boost-fix-cta new file mode 100644 index 0000000000000..62721d1e8071c --- /dev/null +++ b/projects/plugins/boost/changelog/boost-fix-cta @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fix layout of the upgrade CTA From 68f8cb6960c70d70cfecd044a1fa0f43ef6469bf Mon Sep 17 00:00:00 2001 From: Jeremy Yip Date: Wed, 22 Feb 2023 00:10:42 -0800 Subject: [PATCH 06/38] Launchpad: Avoid rendering launchpad modal above post publish modal (#28989) --- ...o-avoid-rendering-above-post-publish-modal | 4 +++ .../plugins/launchpad-save-modal/index.js | 34 +++++++++++++++++-- .../launchpad-save-modal.php | 5 +-- 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/update-launchpad-save-modal-to-avoid-rendering-above-post-publish-modal diff --git a/projects/plugins/jetpack/changelog/update-launchpad-save-modal-to-avoid-rendering-above-post-publish-modal b/projects/plugins/jetpack/changelog/update-launchpad-save-modal-to-avoid-rendering-above-post-publish-modal new file mode 100644 index 0000000000000..f64f394e4939b --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-launchpad-save-modal-to-avoid-rendering-above-post-publish-modal @@ -0,0 +1,4 @@ +Significance: minor +Type: compat + +Prevent launchpad modal from rendering on top of the first post published modal diff --git a/projects/plugins/jetpack/extensions/plugins/launchpad-save-modal/index.js b/projects/plugins/jetpack/extensions/plugins/launchpad-save-modal/index.js index c538245e5c42b..6fc0f5e876686 100644 --- a/projects/plugins/jetpack/extensions/plugins/launchpad-save-modal/index.js +++ b/projects/plugins/jetpack/extensions/plugins/launchpad-save-modal/index.js @@ -4,7 +4,7 @@ import { Modal, Button, CheckboxControl } from '@wordpress/components'; import { usePrevious } from '@wordpress/compose'; import { useSelect } from '@wordpress/data'; import { store as editorStore } from '@wordpress/editor'; -import { useEffect, useState } from '@wordpress/element'; +import { useEffect, useRef, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import './editor.scss'; @@ -17,6 +17,7 @@ export const settings = { [] ); const isSavingPost = useSelect( select => select( editorStore ).isSavingPost(), [] ); + const isPublishingPost = useSelect( select => select( editorStore ).isPublishingPost(), [] ); const isCurrentPostPublished = useSelect( select => select( editorStore ).isCurrentPostPublished(), [] @@ -24,6 +25,7 @@ export const settings = { const prevIsSavingSite = usePrevious( isSavingSite ); const prevIsSavingPost = usePrevious( isSavingPost ); + const prevIsPublishingPost = usePrevious( isPublishingPost ); // We use this state as a flag to manually handle the modal close on first post publish const [ isInitialPostPublish, setIsInitialPostPublish ] = useState( false ); @@ -32,9 +34,11 @@ export const settings = { const [ dontShowAgain, setDontShowAgain ] = useState( false ); const [ isChecked, setIsChecked ] = useState( false ); - const { launchpadScreenOption, siteIntentOption } = window?.Jetpack_LaunchpadSaveModal || {}; + const { launchpadScreenOption, hasNeverPublishedPostOption, siteIntentOption } = + window?.Jetpack_LaunchpadSaveModal || {}; const isInsideSiteEditor = document.getElementById( 'site-editor' ) !== null; const isInsidePostEditor = document.querySelector( '.block-editor' ) !== null; + const prevHasNeverPublishedPostOption = useRef( hasNeverPublishedPostOption ); const siteFragment = getSiteFragment(); const launchPadUrl = getRedirectUrl( `wpcom-launchpad-setup-${ siteIntentOption }`, { @@ -52,13 +56,37 @@ export const settings = { } ); useEffect( () => { + // We want to prevent the launchpad modal from rendering on top of the first + // post published modal that exists in the editing toolkit. The following + // conditional is a stopgap solution for the time being, and the end goal is + // to migrate the first post published modal logic into jetpack, abstract code from + // both modals and their rendering behavior, and remove this solution afterwards. if ( + prevIsPublishingPost === true && + isPublishingPost === false && + prevHasNeverPublishedPostOption.current && + siteIntentOption === 'write' && + isInsidePostEditor + ) { + setIsModalOpen( false ); + prevHasNeverPublishedPostOption.current = ''; + return; + } else if ( ( prevIsSavingSite === true && isSavingSite === false ) || ( prevIsSavingPost === true && isSavingPost === false ) ) { setIsModalOpen( true ); } - }, [ isSavingSite, prevIsSavingSite, isSavingPost, prevIsSavingPost ] ); + }, [ + isSavingSite, + prevIsSavingSite, + isSavingPost, + prevIsSavingPost, + siteIntentOption, + isInsidePostEditor, + isPublishingPost, + prevIsPublishingPost, + ] ); useEffect( () => { // if the isCurrentPostPublished is ever false it means this current post hasn't been published yet so we set the initialPostPublish state diff --git a/projects/plugins/jetpack/extensions/plugins/launchpad-save-modal/launchpad-save-modal.php b/projects/plugins/jetpack/extensions/plugins/launchpad-save-modal/launchpad-save-modal.php index 16e00c7f5d9eb..18effeeb29044 100644 --- a/projects/plugins/jetpack/extensions/plugins/launchpad-save-modal/launchpad-save-modal.php +++ b/projects/plugins/jetpack/extensions/plugins/launchpad-save-modal/launchpad-save-modal.php @@ -22,8 +22,9 @@ function add_launchpad_options() { } $launchpad_options = array( - 'launchpadScreenOption' => get_option( 'launchpad_screen' ), - 'siteIntentOption' => get_option( 'site_intent' ), + 'launchpadScreenOption' => get_option( 'launchpad_screen' ), + 'siteIntentOption' => get_option( 'site_intent' ), + 'hasNeverPublishedPostOption' => get_option( 'has_never_published_post' ), ); wp_add_inline_script( 'jetpack-blocks-editor', From 317fb78670ec335866e36cb451bc4b4dd31b8181 Mon Sep 17 00:00:00 2001 From: Jeremy Herve Date: Wed, 22 Feb 2023 10:14:47 +0100 Subject: [PATCH 07/38] Sharing: add a Mastodon sharing button (#28694) Co-authored-by: Brandon Kraft Co-authored-by: Brad Jorsch --- .../changelog/add-mastodon-sharing-button | 4 + .../modules/sharedaddy/admin-sharing.css | 3 + .../modules/sharedaddy/admin-sharing.js | 7 +- .../services/class-jetpack-mastodon-modal.php | 195 ++++++++++++++++++ .../modules/sharedaddy/sharing-service.php | 1 + .../modules/sharedaddy/sharing-sources.php | 154 +++++++++++++- .../jetpack/modules/sharedaddy/sharing.css | 15 ++ 7 files changed, 375 insertions(+), 4 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/add-mastodon-sharing-button create mode 100644 projects/plugins/jetpack/modules/sharedaddy/services/class-jetpack-mastodon-modal.php diff --git a/projects/plugins/jetpack/changelog/add-mastodon-sharing-button b/projects/plugins/jetpack/changelog/add-mastodon-sharing-button new file mode 100644 index 0000000000000..02e16d39d5924 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-mastodon-sharing-button @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Sharing: add Mastodon sharing button. diff --git a/projects/plugins/jetpack/modules/sharedaddy/admin-sharing.css b/projects/plugins/jetpack/modules/sharedaddy/admin-sharing.css index 4ca7041d234c6..e4bf4044deda8 100644 --- a/projects/plugins/jetpack/modules/sharedaddy/admin-sharing.css +++ b/projects/plugins/jetpack/modules/sharedaddy/admin-sharing.css @@ -165,6 +165,9 @@ li.service.share-jetpack-whatsapp span:before { li.service.share-skype span:before { content: '\f220'; } +li.service.share-mastodon span:before { + content: '\f10a'; +} /** * Preview section diff --git a/projects/plugins/jetpack/modules/sharedaddy/admin-sharing.js b/projects/plugins/jetpack/modules/sharedaddy/admin-sharing.js index 5eff11fe910bd..6f72dc3f4ab2e 100644 --- a/projects/plugins/jetpack/modules/sharedaddy/admin-sharing.js +++ b/projects/plugins/jetpack/modules/sharedaddy/admin-sharing.js @@ -96,12 +96,12 @@ var button_style = $( '#button_style' ).val(); // Toggle .sd-social-official class - var sharedaddy = $('.sharedaddy'); + var sharedaddy = $( '.sharedaddy' ); var oficialClass = 'sd-social-official'; if ( 'official' === button_style ) { - sharedaddy.addClass(oficialClass); + sharedaddy.addClass( oficialClass ); } else { - sharedaddy.removeClass(oficialClass); + sharedaddy.removeClass( oficialClass ); } // Clear the live preview @@ -165,6 +165,7 @@ if ( ! $( this ).hasClass( 'preview-press-this' ) && ! $( this ).hasClass( 'preview-email' ) && + ! $( this ).hasClass( 'preview-mastodon' ) && ! $( this ).hasClass( 'preview-print' ) && ! $( this ).hasClass( 'preview-telegram' ) && ! $( this ).hasClass( 'preview-jetpack-whatsapp' ) && diff --git a/projects/plugins/jetpack/modules/sharedaddy/services/class-jetpack-mastodon-modal.php b/projects/plugins/jetpack/modules/sharedaddy/services/class-jetpack-mastodon-modal.php new file mode 100644 index 0000000000000..f0153de88bc90 --- /dev/null +++ b/projects/plugins/jetpack/modules/sharedaddy/services/class-jetpack-mastodon-modal.php @@ -0,0 +1,195 @@ + + + + + + + + + + +'; + + /** + * Decide whether the modal should be displayed. + * We want to show it when the user clicks on the Mastodon share button, + * thus loading a page with ?share=mastodon&nb=1. + * + * @return bool + */ + public static function should_display_modal() { + if ( + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Only used to display the modal. + isset( $_GET['share'] ) + && 'mastodon' === sanitize_text_field( wp_unslash( $_GET['share'] ) ) + && isset( $_GET['nb'] ) + && 1 === (int) $_GET['nb'] + // phpcs:enable + ) { + return true; + } + + return false; + } + + /** + * Hook the modal render into WordPress. + */ + public static function modal() { + if ( ! self::should_display_modal() ) { + return; + } + + // Render the modal. + self::render_modal(); + + die(); + } + + /** + * Render the modal. + */ + public static function render_modal() { ?> + +> + + + + + + + <?php esc_html_e( 'Share to Mastodon', 'jetpack' ); ?> + + + +
+

+ +
+ + +
+ + +
+
+
+ + + 'Share_Telegram', 'jetpack-whatsapp' => 'Jetpack_Share_WhatsApp', 'skype' => 'Share_Skype', + 'mastodon' => 'Share_Mastodon', ); if ( is_multisite() && is_plugin_active( 'press-this/press-this-plugin.php' ) ) { diff --git a/projects/plugins/jetpack/modules/sharedaddy/sharing-sources.php b/projects/plugins/jetpack/modules/sharedaddy/sharing-sources.php index c0887f053641f..0a26df02a9314 100644 --- a/projects/plugins/jetpack/modules/sharedaddy/sharing-sources.php +++ b/projects/plugins/jetpack/modules/sharedaddy/sharing-sources.php @@ -150,7 +150,47 @@ public function get_share_title( $post_id ) { */ $title = apply_filters( 'sharing_title', $post->post_title, $post_id, $this->id ); - return html_entity_decode( wp_kses( $title, null ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ); + return html_entity_decode( wp_kses( $title, '' ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ); + } + + /** + * Get a comma-separated list of the post's tags to use for sharing. + * Prepends a '#' to each tag. + * + * @param int $post_id Post ID. + * + * @return string + */ + public function get_share_tags( $post_id ) { + $tags = get_the_tags( $post_id ); + if ( ! $tags ) { + return ''; + } + + $tags = array_map( + function ( $tag ) { + // Camel case the tag name and remove spaces as well as apostrophes. + $tag = preg_replace( '/\s+|\'/', '', ucwords( $tag->name ) ); + + // Return with a '#' prepended. + return '#' . $tag; + }, + $tags + ); + + /** + * Allow customizing how the list of tags is displayed. + * + * @module sharedaddy + * @since $$next-version$$ + * + * @param string $tags Comma-separated list of tags. + * @param int $post_id Post ID. + * @param int $this->id Sharing ID. + */ + $tag_list = (string) apply_filters( 'jetpack_sharing_tag_list', implode( ', ', $tags ), $post_id, $this->id ); + + return html_entity_decode( wp_kses( $tag_list, '' ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ); } /** @@ -3041,3 +3081,115 @@ public function display_footer() { endif; } } + +/** + * Mastodon sharing service. + */ +class Share_Mastodon extends Sharing_Source { + /** + * Service short name. + * + * @var string + */ + public $shortname = 'mastodon'; + + /** + * Service icon font code. + * + * @var string + */ + public $icon = '\f10a'; + + /** + * Service name. + * + * @return string + */ + public function get_name() { + return __( 'Mastodon', 'jetpack' ); + } + + /** + * Get the markup of the sharing button. + * + * @param WP_Post $post Post object. + * + * @return string + */ + public function get_display( $post ) { + return $this->get_link( + $this->get_process_request_url( $post->ID ), + _x( 'Mastodon', 'share to', 'jetpack' ), + __( 'Click to share on Mastodon', 'jetpack' ), + 'share=mastodon', + 'sharing-mastodon-' . $post->ID + ); + } + + /** + * Process sharing request. Add actions that need to happen when sharing here. + * + * @param WP_Post $post Post object. + * @param array $post_data Array of information about the post we're sharing. + * + * @return void + */ + public function process_request( $post, array $post_data ) { + if ( empty( $_POST['jetpack-mastodon-instance'] ) ) { + require_once WP_SHARING_PLUGIN_DIR . 'services/class-jetpack-mastodon-modal.php'; + add_action( 'template_redirect', array( 'Jetpack_Mastodon_Modal', 'modal' ) ); + return; + } + + check_admin_referer( 'jetpack_share_mastodon_instance' ); + + $mastodon_instance = isset( $_POST['jetpack-mastodon-instance'] ) + ? trailingslashit( sanitize_text_field( wp_unslash( $_POST['jetpack-mastodon-instance'] ) ) ) + : null; + + $post_title = $this->get_share_title( $post->ID ); + $post_link = $this->get_share_url( $post->ID ); + $post_tags = $this->get_share_tags( $post->ID ); + + /** + * Allow filtering the default message that gets posted to Mastodon. + * + * @module sharedaddy + * @since $$next-version$$ + * + * @param string $share_url The default message that gets posted to Mastodon. + * @param WP_Post $post The post object. + * @param array $post_data Array of information about the post we're sharing. + */ + $shared_message = apply_filters( + 'jetpack_sharing_mastodon_default_message', + $post_title . ' ' . $post_link . ' ' . $post_tags, + $post, + $post_data + ); + + $share_url = sprintf( + '%1$sshare?text=%2$s', + $mastodon_instance, + rawurlencode( $shared_message ) + ); + + // Record stats + parent::process_request( $post, $post_data ); + + parent::redirect_request( $share_url ); + } + + /** + * Add content specific to a service in the footer. + */ + public function display_footer() { + $this->js_dialog( + $this->shortname, + array( + 'width' => 460, + 'height' => 400, + ) + ); + } +} diff --git a/projects/plugins/jetpack/modules/sharedaddy/sharing.css b/projects/plugins/jetpack/modules/sharedaddy/sharing.css index cdf36698590fc..306811670b377 100644 --- a/projects/plugins/jetpack/modules/sharedaddy/sharing.css +++ b/projects/plugins/jetpack/modules/sharedaddy/sharing.css @@ -333,6 +333,16 @@ body .sd-content ul li.share-custom.no-icon a span { .sd-social-icon-text .sd-content li.share-skype a:before { content: '\f220'; } +.sd-social-icon .sd-content ul li.share-mastodon a:before, +.sd-social-text .sd-content ul li.share-mastodon a:before, +.sd-content ul li.share-mastodon div.option.option-smart-off a:before, +.sd-social-icon-text .sd-content li.share-mastodon a:before, +.sd-social-official .sd-content li.share-mastodon a:before { + content: '\f10a'; +} +.sd-social-official .sd-content li.share-mastodon a:before { + color: #563ACC; +} .sd-social-icon .sd-content ul a.share-more:before, .sd-social-text .sd-content ul a.share-more:before, .sd-content ul li.advanced a.share-more:before, @@ -655,6 +665,11 @@ body .sd-social-icon .sd-content li.share-custom a span { color: #fff !important; } +.sd-social-icon .sd-content ul li[class*='share-'].share-mastodon a.sd-button { + background: linear-gradient(to top, #563ACC 0%, #6364FF 100%); + color: #fff !important; +} + /** * Screen Reader Text for "Icon Only" option */ From fbe842dc0e57d78cfc63ca8ea6e06673ec90dafd Mon Sep 17 00:00:00 2001 From: Bogdan Nikolic Date: Wed, 22 Feb 2023 11:03:58 +0100 Subject: [PATCH 08/38] Migration plugin: Fixed styles for mobile screens (#29081) * Migration plugin: fix styling for mobile screens --- .../plugins/migration/changelog/fix-migration-styling | 4 ++++ .../src/js/components/migration/styles.module.scss | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 projects/plugins/migration/changelog/fix-migration-styling diff --git a/projects/plugins/migration/changelog/fix-migration-styling b/projects/plugins/migration/changelog/fix-migration-styling new file mode 100644 index 0000000000000..ee7bbffae7612 --- /dev/null +++ b/projects/plugins/migration/changelog/fix-migration-styling @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fix styles for mobile screens diff --git a/projects/plugins/migration/src/js/components/migration/styles.module.scss b/projects/plugins/migration/src/js/components/migration/styles.module.scss index b4913d0d9db08..651138403c3e7 100644 --- a/projects/plugins/migration/src/js/components/migration/styles.module.scss +++ b/projects/plugins/migration/src/js/components/migration/styles.module.scss @@ -14,6 +14,10 @@ .jp-connection__connect-screen-layout__left { padding: 64px; + + @media ( max-width: 450px ) { + padding: 24px + } } .jp-connection__connect-screen-layout__right { @@ -59,6 +63,7 @@ .get-started-help { font-size: 14px; color: var( --jp-gray-50 ); + margin-top: 12px !important; margin-bottom: 0 !important; .components-button { @@ -79,6 +84,7 @@ padding: 20px; min-width: 146px; margin-right: 16px; + margin-bottom: 12px; justify-content: center; border-radius: 4px; @@ -103,8 +109,8 @@ } .components-notice { + margin-top: 20px; margin-left: 0; - margin-top: 20px; } } } From 5a16af933fafda9dcc00cd084d7bf8603acb2b75 Mon Sep 17 00:00:00 2001 From: Artur Piszek Date: Wed, 22 Feb 2023 11:35:41 +0100 Subject: [PATCH 09/38] Subscribe block: email field required in WPCOM (#28995) * Make the email required for subscriptions block on WPCOM --- .../plugins/jetpack/changelog/paid-newsletters-required-field | 4 ++++ .../jetpack/extensions/blocks/subscriptions/subscriptions.php | 1 + 2 files changed, 5 insertions(+) create mode 100644 projects/plugins/jetpack/changelog/paid-newsletters-required-field diff --git a/projects/plugins/jetpack/changelog/paid-newsletters-required-field b/projects/plugins/jetpack/changelog/paid-newsletters-required-field new file mode 100644 index 0000000000000..53c214bf02cef --- /dev/null +++ b/projects/plugins/jetpack/changelog/paid-newsletters-required-field @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +WPCOM Only: Make sure the email field in the subscribe block is required diff --git a/projects/plugins/jetpack/extensions/blocks/subscriptions/subscriptions.php b/projects/plugins/jetpack/extensions/blocks/subscriptions/subscriptions.php index 22cf962c4d93d..bba662cd18781 100644 --- a/projects/plugins/jetpack/extensions/blocks/subscriptions/subscriptions.php +++ b/projects/plugins/jetpack/extensions/blocks/subscriptions/subscriptions.php @@ -496,6 +496,7 @@ class="screen-reader-text" Date: Wed, 22 Feb 2023 16:16:07 +0200 Subject: [PATCH 10/38] Jetpack Boost: Fix Undefined array key "post" (#29096) * Fix undefined `post` warning * Add changelog --- .../plugins/boost/app/lib/Environment_Change_Detector.php | 2 +- .../plugins/boost/changelog/boost-fix-php-environment-warning | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 projects/plugins/boost/changelog/boost-fix-php-environment-warning diff --git a/projects/plugins/boost/app/lib/Environment_Change_Detector.php b/projects/plugins/boost/app/lib/Environment_Change_Detector.php index 8955b0e2fa639..fd8bd668fa55c 100644 --- a/projects/plugins/boost/app/lib/Environment_Change_Detector.php +++ b/projects/plugins/boost/app/lib/Environment_Change_Detector.php @@ -31,7 +31,7 @@ public function register_hooks() { public function handle_post_change( $post_id, $post ) { $post_types = get_post_types( array( 'name' => $post->post_type ), 'objects' ); - if ( empty( $post_types ) || $post_types['post']->public !== true ) { + if ( empty( $post_types ) || ! isset( $post_types['post'] ) || $post_types['post']->public !== true ) { return; } diff --git a/projects/plugins/boost/changelog/boost-fix-php-environment-warning b/projects/plugins/boost/changelog/boost-fix-php-environment-warning new file mode 100644 index 0000000000000..78ef0d3552f9b --- /dev/null +++ b/projects/plugins/boost/changelog/boost-fix-php-environment-warning @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed Undefined array key "post" warning From d46a69621c9e9eed45d5d531928c614573f58003 Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Wed, 22 Feb 2023 09:51:29 -0500 Subject: [PATCH 11/38] Update React peer deps to v18 (#28924) When #28710 updated the React dependencies, our packages' peer dependencies were overlooked. Update them too. --- .../js-packages/components/changelog/update-react-peer-deps | 4 ++++ projects/js-packages/components/package.json | 6 +++--- .../js-packages/connection/changelog/update-react-peer-deps | 4 ++++ projects/js-packages/connection/package.json | 6 +++--- projects/js-packages/idc/changelog/update-react-peer-deps | 4 ++++ projects/js-packages/idc/package.json | 6 +++--- .../partner-coupon/changelog/update-react-peer-deps | 4 ++++ projects/js-packages/partner-coupon/package.json | 6 +++--- .../publicize-components/changelog/update-react-peer-deps | 4 ++++ projects/js-packages/publicize-components/package.json | 2 +- 10 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 projects/js-packages/components/changelog/update-react-peer-deps create mode 100644 projects/js-packages/connection/changelog/update-react-peer-deps create mode 100644 projects/js-packages/idc/changelog/update-react-peer-deps create mode 100644 projects/js-packages/partner-coupon/changelog/update-react-peer-deps create mode 100644 projects/js-packages/publicize-components/changelog/update-react-peer-deps diff --git a/projects/js-packages/components/changelog/update-react-peer-deps b/projects/js-packages/components/changelog/update-react-peer-deps new file mode 100644 index 0000000000000..dd7eb39fb4827 --- /dev/null +++ b/projects/js-packages/components/changelog/update-react-peer-deps @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Update React peer dependencies to match updated dev dependencies. diff --git a/projects/js-packages/components/package.json b/projects/js-packages/components/package.json index 17e4a6bcfba8f..6a34c43530e0a 100644 --- a/projects/js-packages/components/package.json +++ b/projects/js-packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-components", - "version": "0.27.7", + "version": "0.27.8-alpha", "description": "Jetpack Components Package", "author": "Automattic", "license": "GPL-2.0-or-later", @@ -44,8 +44,8 @@ "webpack-cli": "4.9.1" }, "peerDependencies": { - "react": "^17.0.2", - "react-dom": "^17.0.2" + "react": "^18.0.0", + "react-dom": "^18.0.0" }, "type": "module", "browserslist": [ diff --git a/projects/js-packages/connection/changelog/update-react-peer-deps b/projects/js-packages/connection/changelog/update-react-peer-deps new file mode 100644 index 0000000000000..dd7eb39fb4827 --- /dev/null +++ b/projects/js-packages/connection/changelog/update-react-peer-deps @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Update React peer dependencies to match updated dev dependencies. diff --git a/projects/js-packages/connection/package.json b/projects/js-packages/connection/package.json index 4f0bad9811a98..5660af5565436 100644 --- a/projects/js-packages/connection/package.json +++ b/projects/js-packages/connection/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-connection", - "version": "0.25.1", + "version": "0.25.2-alpha", "description": "Jetpack Connection Component", "author": "Automattic", "license": "GPL-2.0-or-later", @@ -34,8 +34,8 @@ "react-test-renderer": "18.2.0" }, "peerDependencies": { - "react": "^17.0.2", - "react-dom": "^17.0.2" + "react": "^18.0.0", + "react-dom": "^18.0.0" }, "type": "module", "exports": { diff --git a/projects/js-packages/idc/changelog/update-react-peer-deps b/projects/js-packages/idc/changelog/update-react-peer-deps new file mode 100644 index 0000000000000..dd7eb39fb4827 --- /dev/null +++ b/projects/js-packages/idc/changelog/update-react-peer-deps @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Update React peer dependencies to match updated dev dependencies. diff --git a/projects/js-packages/idc/package.json b/projects/js-packages/idc/package.json index 56983eccd8f8b..42a6d2168f72e 100644 --- a/projects/js-packages/idc/package.json +++ b/projects/js-packages/idc/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-idc", - "version": "0.10.34", + "version": "0.10.35-alpha", "description": "Jetpack Connection Component", "author": "Automattic", "license": "GPL-2.0-or-later", @@ -26,8 +26,8 @@ "react-test-renderer": "18.2.0" }, "peerDependencies": { - "react": "^17.0.2", - "react-dom": "^17.0.2" + "react": "^18.0.0", + "react-dom": "^18.0.0" }, "type": "module", "exports": { diff --git a/projects/js-packages/partner-coupon/changelog/update-react-peer-deps b/projects/js-packages/partner-coupon/changelog/update-react-peer-deps new file mode 100644 index 0000000000000..dd7eb39fb4827 --- /dev/null +++ b/projects/js-packages/partner-coupon/changelog/update-react-peer-deps @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Update React peer dependencies to match updated dev dependencies. diff --git a/projects/js-packages/partner-coupon/package.json b/projects/js-packages/partner-coupon/package.json index 7712557022321..e344a46661a4f 100644 --- a/projects/js-packages/partner-coupon/package.json +++ b/projects/js-packages/partner-coupon/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-partner-coupon", - "version": "0.2.40", + "version": "0.2.41-alpha", "description": "This package aims to add components to make it easier to redeem partner coupons", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/partner-coupon/#readme", "bugs": { @@ -36,8 +36,8 @@ "react-test-renderer": "18.2.0" }, "peerDependencies": { - "react": "^17.0.2", - "react-dom": "17.0.2" + "react": "^18.0.0", + "react-dom": "^18.0.0" }, "dependencies": { "@automattic/jetpack-components": "workspace:*", diff --git a/projects/js-packages/publicize-components/changelog/update-react-peer-deps b/projects/js-packages/publicize-components/changelog/update-react-peer-deps new file mode 100644 index 0000000000000..dd7eb39fb4827 --- /dev/null +++ b/projects/js-packages/publicize-components/changelog/update-react-peer-deps @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Update React peer dependencies to match updated dev dependencies. diff --git a/projects/js-packages/publicize-components/package.json b/projects/js-packages/publicize-components/package.json index e9ee8d9b9d811..d5274c6c4e302 100644 --- a/projects/js-packages/publicize-components/package.json +++ b/projects/js-packages/publicize-components/package.json @@ -68,6 +68,6 @@ ".": "./index.js" }, "peerDependencies": { - "react-dom": "17.0.2" + "react-dom": "^18.0.0" } } From 21d67f7b1e1892af7d5ad36f1abb2587b9e1ac71 Mon Sep 17 00:00:00 2001 From: Samiff Date: Wed, 22 Feb 2023 09:02:17 -0700 Subject: [PATCH 12/38] jetpack-mu-wpcom: updated checks for coming-soon (#28932) * jetpack-mu-wpcom: updated checks for coming-soon * Fixup project versions * Try major version bump * Fixup project versions * More fixup project versions * changelog --- .../update-jetpack-mu-wpcom-coming-soon-checks | 4 ++++ .../packages/jetpack-mu-wpcom/package.json | 2 +- .../src/class-jetpack-mu-wpcom.php | 18 +++++++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/update-jetpack-mu-wpcom-coming-soon-checks diff --git a/projects/packages/jetpack-mu-wpcom/changelog/update-jetpack-mu-wpcom-coming-soon-checks b/projects/packages/jetpack-mu-wpcom/changelog/update-jetpack-mu-wpcom-coming-soon-checks new file mode 100644 index 0000000000000..0c12e8dc70caa --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/update-jetpack-mu-wpcom-coming-soon-checks @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Updated checks for loading the coming soon feature. diff --git a/projects/packages/jetpack-mu-wpcom/package.json b/projects/packages/jetpack-mu-wpcom/package.json index 80fc4d2c393b0..447a26265fc71 100644 --- a/projects/packages/jetpack-mu-wpcom/package.json +++ b/projects/packages/jetpack-mu-wpcom/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-mu-wpcom", - "version": "0.2.2", + "version": "0.2.3-alpha", "description": "Enhances your site with features powered by WordPress.com", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/jetpack-mu-wpcom/#readme", "bugs": { diff --git a/projects/packages/jetpack-mu-wpcom/src/class-jetpack-mu-wpcom.php b/projects/packages/jetpack-mu-wpcom/src/class-jetpack-mu-wpcom.php index 6f1aa08e6f9f9..11814a3bdc0ee 100644 --- a/projects/packages/jetpack-mu-wpcom/src/class-jetpack-mu-wpcom.php +++ b/projects/packages/jetpack-mu-wpcom/src/class-jetpack-mu-wpcom.php @@ -14,7 +14,7 @@ */ class Jetpack_Mu_Wpcom { - const PACKAGE_VERSION = '0.2.2'; + const PACKAGE_VERSION = '0.2.3-alpha'; const PKG_DIR = __DIR__ . '/../'; /** @@ -29,10 +29,9 @@ public static function init() { // Shared code for src/features require_once self::PKG_DIR . 'src/common/index.php'; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.NotAbsolutePath - // Todo: once coming-soon is removed from ETK, we can remove the has_action check. - if ( has_action( 'plugins_loaded', 'A8C\FSE\load_coming_soon' ) === false ) { - add_action( 'plugins_loaded', array( __CLASS__, 'load_coming_soon' ) ); - } + + // Coming Soon feature. + add_action( 'plugins_loaded', array( __CLASS__, 'load_coming_soon' ) ); /** * Runs right after the Jetpack_Mu_Wpcom package is initialized. @@ -46,6 +45,15 @@ public static function init() { * Load the Coming Soon feature. */ public static function load_coming_soon() { + /** + * On WoA sites, users may be using non-symlinked older versions of the FSE plugin. + * If they are, check the active version to avoid redeclaration errors. + */ + $invalid_fse_version_active = is_plugin_active( 'full-site-editing/full-site-editing-plugin.php' ) && version_compare( get_plugin_data( WP_PLUGIN_DIR . '/full-site-editing/full-site-editing-plugin.php' )['Version'], '3.56084', '<' ); + if ( $invalid_fse_version_active ) { + return; + } + if ( ( defined( 'WPCOM_PUBLIC_COMING_SOON' ) && WPCOM_PUBLIC_COMING_SOON ) || apply_filters( 'a8c_enable_public_coming_soon', false ) From d4271af48d816669f4a97dbc92f397a021938bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Wed, 22 Feb 2023 13:31:03 -0300 Subject: [PATCH 13/38] Jetpack: change how to register VideoPress video block (#29084) * [not verified] tidy how register VideoPress video block * [not verified] changelog * [not verified] Update projects/plugins/jetpack/extensions/extended-blocks/videopress-video/videopress-video.php Co-authored-by: Luiz Kowalski * [not verified] Update projects/plugins/jetpack/extensions/extended-blocks/videopress-video/videopress-video.php Co-authored-by: Luiz Kowalski * align equals --------- Co-authored-by: Luiz Kowalski --- .../changelog/update-vieopress-register-v6 | 4 +++ .../videopress-video/videopress-video.php | 31 +++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/update-vieopress-register-v6 diff --git a/projects/plugins/jetpack/changelog/update-vieopress-register-v6 b/projects/plugins/jetpack/changelog/update-vieopress-register-v6 new file mode 100644 index 0000000000000..fa1ae3d0c225a --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-vieopress-register-v6 @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Jetpack: tidy registering VideoPress video block diff --git a/projects/plugins/jetpack/extensions/extended-blocks/videopress-video/videopress-video.php b/projects/plugins/jetpack/extensions/extended-blocks/videopress-video/videopress-video.php index c63222f3e9e38..0c609d1e75e87 100644 --- a/projects/plugins/jetpack/extensions/extended-blocks/videopress-video/videopress-video.php +++ b/projects/plugins/jetpack/extensions/extended-blocks/videopress-video/videopress-video.php @@ -9,29 +9,26 @@ use Automattic\Jetpack\VideoPress\Initializer as VideoPress_Pkg_Initializer; +// Set the videopress/video block availability, depending on the site plan. add_action( - 'init', + 'jetpack_register_gutenberg_extensions', function () { - $is_video_extension_available = in_array( 'videopress/video', \Jetpack_Gutenberg::get_available_extensions(), true ); - $is_chapters_extension_available = in_array( 'videopress/video-chapters', \Jetpack_Gutenberg::get_available_extensions(), true ); - - $is_some_extension_available = $is_video_extension_available || $is_chapters_extension_available; - $is_proxied = function_exists( 'wpcom_is_proxied_request' ) ? wpcom_is_proxied_request() : false; - - if ( ! $is_some_extension_available && ! $is_proxied ) { - return; - } - - if ( method_exists( 'Automattic\Jetpack\VideoPress\Initializer', 'register_videopress_video_block' ) ) { - VideoPress_Pkg_Initializer::register_videopress_video_block(); - } + \Jetpack_Gutenberg::set_availability_for_plan( 'videopress/video' ); } ); -// Set the videopress/video block availability, depending on the site plan. +// Register the videopress/video block. add_action( - 'jetpack_register_gutenberg_extensions', + 'init', function () { - \Jetpack_Gutenberg::set_availability_for_plan( 'videopress/video' ); + $availability = \Jetpack_Gutenberg::get_availability(); + $is_videopress_video_enabled = isset( $availability['videopress/video'] ) && $availability['videopress/video']['available']; + + if ( + $is_videopress_video_enabled && + method_exists( 'Automattic\Jetpack\VideoPress\Initializer', 'register_videopress_video_block' ) + ) { + VideoPress_Pkg_Initializer::register_videopress_video_block(); + } } ); From c2058086171de7b9c5d8a60f53a6a216ac3db8cf Mon Sep 17 00:00:00 2001 From: Calypso Bot Date: Wed, 22 Feb 2023 18:17:18 +0100 Subject: [PATCH 14/38] Update Instant Search Dependency Updates (#28681) Co-authored-by: Renovate Bot --- pnpm-lock.yaml | 26 +++++++++---------- ...renovate-instant-search-dependency-updates | 4 +++ projects/packages/search/package.json | 6 ++--- .../packages/search/src/class-package.php | 2 +- ...renovate-instant-search-dependency-updates | 4 +++ projects/packages/wordads/package.json | 6 ++--- .../packages/wordads/src/class-package.php | 2 +- 7 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 projects/packages/search/changelog/renovate-instant-search-dependency-updates create mode 100644 projects/packages/wordads/changelog/renovate-instant-search-dependency-updates diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24ea7d7c48ebd..8c109cd3a8651 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1781,8 +1781,8 @@ importers: specifier: 4.0.0 version: 4.0.0 preact: - specifier: 10.5.15 - version: 10.5.15 + specifier: 10.12.1 + version: 10.12.1 prop-types: specifier: 15.7.2 version: 15.7.2 @@ -1845,8 +1845,8 @@ importers: specifier: 8.19.1 version: 8.19.1 '@testing-library/preact': - specifier: 3.2.2 - version: 3.2.2(@testing-library/dom@8.19.1)(preact@10.5.15) + specifier: 3.2.3 + version: 3.2.3(@testing-library/dom@8.19.1)(preact@10.12.1) '@testing-library/react': specifier: 13.4.0 version: 13.4.0(@testing-library/dom@8.19.1)(react-dom@18.2.0)(react@18.2.0) @@ -2138,8 +2138,8 @@ importers: specifier: 4.0.0 version: 4.0.0 preact: - specifier: 10.5.15 - version: 10.5.15 + specifier: 10.12.1 + version: 10.12.1 prop-types: specifier: 15.7.2 version: 15.7.2 @@ -2202,8 +2202,8 @@ importers: specifier: 8.19.1 version: 8.19.1 '@testing-library/preact': - specifier: 3.2.2 - version: 3.2.2(@testing-library/dom@8.19.1)(preact@10.5.15) + specifier: 3.2.3 + version: 3.2.3(@testing-library/dom@8.19.1)(preact@10.12.1) '@testing-library/react': specifier: 13.4.0 version: 13.4.0(@testing-library/dom@8.19.1)(react-dom@18.2.0)(react@18.2.0) @@ -9326,15 +9326,15 @@ packages: redent: 3.0.0 dev: true - /@testing-library/preact@3.2.2(@testing-library/dom@8.19.1)(preact@10.5.15): - resolution: {integrity: sha512-mMPEp/9TOOqf3QqDHY02ieGFfRbi8fAxZvRifn+vOzrdNcCR1zchwPA6BvqXG3wAweRan4QJioYgEc1cePeC3g==} + /@testing-library/preact@3.2.3(@testing-library/dom@8.19.1)(preact@10.12.1): + resolution: {integrity: sha512-y6Kklp1XK3f1X2fWCbujmJyzkf+1BgLYXNgAx21j9+D4CoqMTz5qC4SQufb1L6q/jxLGACzrQ90ewVOTBvHOfg==} engines: {node: '>= 12'} peerDependencies: '@testing-library/dom': ^8.11.1 preact: '>=10 || ^10.0.0-alpha.0 || ^10.0.0-beta.0' dependencies: '@testing-library/dom': 8.19.1 - preact: 10.5.15 + preact: 10.12.1 dev: true /@testing-library/react@13.4.0(@testing-library/dom@8.19.1)(react-dom@18.2.0)(react@18.2.0): @@ -19637,8 +19637,8 @@ packages: resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} dev: false - /preact@10.5.15: - resolution: {integrity: sha512-5chK29n6QcJc3m1lVrKQSQ+V7K1Gb8HeQY6FViQ5AxCAEGu3DaHffWNDkC9+miZgsLvbvU9rxbV1qinGHMHzqA==} + /preact@10.12.1: + resolution: {integrity: sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==} /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} diff --git a/projects/packages/search/changelog/renovate-instant-search-dependency-updates b/projects/packages/search/changelog/renovate-instant-search-dependency-updates new file mode 100644 index 0000000000000..c47cb18e82997 --- /dev/null +++ b/projects/packages/search/changelog/renovate-instant-search-dependency-updates @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Updated package dependencies. diff --git a/projects/packages/search/package.json b/projects/packages/search/package.json index 98f7b60c03f50..35adb2b89caef 100644 --- a/projects/packages/search/package.json +++ b/projects/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "jetpack-search", - "version": "0.33.0", + "version": "0.33.1-alpha", "description": "Package for Jetpack Search products", "main": "main.js", "directories": { @@ -55,7 +55,7 @@ "fast-json-stable-stringify": "2.1.0", "lodash": "4.17.21", "photon": "4.0.0", - "preact": "10.5.15", + "preact": "10.12.1", "prop-types": "15.7.2", "q-flat": "1.0.7", "qss": "2.0.3", @@ -78,7 +78,7 @@ "@babel/runtime": "7.20.13", "@size-limit/preset-app": "6.0.4", "@testing-library/dom": "8.19.1", - "@testing-library/preact": "3.2.2", + "@testing-library/preact": "3.2.3", "@testing-library/react": "13.4.0", "@wordpress/babel-plugin-import-jsx-pragma": "4.9.0", "@wordpress/browserslist-config": "5.9.0", diff --git a/projects/packages/search/src/class-package.php b/projects/packages/search/src/class-package.php index 6ea37d1660275..04db0a1d5bcc2 100644 --- a/projects/packages/search/src/class-package.php +++ b/projects/packages/search/src/class-package.php @@ -11,7 +11,7 @@ * Search package general information */ class Package { - const VERSION = '0.33.0'; + const VERSION = '0.33.1-alpha'; const SLUG = 'search'; /** diff --git a/projects/packages/wordads/changelog/renovate-instant-search-dependency-updates b/projects/packages/wordads/changelog/renovate-instant-search-dependency-updates new file mode 100644 index 0000000000000..c47cb18e82997 --- /dev/null +++ b/projects/packages/wordads/changelog/renovate-instant-search-dependency-updates @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Updated package dependencies. diff --git a/projects/packages/wordads/package.json b/projects/packages/wordads/package.json index c78ad41a7bc21..d5cb6a61bf9e0 100644 --- a/projects/packages/wordads/package.json +++ b/projects/packages/wordads/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-wordads", - "version": "0.2.32", + "version": "0.2.33-alpha", "description": "Earn income by allowing Jetpack to display high quality ads.", "main": "main.js", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/wordads/#readme", @@ -46,7 +46,7 @@ "fast-json-stable-stringify": "2.1.0", "lodash": "4.17.21", "photon": "4.0.0", - "preact": "10.5.15", + "preact": "10.12.1", "prop-types": "15.7.2", "q-flat": "1.0.7", "qss": "2.0.3", @@ -69,7 +69,7 @@ "@babel/runtime": "7.20.13", "@size-limit/preset-app": "6.0.4", "@testing-library/dom": "8.19.1", - "@testing-library/preact": "3.2.2", + "@testing-library/preact": "3.2.3", "@testing-library/react": "13.4.0", "@wordpress/babel-plugin-import-jsx-pragma": "4.9.0", "@wordpress/browserslist-config": "5.9.0", diff --git a/projects/packages/wordads/src/class-package.php b/projects/packages/wordads/src/class-package.php index 3542dd1b8787c..6101b214e4906 100644 --- a/projects/packages/wordads/src/class-package.php +++ b/projects/packages/wordads/src/class-package.php @@ -11,7 +11,7 @@ * WordAds package general information */ class Package { - const VERSION = '0.2.32'; + const VERSION = '0.2.33-alpha'; const SLUG = 'wordads'; /** From 449136528d78ff7eaaff743c66e51005f461e349 Mon Sep 17 00:00:00 2001 From: Clemen Date: Wed, 22 Feb 2023 17:47:03 +0000 Subject: [PATCH 15/38] CRM: PHP notice on woo order draft status (#29099) * Add checkout-draft Woo order status to statuses map * Add checkout-draft Woo order status to statuses map --- ...x-crm-php-notice-on-woo-order-draft-status | 4 +++ .../woo-sync/includes/class-woo-sync.php | 30 ++++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 projects/plugins/crm/changelog/fix-crm-php-notice-on-woo-order-draft-status diff --git a/projects/plugins/crm/changelog/fix-crm-php-notice-on-woo-order-draft-status b/projects/plugins/crm/changelog/fix-crm-php-notice-on-woo-order-draft-status new file mode 100644 index 0000000000000..b1b8b6ad1f4ca --- /dev/null +++ b/projects/plugins/crm/changelog/fix-crm-php-notice-on-woo-order-draft-status @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +PHP notice when a WooCommerce order is in a Draft status diff --git a/projects/plugins/crm/modules/woo-sync/includes/class-woo-sync.php b/projects/plugins/crm/modules/woo-sync/includes/class-woo-sync.php index 1b8881aa57018..49a0c491b756c 100644 --- a/projects/plugins/crm/modules/woo-sync/includes/class-woo-sync.php +++ b/projects/plugins/crm/modules/woo-sync/includes/class-woo-sync.php @@ -228,13 +228,14 @@ public function get_settings() { */ public function get_woo_order_statuses() { $woo_order_statuses = array( - 'wcpending' => __( 'Pending', 'zero-bs-crm' ), - 'wcprocessing' => __( 'Processing', 'zero-bs-crm' ), - 'wconhold' => __( 'On hold', 'zero-bs-crm' ), - 'wccompleted' => __( 'Completed', 'zero-bs-crm' ), - 'wccancelled' => __( 'Cancelled', 'zero-bs-crm' ), - 'wcrefunded' => __( 'Refunded', 'zero-bs-crm' ), - 'wcfailed' => __( 'Failed', 'zero-bs-crm' ), + 'wcpending' => __( 'Pending', 'zero-bs-crm' ), + 'wcprocessing' => __( 'Processing', 'zero-bs-crm' ), + 'wconhold' => __( 'On hold', 'zero-bs-crm' ), + 'wccompleted' => __( 'Completed', 'zero-bs-crm' ), + 'wccancelled' => __( 'Cancelled', 'zero-bs-crm' ), + 'wcrefunded' => __( 'Refunded', 'zero-bs-crm' ), + 'wcfailed' => __( 'Failed', 'zero-bs-crm' ), + 'wccheckoutdraft' => __( 'Draft', 'zero-bs-crm' ), ); return apply_filters( 'zbs-woo-additional-status', $woo_order_statuses ); } @@ -1531,13 +1532,14 @@ public function woo_order_status_mapping( $crm_type ) { $setting_prefix = $this->get_woo_order_mapping_types()[ $crm_type ]['prefix']; return array( - 'completed' => $setting_prefix . 'wccompleted', - 'on-hold' => $setting_prefix . 'wconhold', - 'cancelled' => $setting_prefix . 'wccancelled', - 'processing' => $setting_prefix . 'wcprocessing', - 'refunded' => $setting_prefix . 'wcrefunded', - 'failed' => $setting_prefix . 'wcfailed', - 'pending' => $setting_prefix . 'wcpending', + 'completed' => $setting_prefix . 'wccompleted', + 'on-hold' => $setting_prefix . 'wconhold', + 'cancelled' => $setting_prefix . 'wccancelled', + 'processing' => $setting_prefix . 'wcprocessing', + 'refunded' => $setting_prefix . 'wcrefunded', + 'failed' => $setting_prefix . 'wcfailed', + 'pending' => $setting_prefix . 'wcpending', + 'checkout-draft' => $setting_prefix . 'wccheckoutdraft', ); } From 0851103464b5b43886a7a5654a34a6bb321c3b74 Mon Sep 17 00:00:00 2001 From: Osk Date: Wed, 22 Feb 2023 16:09:47 -0300 Subject: [PATCH 16/38] Related Posts: Add block suport for fontFamily controls (#29097) --- .../changelog/add-related-posts-block-font-family-support | 4 ++++ .../plugins/jetpack/extensions/blocks/related-posts/index.js | 1 + 2 files changed, 5 insertions(+) create mode 100644 projects/plugins/jetpack/changelog/add-related-posts-block-font-family-support diff --git a/projects/plugins/jetpack/changelog/add-related-posts-block-font-family-support b/projects/plugins/jetpack/changelog/add-related-posts-block-font-family-support new file mode 100644 index 0000000000000..4bdb870c4c6de --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-related-posts-block-font-family-support @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Add block support for font family in Related Posts block diff --git a/projects/plugins/jetpack/extensions/blocks/related-posts/index.js b/projects/plugins/jetpack/extensions/blocks/related-posts/index.js index c590e47117abe..e691eec5d50c4 100644 --- a/projects/plugins/jetpack/extensions/blocks/related-posts/index.js +++ b/projects/plugins/jetpack/extensions/blocks/related-posts/index.js @@ -66,6 +66,7 @@ export const settings = { padding: true, }, typography: { + __experimentalFontFamily: true, fontSize: true, lineHeight: true, }, From 68604d3e4012f92d3ccdb8245f9c851ca692727c Mon Sep 17 00:00:00 2001 From: dkmyta <43220201+dkmyta@users.noreply.github.com> Date: Wed, 22 Feb 2023 11:23:43 -0800 Subject: [PATCH 17/38] Protect: Add existing plan/license key CTA (#27745) * Initialize project branch * Protect: Add WAF package (#27530) * Protect: Clean up components (#27256) * Protect: Add routing and blank Firewall page (#27245) * Protect: Add Tabbed Navigation (#27259) * Protect: Add "new" badge for users who have never viewed the Firewall tab (#27265) * Protect: Add useWafData hook (#27307) * Add: Protect WAF header (#27279) * Add hook for interfacing with WAF data * [not verified] Add firewall-header component folder structure * [not verified] Apply header component code and styling * [not verified] Remove unneeded comments * [not verified] Update hasRequiredPlan check to useProtectData * [not verified] Fix typo * Add FirewallHeader component stories and improve portability * Add useWafData hook placeholder for moduleIsEnabled check * [not verified] Remove isModuleEnabled property in favor of currently available WAF data * [not verified] Remove console logging * [not verified] Add upgrade functionality to button * [not verified] Add Popover component for upgrade info * Fix styling issues * Create ConnectedFirewallHeader component and move all logic out of FirewallPage * Improve code efficiency * Update FirewallHeader status check, and checkout flow redirect URL * changelog * [not verified] Remove changelog entry * Change FirewallUrl to firewallUrl Co-authored-by: Nate Weller * Protect: Add WAF footer (#27280) * Add hook for interfacing with WAF data * [not verified] Add firewall-header component folder structure * [not verified] Apply header component code and styling * [not verified] Remove unneeded comments * [not verified] Update hasRequiredPlan check to useProtectData * [not verified] Fix typo * Add FirewallHeader component stories and improve portability * Add useWafData hook placeholder for moduleIsEnabled check * [not verified] Remove isModuleEnabled property in favor of currently available WAF data * [not verified] Remove console logging * [not verified] Add upgrade functionality to button * [not verified] Add Popover component for upgrade info * Introduce and apply FirewallFooter component * Add central AdminSection component for better visualization * Improve spacing * [not verified] Apply initial run at StandaloneModeModal component * [not verified] Update Footer component to ScanFooter * [not verified] Improve StandaloneModeModal structure and styling * [not verified] Add FirewallFooter stories * [not verified] Update FirewallFooter to use SeventyFiveLayout * [not verified] Update styling comments * Fix styling issues * Create ConnectedFirewallHeader component and move all logic out of FirewallPage * Improve code efficiency * Update FirewallHeader status check, and checkout flow redirect URL * changelog * [not verified] Remove changelog entry * Change FirewallUrl to firewallUrl * Fix styling issues Co-authored-by: Nate Weller * Fix composer.json * Changelog * Protect: Add upgrade prompt while WAF enabled with no rules access (#27584) * Add upgrade prompt when WAF enabled but no rules access * Create separate internal component for upgrade prompt * Add story for on with no plan * WAF: Remove has_rules_access in favour of external checks (#27479) * [not verified] Update WAF package to use portable Current_Plan over Jetpack_Plan for upgrade detection * Add jetpack-plans dependency direct to the WAF package * Add and use hasScan (over hasRulesAccess) check in Jetpack settings for the WAF module * Remove hasRulesAccess property from WAF module component * Remove has_rules_access method in favour of external plan checks * Protect: Add Basic WAF Controls (#27291) * Update Protect to version 1.2.0-alpha * Protect: WAF share data toggle (#27577) * [not verified] Add jetpack_waf_share_data option value to initial state, and use for checked value * [not verified] Remove comments * [not verified] Remove prior solution in favor of WAF endpoints solution and apply toggleShareData functionality to FirewallFooter checkbox * [not verified] Improve share data toggle functionality * [not verified] Improve code organization * [not verified] Add isEnabled check for share data section display * Remove wafShareData from initial state Co-authored-by: Nate Weller * Changelogs * Remove outdated TODO comment * Protect: Add notices to the Firewall screen (#27621) * Fix invalid HTML - pre cannot be a child of p * Fix notice duration timeout * Remove manual WAF initialization * Register REST routes on WAF init * Update WAF after settings have changed * Update tracks events in Jetpack Protect (#27659) * Initialize project branch * Initialize project branch * Add existing plan link to pricing page and fix registration redirect to check for plan when rendering the UI * Initialize project branch * Remove changelog entries * Use addQueryArgs and getQueryArgs to check plan and redirect after successful user connection * Update redirect handling after user connection to managed with PHP * changelog * Initial commit. * Check for licenses and redirect users to the license activation page. * Add changelogs and version bumps. * Add changelog files. * Adding filter to backup class * changelog * Fix constant naming * Add Jetpack Complete plan ID's. * Add the filter comment. * Revert prior changes and update as per new standards * Update filter callback format * Revert changes to getProtectFree registration flow * Add plan and status refresh to getProtectFree reg flow and update getStarted button to have a unique loading state * Require jetpack-connection package * Revert direct require of jetpack-connection package * Add checkout flow to get started button to handle license check * Provide plugins with a plugin slug to make sure we only verify licenses for the products we need. * Fix license check based on the plugin slug. * If an appropriate unattached license is found, check if the plugin already has a license activated on the site before redirecting them. * Move the license check code from connection to licensing package. * Update changelog files. * Bump package version. * Replace parse_url() with wp_parse_url(). * Update license check callback to include a plan check * Update UI handling for the loading of various buttons * Remove siteProductAvailabilityHandler arg in the existing checkout flow in favour of plan check in the filter callback * Fix project versions * Add missing product IDs, remove plan check from filter callback in favour of reattaching siteProductAvailabilityHandler to the checkout flow --------- Co-authored-by: Nate Weller Co-authored-by: Sergey Mitroshin Co-authored-by: bindlegirl <1242807+bindlegirl@users.noreply.github.com> --- .../changelog/add-protect-existing-plan-cta | 4 ++ projects/plugins/protect/composer.json | 2 +- projects/plugins/protect/jetpack-protect.php | 4 +- .../protect/src/class-jetpack-protect.php | 49 +++++++++++++++++++ .../src/js/components/admin-page/index.jsx | 2 +- .../js/components/interstitial-page/index.jsx | 40 ++++++++++++--- .../interstitial-page/styles.module.scss | 12 +++++ .../src/js/components/pricing-table/index.jsx | 38 ++++++++------ 8 files changed, 126 insertions(+), 25 deletions(-) create mode 100644 projects/plugins/protect/changelog/add-protect-existing-plan-cta create mode 100644 projects/plugins/protect/src/js/components/interstitial-page/styles.module.scss diff --git a/projects/plugins/protect/changelog/add-protect-existing-plan-cta b/projects/plugins/protect/changelog/add-protect-existing-plan-cta new file mode 100644 index 0000000000000..db0eea854ebe3 --- /dev/null +++ b/projects/plugins/protect/changelog/add-protect-existing-plan-cta @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Interstitial page link for getting started with an existing plan or license key diff --git a/projects/plugins/protect/composer.json b/projects/plugins/protect/composer.json index 7f9cea4fb4fca..e83949694eb2f 100644 --- a/projects/plugins/protect/composer.json +++ b/projects/plugins/protect/composer.json @@ -79,6 +79,6 @@ "automattic/jetpack-autoloader": true, "automattic/jetpack-composer-plugin": true }, - "autoloader-suffix": "c4802e05bbcf59fd3b6350e8d3e5482c_protectⓥ1_3_1_alpha" + "autoloader-suffix": "c4802e05bbcf59fd3b6350e8d3e5482c_protectⓥ1_4_0_alpha" } } diff --git a/projects/plugins/protect/jetpack-protect.php b/projects/plugins/protect/jetpack-protect.php index beeab97c9af4f..0354dedacc649 100644 --- a/projects/plugins/protect/jetpack-protect.php +++ b/projects/plugins/protect/jetpack-protect.php @@ -3,7 +3,7 @@ * Plugin Name: Jetpack Protect * Plugin URI: https://wordpress.org/plugins/jetpack-protect * Description: Security tools that keep your site safe and sound, from posts to plugins. - * Version: 1.3.1-alpha + * Version: 1.4.0-alpha * Author: Automattic - Jetpack Security team * Author URI: https://jetpack.com/protect/ * License: GPLv2 or later @@ -32,7 +32,7 @@ exit; } -define( 'JETPACK_PROTECT_VERSION', '1.3.1-alpha' ); +define( 'JETPACK_PROTECT_VERSION', '1.4.0-alpha' ); define( 'JETPACK_PROTECT_DIR', plugin_dir_path( __FILE__ ) ); define( 'JETPACK_PROTECT_ROOT_FILE', __FILE__ ); define( 'JETPACK_PROTECT_ROOT_FILE_RELATIVE_PATH', plugin_basename( __FILE__ ) ); diff --git a/projects/plugins/protect/src/class-jetpack-protect.php b/projects/plugins/protect/src/class-jetpack-protect.php index 051d008681bdc..a7a3a44e154af 100644 --- a/projects/plugins/protect/src/class-jetpack-protect.php +++ b/projects/plugins/protect/src/class-jetpack-protect.php @@ -33,6 +33,27 @@ */ class Jetpack_Protect { + /** + * Licenses product ID. + * + * @var string + */ + const JETPACK_SCAN_PRODUCT_IDS = array( + 2010, // JETPACK_SECURITY_DAILY. + 2011, // JETPACK_SECURITY_DAILY_MOTNHLY. + 2012, // JETPACK_SECURITY_REALTIME. + 2013, // JETPACK_SECURITY_REALTIME_MONTHLY. + 2014, // JETPACK_COMPLETE. + 2015, // JETPACK_COMPLETE_MONTHLY. + 2016, // JETPACK_SECURITY_TIER_1_YEARLY. + 2017, // JETPACK_SECURITY_TIER_1_MONTHLY. + 2019, // JETPACK_SECURITY_TIER_2_YEARLY. + 2020, // JETPACK_SECURITY_TIER_2_MONTHLY. + 2106, // JETPACK_SCAN. + 2107, // JETPACK_SCAN_MONTHLY. + 2108, // JETPACK_SCAN_REALTIME. + 2109, // JETPACK_SCAN_REALTIME_MONTHLY. + ); const JETPACK_WAF_MODULE_SLUG = 'waf'; const JETPACK_PROTECT_ACTIVATION_OPTION = JETPACK_PROTECT_SLUG . '_activated'; @@ -92,6 +113,8 @@ function () { 1 ); + add_filter( 'jetpack_connection_user_has_license', array( $this, 'jetpack_check_user_licenses' ), 10, 3 ); + add_filter( 'jetpack_get_available_standalone_modules', array( $this, 'protect_filter_available_modules' ), 10, 1 ); } @@ -305,6 +328,32 @@ public function protect_filter_available_modules( $modules ) { return array_merge( array( self::JETPACK_WAF_MODULE_SLUG ), $modules ); } + /** + * Check for user licenses. + * + * @param boolean $has_license Check if user has a license. + * @param object $licenses List of licenses. + * @param string $plugin_slug The plugin that initiated the flow. + * + * @return boolean + */ + public static function jetpack_check_user_licenses( $has_license, $licenses, $plugin_slug ) { + if ( $plugin_slug !== JETPACK_PROTECT_SLUG || $has_license ) { + return $has_license; + } + + $license_found = false; + + foreach ( $licenses as $license ) { + if ( in_array( $license->product_id, self::JETPACK_SCAN_PRODUCT_IDS, true ) ) { + $license_found = true; + break; + } + } + + return $license_found; + } + /** * Get WAF "Seen" Status * diff --git a/projects/plugins/protect/src/js/components/admin-page/index.jsx b/projects/plugins/protect/src/js/components/admin-page/index.jsx index f71696d817285..b551c122f716d 100644 --- a/projects/plugins/protect/src/js/components/admin-page/index.jsx +++ b/projects/plugins/protect/src/js/components/admin-page/index.jsx @@ -46,7 +46,7 @@ const AdminPage = ( { children } ) => { * - Checkout workflow has started */ if ( ! isRegistered || hasCheckoutStarted ) { - return ; + return ; } return ( diff --git a/projects/plugins/protect/src/js/components/interstitial-page/index.jsx b/projects/plugins/protect/src/js/components/interstitial-page/index.jsx index 3e803ce28c651..143ac653e5307 100644 --- a/projects/plugins/protect/src/js/components/interstitial-page/index.jsx +++ b/projects/plugins/protect/src/js/components/interstitial-page/index.jsx @@ -3,33 +3,61 @@ import { AdminSectionHero, Col, Container, + Text, + Button, } from '@automattic/jetpack-components'; import { __ } from '@wordpress/i18n'; -import React from 'react'; +import React, { useState, useCallback } from 'react'; import useAnalyticsTracks from '../../hooks/use-analytics-tracks'; import Logo from '../logo'; import ConnectedPricingTable from '../pricing-table'; +import styles from './styles.module.scss'; /** - * Intersitial Page + * Interstitial Page * * @param {object} props - Component props * @param {Function} props.onScanAdd - Callback when adding paid protect product successfully - * @param {Function} props.scanJustAdded - Callback when adding paid protect product was recently added * @returns {React.Component} Interstitial react component. */ -const InterstitialPage = ( { onScanAdd, scanJustAdded } ) => { +const InterstitialPage = ( { onScanAdd } ) => { + const [ getStartedButtonIsLoading, setGetStartedButtonIsLoading ] = useState( false ); + + const getStarted = useCallback( () => { + setGetStartedButtonIsLoading( true ); + onScanAdd(); + }, [ onScanAdd ] ); + // Track view for Protect WAF page. useAnalyticsTracks( { pageViewEventName: 'protect_interstitial', } ); return ( - }> + + + + { __( 'Already have an existing plan or license key? ', 'jetpack-protect' ) } + + + + } + > - + diff --git a/projects/plugins/protect/src/js/components/interstitial-page/styles.module.scss b/projects/plugins/protect/src/js/components/interstitial-page/styles.module.scss new file mode 100644 index 0000000000000..c8afc9eeabe03 --- /dev/null +++ b/projects/plugins/protect/src/js/components/interstitial-page/styles.module.scss @@ -0,0 +1,12 @@ +.protect-header { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; +} + +.get-started-button { + &:focus:not(:disabled) { + box-shadow: none; + } +} diff --git a/projects/plugins/protect/src/js/components/pricing-table/index.jsx b/projects/plugins/protect/src/js/components/pricing-table/index.jsx index 0ffc9b5a4ac5c..31c93e7cd677c 100644 --- a/projects/plugins/protect/src/js/components/pricing-table/index.jsx +++ b/projects/plugins/protect/src/js/components/pricing-table/index.jsx @@ -10,26 +10,30 @@ import { PricingTableItem, } from '@automattic/jetpack-components'; import { useConnection } from '@automattic/jetpack-connection'; +import { useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import React, { useCallback, useState } from 'react'; import useAnalyticsTracks from '../../hooks/use-analytics-tracks'; import useProtectData from '../../hooks/use-protect-data'; import useWafData from '../../hooks/use-waf-data'; +import { STORE_ID } from '../../state/store'; /** * Product Detail component. * * @param {object} props - Component props * @param {Function} props.onScanAdd - Callback when adding paid protect product successfully - * @param {Function} props.scanJustAdded - Callback when adding paid protect product was recently added * @returns {object} ConnectedPricingTable react component. */ -const ConnectedPricingTable = ( { onScanAdd, scanJustAdded } ) => { +const ConnectedPricingTable = ( { onScanAdd } ) => { const { handleRegisterSite, registrationError } = useConnection( { skipUserConnection: true, } ); - const [ protectFreeIsRegistering, setProtectFreeIsRegistering ] = useState( false ); + const { refreshPlan, refreshStatus } = useDispatch( STORE_ID ); + + const [ getProtectFreeButtonIsLoading, setGetProtectFreeButtonIsLoading ] = useState( false ); + const [ getScanButtonIsLoading, setGetScanButtonIsLoading ] = useState( false ); // Access paid protect product data const { jetpackScan } = useProtectData(); @@ -45,18 +49,22 @@ const ConnectedPricingTable = ( { onScanAdd, scanJustAdded } ) => { // Track free and paid click events const { recordEvent, recordEventHandler } = useAnalyticsTracks(); - const getScan = recordEventHandler( - 'jetpack_protect_pricing_table_get_scan_link_click', - onScanAdd - ); + const getScan = recordEventHandler( 'jetpack_protect_pricing_table_get_scan_link_click', () => { + setGetScanButtonIsLoading( true ); + onScanAdd(); + } ); const getProtectFree = useCallback( () => { recordEvent( 'jetpack_protect_connected_product_activated' ); - setProtectFreeIsRegistering( true ); + setGetProtectFreeButtonIsLoading( true ); handleRegisterSite() - .then( () => setProtectFreeIsRegistering( false ) ) - .then( refreshWaf ); - }, [ handleRegisterSite, recordEvent, refreshWaf ] ); + .then( () => setGetProtectFreeButtonIsLoading( false ) ) + .then( () => { + refreshPlan(); + refreshWaf(); + refreshStatus( true ); + } ); + }, [ handleRegisterSite, recordEvent, refreshWaf, refreshPlan, refreshStatus ] ); const args = { title: __( 'Stay one step ahead of threats', 'jetpack-protect' ), @@ -97,8 +105,8 @@ const ConnectedPricingTable = ( { onScanAdd, scanJustAdded } ) => { @@ -128,8 +136,8 @@ const ConnectedPricingTable = ( { onScanAdd, scanJustAdded } ) => { fullWidth variant="secondary" onClick={ getProtectFree } - isLoading={ protectFreeIsRegistering } - disabled={ protectFreeIsRegistering || scanJustAdded } + isLoading={ getProtectFreeButtonIsLoading } + disabled={ getProtectFreeButtonIsLoading || getScanButtonIsLoading } error={ registrationError ? __( 'An error occurred. Please try again.', 'jetpack-protect' ) From 79e814f11ec5a19dd801f511c03a2848ffccc22d Mon Sep 17 00:00:00 2001 From: Fernando Fernandes Date: Wed, 22 Feb 2023 17:11:53 -0300 Subject: [PATCH 18/38] Jetpack Social: add mastodon restrictions (#29034) * Add Mastodon restrictions and a global default The default restrictions is being used whenever there's no restriction set for such service in `restrictions.js`. As it was the case when testing this when `mastodon` was available in `enabledConnections`. Added some tests for such scenario as well. * Add changelog * Update package version --- ...e-jetpack-social-add-mastodon-restrictions | 4 ++ .../src/hooks/use-media-restrictions/index.js | 33 ++++++----- .../use-media-restrictions/restrictions.js | 33 +++++++++++ .../use-media-restrictions/test/index.test.js | 55 ++++++++++++++++++- 4 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 projects/js-packages/publicize-components/changelog/update-jetpack-social-add-mastodon-restrictions diff --git a/projects/js-packages/publicize-components/changelog/update-jetpack-social-add-mastodon-restrictions b/projects/js-packages/publicize-components/changelog/update-jetpack-social-add-mastodon-restrictions new file mode 100644 index 0000000000000..aba8c33b09058 --- /dev/null +++ b/projects/js-packages/publicize-components/changelog/update-jetpack-social-add-mastodon-restrictions @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Jetpack Social: Add Mastodon and default media upload restrictions diff --git a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/index.js b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/index.js index d6aee66af9f09..6d8feffd43e4d 100644 --- a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/index.js +++ b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/index.js @@ -1,14 +1,10 @@ import { useCallback, useMemo } from 'react'; -import { RESTRICTIONS } from './restrictions'; +import { DEFAULT_RESTRICTIONS, RESTRICTIONS, GLOBAL_MAX_SIZE } from './restrictions'; export const FILE_TYPE_ERROR = 'FILE_TYPE_ERROR'; export const FILE_SIZE_ERROR = 'FILE_SIZE_ERROR'; export const VIDEO_LENGTH_TOO_LONG_ERROR = 'VIDEO_LENGTH_TOO_LONG_ERROR'; export const VIDEO_LENGTH_TOO_SHORT_ERROR = 'VIDEO_LENGTH_TOO_SHORT_ERROR'; - -// Global max size: 100 GB; -const GLOBAL_MAX_SIZE = 100000; - /** * Checks whether a media is a video. * @@ -31,13 +27,12 @@ const reduceVideoLimits = ( prev, current ) => ( { const getVideoLimits = enabledConnections => enabledConnections - .map( connection => RESTRICTIONS[ connection.service_name ].video ) - .reduce( reduceVideoLimits, { - minSize: 0, - maxSize: GLOBAL_MAX_SIZE, - maxLength: GLOBAL_MAX_SIZE, - minLength: 0, - } ); + .map( connection => + RESTRICTIONS[ connection.service_name ] + ? RESTRICTIONS[ connection.service_name ].video + : DEFAULT_RESTRICTIONS.video + ) + .reduce( reduceVideoLimits ); /** * Returns the currently allowed media types @@ -46,12 +41,16 @@ const getVideoLimits = enabledConnections => * @returns {Array} Array of allowed types */ export const getAllowedMediaTypes = enabledConnections => { - const typeArrays = enabledConnections.map( - connection => RESTRICTIONS[ connection.service_name ].allowedMediaTypes + const typeArrays = enabledConnections.map( connection => + RESTRICTIONS[ connection.service_name ] + ? RESTRICTIONS[ connection.service_name ].allowedMediaTypes + : DEFAULT_RESTRICTIONS.allowedMediaTypes ); + if ( typeArrays.length === 0 ) { return []; } + return typeArrays.reduce( ( a, b ) => a.filter( c => b.includes( c ) ) ); // Intersection }; @@ -63,7 +62,11 @@ export const getAllowedMediaTypes = enabledConnections => { */ export default function useMediaRestrictions( enabledConnections ) { const maxImageSize = Math.min( - ...enabledConnections.map( connection => RESTRICTIONS[ connection.service_name ].image.maxSize ) + ...enabledConnections.map( connection => + RESTRICTIONS[ connection.service_name ] + ? RESTRICTIONS[ connection.service_name ].image.maxSize + : DEFAULT_RESTRICTIONS.image.maxSize + ) ); const [ videoLimits, allowedMediaTypes ] = useMemo( diff --git a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/restrictions.js b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/restrictions.js index be75d28a96b9c..1949bdff3db1f 100644 --- a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/restrictions.js +++ b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/restrictions.js @@ -46,6 +46,30 @@ const facebookVideoTypes = [ 'video/vob', 'video/wmv', ]; +const mastodonImageTypes = allowedImageTypes.concat( [ + 'image/gif', + 'image/heic', + 'image/heif', + 'image/webp', + 'image/avif', +] ); +const mastodonVideoTypes = [ 'video/webm', 'video/quicktime', 'video/ogg' ]; + +// Global max size: 100 GB; +export const GLOBAL_MAX_SIZE = 100000; + +export const DEFAULT_RESTRICTIONS = { + allowedMediaTypes: allowedImageTypes.concat( [ MP4, VIDEOPRESS, MOV ] ), + image: { + maxSize: 4, + }, + video: { + minLength: 0, + minSize: 0, + maxSize: GLOBAL_MAX_SIZE, + maxLength: GLOBAL_MAX_SIZE, + }, +}; export const RESTRICTIONS = { twitter: { @@ -90,4 +114,13 @@ export const RESTRICTIONS = { minLength: 3, }, }, + mastodon: { + allowedMediaTypes: mastodonImageTypes.concat( [ ...mastodonVideoTypes, MP4, VIDEOPRESS ] ), + image: { + maxSize: 10, + }, + video: { + maxSize: 40, + }, + }, }; diff --git a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/test/index.test.js b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/test/index.test.js index e021ee4a63876..c4e293c0af760 100644 --- a/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/test/index.test.js +++ b/projects/js-packages/publicize-components/src/hooks/use-media-restrictions/test/index.test.js @@ -20,8 +20,13 @@ const DUMMY_CONNECTIONS = [ { service_name: 'linkedin', }, + { + service_name: 'mastodon', + }, ]; +const UNKNOWN_CONNECTION = [ { service_name: 'unknown' } ]; + const INVALID_TYPES = [ 'imagejpg', 'image/tgif', 'video/mp5', '', null ]; const INVALID_LENGTH_VIDEOS = [ { mime: 'video/mp4', fileSize: 1000000, length: 2 }, // Too short video @@ -60,6 +65,54 @@ describe( 'useMediaRestrictions hook', () => { expect( linkedinMaxImageSize ).toBe( 20 ); } ); + test( 'Returns default video limits for unknown service', () => { + const { result } = renderHook( connections => useMediaRestrictions( connections ), { + initialProps: UNKNOWN_CONNECTION, + } ); + + const defaultVideoLimits = result.current.videoLimits; + + expect( defaultVideoLimits ).toStrictEqual( { + minLength: 0, + minSize: 0, + maxSize: 100000, + maxLength: 100000, + } ); + } ); + + test( 'Returns correct video limits when a service and an unknown service are defined', () => { + const { result } = renderHook( connections => useMediaRestrictions( connections ), { + initialProps: UNKNOWN_CONNECTION.concat( [ { service_name: 'linkedin' } ] ), + } ); + + const defaultVideoLimits = result.current.videoLimits; + + expect( defaultVideoLimits ).toStrictEqual( { + minSize: 0.075, + maxSize: 200, + maxLength: 600, + minLength: 3, + } ); + } ); + + test( 'Returns default maxImageSize for unknown service', () => { + const { result } = renderHook( connections => useMediaRestrictions( connections ), { + initialProps: UNKNOWN_CONNECTION, + } ); + + const defaultMaxImageSize = result.current.maxImageSize; + expect( defaultMaxImageSize ).toBe( 4 ); + } ); + + test( 'Returns correct maxImageSize when a service and an unknown service are defined', () => { + const { result } = renderHook( connections => useMediaRestrictions( connections ), { + initialProps: UNKNOWN_CONNECTION.concat( [ { service_name: 'linkedin' } ] ), + } ); + + const defaultMaxImageSize = result.current.maxImageSize; + expect( defaultMaxImageSize ).toBe( 4 ); + } ); + test( 'Video limits are calculated correctly', () => { const { result, rerender } = renderHook( connections => useMediaRestrictions( connections ), { initialProps: DUMMY_CONNECTIONS, @@ -72,7 +125,7 @@ describe( 'useMediaRestrictions hook', () => { expect( defaultVideoLimits ).toStrictEqual( { maxLength: 140, - maxSize: 200, + maxSize: 40, minLength: 3, minSize: 0.075, } ); From 391038a67765eacffd5c1747e3b59962e19ee709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Wed, 22 Feb 2023 18:28:56 -0300 Subject: [PATCH 19/38] VideoPress: add player-bridge. Update player loading state (#29057) * [not verified] do not load current bridge * [not verified] Player bridge: listening events * [not verified] build player-bridge * [not verified] expose player bridge URL * [not verified] listen player loading state. Update local state * [not verified] changelog * [not verified] fix versions * fix changelog typo --------- Co-authored-by: Douglas Henri --- .../update-videopress-add-player-bridge | 4 ++ projects/packages/videopress/package.json | 2 +- .../src/class-block-editor-extensions.php | 1 + .../videopress/src/class-package-version.php | 2 +- .../components/videopress-player/index.js | 32 +++++----- .../src/client/block-editor/global.d.ts | 1 + .../src/client/lib/player-bridge/Readme.md | 3 + .../src/client/lib/player-bridge/index.ts | 60 +++++++++++++++++++ .../packages/videopress/webpack.config.js | 2 + 9 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 projects/packages/videopress/changelog/update-videopress-add-player-bridge create mode 100644 projects/packages/videopress/src/client/lib/player-bridge/Readme.md create mode 100644 projects/packages/videopress/src/client/lib/player-bridge/index.ts diff --git a/projects/packages/videopress/changelog/update-videopress-add-player-bridge b/projects/packages/videopress/changelog/update-videopress-add-player-bridge new file mode 100644 index 0000000000000..31c23ed8706a7 --- /dev/null +++ b/projects/packages/videopress/changelog/update-videopress-add-player-bridge @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +VideoPress: add player-bridge. Update player loading state diff --git a/projects/packages/videopress/package.json b/projects/packages/videopress/package.json index 580838b696c48..ffcfc7de2a2e3 100644 --- a/projects/packages/videopress/package.json +++ b/projects/packages/videopress/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-videopress", - "version": "0.10.12", + "version": "0.10.13-alpha", "description": "VideoPress package", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/videopress/#readme", "bugs": { diff --git a/projects/packages/videopress/src/class-block-editor-extensions.php b/projects/packages/videopress/src/class-block-editor-extensions.php index 3d66a5cc9c041..4f1adb671199a 100644 --- a/projects/packages/videopress/src/class-block-editor-extensions.php +++ b/projects/packages/videopress/src/class-block-editor-extensions.php @@ -160,6 +160,7 @@ public static function enqueue_extensions() { 'isVideoPressModuleActive' => Status::is_jetpack_plugin_and_videopress_module_active(), 'isStandaloneActive' => Status::is_standalone_plugin_active(), 'imagesURLBase' => plugin_dir_url( __DIR__ ) . 'build/images/', + 'playerBridgeUrl' => plugins_url( '../build/lib/player-bridge.js', __FILE__ ), ); // Expose initital state of site connection diff --git a/projects/packages/videopress/src/class-package-version.php b/projects/packages/videopress/src/class-package-version.php index 7d2ee31135bac..9c2b068283081 100644 --- a/projects/packages/videopress/src/class-package-version.php +++ b/projects/packages/videopress/src/class-package-version.php @@ -11,7 +11,7 @@ * The Package_Version class. */ class Package_Version { - const PACKAGE_VERSION = '0.10.12'; + const PACKAGE_VERSION = '0.10.13-alpha'; const PACKAGE_SLUG = 'videopress'; diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/components/videopress-player/index.js b/projects/packages/videopress/src/client/block-editor/blocks/video/components/videopress-player/index.js index f0f9f7e0a1119..35674411e3e37 100644 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/components/videopress-player/index.js +++ b/projects/packages/videopress/src/client/block-editor/blocks/video/components/videopress-player/index.js @@ -6,10 +6,6 @@ import { ResizableBox, SandBox } from '@wordpress/components'; import { useCallback, useRef, useEffect, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import debugFactory from 'debug'; -/** - * Internal dependencies - */ -import vpBlockBridge from '../../scripts/vp-block-bridge'; // Global scripts array to be run in the Sandbox context. const globalScripts = []; @@ -34,12 +30,13 @@ if ( window.videopressAjax ) { ); } +if ( window?.videoPressEditorState?.playerBridgeUrl ) { + globalScripts.push( window.videoPressEditorState.playerBridgeUrl ); +} + // Define a debug instance for block bridge. window.debugBridgeInstance = debugFactory( 'jetpack:vp-block:bridge' ); -// Load VideoPressBlock bridge script. -globalScripts.push( vpBlockBridge ); - /** * VideoPlayer react component * @@ -140,24 +137,29 @@ export default function VideoPressPlayer( { /* * Callback state handler for the video player - * tied to the `onVideoPressLoadingState` event, + * tied to the `message` event, * provided by the videopress player through the bridge. */ - const onVideoLoadingStateHandler = useCallback( ( { detail } ) => { - setIsVideoPlayerLoaded( detail?.state === 'loaded' ); + const onVideoLoadingStateHandler = useCallback( ev => { + const eventName = ev?.data?.event; + if ( ! eventName || eventName !== 'videopress_loading_state' ) { + return; + } + + const playerLoadingState = ev?.data?.state; + setIsVideoPlayerLoaded( playerLoadingState === 'loaded' ); }, [] ); - // Listen to the `onVideoPressLoadingState` event. + // Listen to the `message` event. useEffect( () => { if ( ! window ) { return; } - window.addEventListener( 'onVideoPressLoadingState', onVideoLoadingStateHandler ); + window.addEventListener( 'message', onVideoLoadingStateHandler ); - return () => - window?.removeEventListener( 'onVideoPressLoadingState', onVideoLoadingStateHandler ); - }, [ onVideoLoadingStateHandler, window, html ] ); + return () => window?.removeEventListener( 'message', onVideoLoadingStateHandler ); + }, [ onVideoLoadingStateHandler ] ); useEffect( () => { if ( isRequestingEmbedPreview ) { diff --git a/projects/packages/videopress/src/client/block-editor/global.d.ts b/projects/packages/videopress/src/client/block-editor/global.d.ts index bb50c96c52f82..fa419008983c2 100644 --- a/projects/packages/videopress/src/client/block-editor/global.d.ts +++ b/projects/packages/videopress/src/client/block-editor/global.d.ts @@ -12,6 +12,7 @@ declare global { isStandaloneActive: '' | '1'; jetpackVideoPressSettingUrl: string; imagesURLBase: string; + playerBridgeUrl: string; }; JP_CONNECTION_INITIAL_STATE: { diff --git a/projects/packages/videopress/src/client/lib/player-bridge/Readme.md b/projects/packages/videopress/src/client/lib/player-bridge/Readme.md new file mode 100644 index 0000000000000..8ab661f4dfd93 --- /dev/null +++ b/projects/packages/videopress/src/client/lib/player-bridge/Readme.md @@ -0,0 +1,3 @@ +# Player Bridge + +Bridge to communicate player with the block editor. \ No newline at end of file diff --git a/projects/packages/videopress/src/client/lib/player-bridge/index.ts b/projects/packages/videopress/src/client/lib/player-bridge/index.ts new file mode 100644 index 0000000000000..f3e173b3cb021 --- /dev/null +++ b/projects/packages/videopress/src/client/lib/player-bridge/index.ts @@ -0,0 +1,60 @@ +/** + * Types + */ +import type { VideoGUID } from '../../block-editor/blocks/video/types'; + +type Origin = 'https://videopress.com' | 'https://video.wordpress.com'; + +const VIDEOPRESS_ALLOWED_LISTENING_EVENTS = [ + 'videopress_playing', + 'videopress_pause', + 'videopress_seeking', + 'videopress_resize', + 'videopress_volumechange', + 'videopress_ended', + 'videopress_timeupdate', + 'videopress_durationchange', + 'videopress_progress', + 'videopress_loading_state', + 'videopress_toggle_fullscreen', +] as const; + +type PlayerBrigeEventProps = { + event: typeof VIDEOPRESS_ALLOWED_LISTENING_EVENTS[ number ]; + id: VideoGUID; + origin: Origin; +}; + +/** + * Function handler to dialog between + * the client (player) and the app (editor) + * + * @param {object} event - The event object + */ +export async function playerBridgeHandler( + event: MessageEvent< PlayerBrigeEventProps > +): Promise< void > { + const { data = { event: null } } = event || {}; + const { event: eventName } = data; + + // Propagate only allowed events. + if ( ! VIDEOPRESS_ALLOWED_LISTENING_EVENTS.includes( eventName ) ) { + return; + } + + // Propagate only allowed origins. + const allowed_origins: Array< Origin > = [ + 'https://videopress.com', + 'https://video.wordpress.com', + ]; + + if ( -1 === allowed_origins.indexOf( event.origin as Origin ) ) { + return; + } + + window.top.postMessage( event.data, '*' ); +} + +( function () { + window.addEventListener( 'message', playerBridgeHandler ); +} )(); diff --git a/projects/packages/videopress/webpack.config.js b/projects/packages/videopress/webpack.config.js index 1919f4b3cfebd..683bce8bf5c3d 100644 --- a/projects/packages/videopress/webpack.config.js +++ b/projects/packages/videopress/webpack.config.js @@ -10,6 +10,8 @@ module.exports = [ 'block-editor/blocks/video/view': './src/client/block-editor/blocks/video/view.ts', 'lib/token-bridge': './src/client/lib/token-bridge/index.ts', + 'lib/player-bridge': './src/client/lib/player-bridge/index.ts', + 'lib/videopress-token-bridge': './src/client/lib/videopress-token-bridge.js', // VideoPress dashboard page From 5584a646be2d9e9f17d6231178e8e871b757716a Mon Sep 17 00:00:00 2001 From: gogdzl <37049295+gogdzl@users.noreply.github.com> Date: Wed, 22 Feb 2023 20:13:04 -0300 Subject: [PATCH 20/38] CRM: Client Portal's menu has a dark gray background in the Twenty Seventeen theme (#29052) * Fix wrong background in twenty seventeen theme * changelog --- .../crm/changelog/crm-client-portal-grey-menu-in-2017-theme | 4 ++++ projects/plugins/crm/sass/_ZeroBSCRM.ThemeCompat.scss | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 projects/plugins/crm/changelog/crm-client-portal-grey-menu-in-2017-theme diff --git a/projects/plugins/crm/changelog/crm-client-portal-grey-menu-in-2017-theme b/projects/plugins/crm/changelog/crm-client-portal-grey-menu-in-2017-theme new file mode 100644 index 0000000000000..b3284aed8622a --- /dev/null +++ b/projects/plugins/crm/changelog/crm-client-portal-grey-menu-in-2017-theme @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +In Client Portal, the background for the menu in the Twenty Seventeen theme is no longer dark gray diff --git a/projects/plugins/crm/sass/_ZeroBSCRM.ThemeCompat.scss b/projects/plugins/crm/sass/_ZeroBSCRM.ThemeCompat.scss index 0e432bf8fee02..bee79622fe916 100644 --- a/projects/plugins/crm/sass/_ZeroBSCRM.ThemeCompat.scss +++ b/projects/plugins/crm/sass/_ZeroBSCRM.ThemeCompat.scss @@ -31,7 +31,6 @@ } #zbs-nav-tabs { - background: #22272d !important; li a i.fa { margin-right: 0.6em; } From ca8ee68a4b724adbc018e2f2969fa1dac514f770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kallehauge?= <3846700+kallehauge@users.noreply.github.com> Date: Thu, 23 Feb 2023 00:43:06 +0100 Subject: [PATCH 21/38] CRM: Remove assumption that conditional avatar input exists (#29086) * CRM: Add defensive code for edit contact profile picture fields We have to do this because executing the logic on null values causes a breaking error that causes the next code not to function. * changelog --- .../fix-crm-assume-conditional-avatar-input-exist | 4 ++++ projects/plugins/crm/js/ZeroBSCRM.admin.editview.js | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 projects/plugins/crm/changelog/fix-crm-assume-conditional-avatar-input-exist diff --git a/projects/plugins/crm/changelog/fix-crm-assume-conditional-avatar-input-exist b/projects/plugins/crm/changelog/fix-crm-assume-conditional-avatar-input-exist new file mode 100644 index 0000000000000..2ada8c91473dc --- /dev/null +++ b/projects/plugins/crm/changelog/fix-crm-assume-conditional-avatar-input-exist @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Contact Edit: prevent JS error when custom avatars are not enabled diff --git a/projects/plugins/crm/js/ZeroBSCRM.admin.editview.js b/projects/plugins/crm/js/ZeroBSCRM.admin.editview.js index 7f4a2682467f7..735bc4a263976 100644 --- a/projects/plugins/crm/js/ZeroBSCRM.admin.editview.js +++ b/projects/plugins/crm/js/ZeroBSCRM.admin.editview.js @@ -167,10 +167,16 @@ function zeroBSCRMJS_hideNotificationsAfter() { */ function zeroBSCRMJS_editContactInit() { var picture_file_input = document.getElementById( 'zbsc_profile-picture-file' ); - picture_file_input.addEventListener( 'change', jpcrm_customer_profile_picture_on_change ); + + if ( picture_file_input ) { + picture_file_input.addEventListener( 'change', jpcrm_customer_profile_picture_on_change ); + } var picture_file_remove_btn = document.getElementById( 'zbsc_remove-profile-picture-button' ); - picture_file_remove_btn.addEventListener( 'click', jpcrm_customer_remove_profile_picture ); + + if ( picture_file_remove_btn ) { + picture_file_remove_btn.addEventListener( 'click', jpcrm_customer_remove_profile_picture ); + } jQuery( '.send-sms-none' ).on( 'click', function ( e ) { swal( @@ -493,7 +499,7 @@ function zeroBSCRMJS_events_bindCompanyLinkIf() { if ( typeof module !== 'undefined' ) { - module.exports = { zbscrmjs_events_setContact, zbscrmjs_events_setCompany, zbscrmjs_events_changeContact, + module.exports = { zbscrmjs_events_setContact, zbscrmjs_events_setCompany, zbscrmjs_events_changeContact, zbscrmjs_events_changeCompany, zeroBSCRMJS_initEditView, zeroBSCRMJS_preLeaveEditView, zeroBSCRMJS_editViewLang, zeroBSCRMJS_drawEditView, zeroBSCRMJS_editViewBinds, zeroBSCRMJS_hideNotificationsAfter, zeroBSCRMJS_editContactInit, jpcrm_customer_profile_picture_on_change, jpcrm_customer_remove_profile_picture, From 6ce2504ce505ef1ff1a4e8cfd848af906bb4910c Mon Sep 17 00:00:00 2001 From: Vishnu Gopal <533+vishnugopal@users.noreply.github.com> Date: Thu, 23 Feb 2023 10:05:46 +0530 Subject: [PATCH 22/38] Fix viewer lists for List User API (#29068) --- .../jetpack/changelog/fix-users-include-viewer | 4 ++++ .../class.wpcom-json-api-list-users-endpoint.php | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/fix-users-include-viewer diff --git a/projects/plugins/jetpack/changelog/fix-users-include-viewer b/projects/plugins/jetpack/changelog/fix-users-include-viewer new file mode 100644 index 0000000000000..ae1ba2e721374 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-users-include-viewer @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +Fix a bug in list user endpoint when include_viewers is true diff --git a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php index d0eadbd256607..b091e17d1ea2d 100644 --- a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php +++ b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php @@ -33,7 +33,7 @@ 'post_count' => 'Order by number of posts published.', ), 'authors_only' => '(bool) Set to true to fetch authors only', - 'include_viewers' => '(bool) Set to true to include viewers for Simple sites', + 'include_viewers' => '(bool) Set to true to include viewers for Simple sites. When you pass in this parameter, order, order_by and search_columns are ignored. Currently, `search` is limited to the first page of results.', 'type' => "(string) Specify the post type to query authors for. Only works when combined with the `authors_only` flag. Defaults to 'post'. Post types besides post and page need to be whitelisted using the rest_api_allowed_post_types filter.", 'search' => '(string) Find matching users.', 'search_columns' => "(array) Specify which columns to check for matching users. Can be any of 'ID', 'user_login', 'user_email', 'user_url', 'user_nicename', and 'display_name'. Only works when combined with `search` parameter.", @@ -192,8 +192,17 @@ function ( $viewer ) use ( $args ) { foreach ( array_keys( $this->response_format ) as $key ) { switch ( $key ) { case 'found': - $user_count = (int) $user_query->get_total(); - $viewer_count = $include_viewers ? count( $viewers ) : 0; + $user_count = (int) $user_query->get_total(); + + $viewer_count = 0; + if ( $include_viewers ) { + if ( empty( $args['search'] ) ) { + $viewer_count = (int) get_count_private_blog_users( $blog_id ); + } else { + $viewer_count = count( $viewers ); + } + } + $return[ $key ] = $user_count + $viewer_count; break; case 'users': From 551ffab90f19f39d82b1cdc217a2a603ae102118 Mon Sep 17 00:00:00 2001 From: Kevin L <40267301+a8ck3n@users.noreply.github.com> Date: Thu, 23 Feb 2023 15:58:31 +0800 Subject: [PATCH 23/38] Odyssey Stats: Add new upgrade nudge to old stats page (#28828) --- .../changelog/odyssey-stats-new-upgrade-nudge | 4 + projects/plugins/jetpack/modules/stats.php | 274 +++++++++++++++++- 2 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/odyssey-stats-new-upgrade-nudge diff --git a/projects/plugins/jetpack/changelog/odyssey-stats-new-upgrade-nudge b/projects/plugins/jetpack/changelog/odyssey-stats-new-upgrade-nudge new file mode 100644 index 0000000000000..f8f698ec0c935 --- /dev/null +++ b/projects/plugins/jetpack/changelog/odyssey-stats-new-upgrade-nudge @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Add an upgrade nudge for Odyssey Stats diff --git a/projects/plugins/jetpack/modules/stats.php b/projects/plugins/jetpack/modules/stats.php index 0178b402d3083..8b84ffc357589 100644 --- a/projects/plugins/jetpack/modules/stats.php +++ b/projects/plugins/jetpack/modules/stats.php @@ -22,7 +22,9 @@ use Automattic\Jetpack\Stats\Options as Stats_Options; use Automattic\Jetpack\Stats\Tracking_Pixel as Stats_Tracking_Pixel; use Automattic\Jetpack\Stats\XMLRPC_Provider as Stats_XMLRPC; -use Automattic\Jetpack\Stats_Admin\Dashboard as StatsDashboard; +use Automattic\Jetpack\Stats_Admin\Dashboard as Stats_Dashboard; +use Automattic\Jetpack\Stats_Admin\Main as Stats_Main; +use Automattic\Jetpack\Stats_Admin\Notices as Stats_Notices; use Automattic\Jetpack\Status\Host; use Automattic\Jetpack\Tracking; @@ -32,6 +34,12 @@ define( 'STATS_DASHBOARD_SERVER', 'dashboard.wordpress.com' ); +/** + * Stats content marker. + * Used to test for content vs script when parsing server-generated HTML. + */ +const STATS_CONTENT_MARKER = '
'; + add_action( 'jetpack_modules_loaded', 'stats_load' ); /** @@ -284,6 +292,13 @@ function stats_admin_menu() { } } + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( ! ( new Host() )->is_woa_site() && isset( $_GET['enable_new_stats'] ) && '1' === $_GET['enable_new_stats'] ) { + // Passing true enables Odyssey Stats. + // We're ignorning the return value for now. + Stats_Main::update_new_stats_status( true ); + } + // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ( new Host() )->is_woa_site() || ! Stats_Options::get_option( 'enable_odyssey_stats' ) || isset( $_GET['noheader'] ) ) { // Show old Jetpack Stats interface for: @@ -294,7 +309,7 @@ function stats_admin_menu() { add_action( "load-$hook", 'stats_reports_load' ); } else { // Enable the new Odyssey Stats experience. - $stats_dashboard = new StatsDashboard(); + $stats_dashboard = new Stats_Dashboard(); $hook = add_submenu_page( 'jetpack', __( 'Stats', 'jetpack' ), __( 'Stats', 'jetpack' ), 'view_stats', 'stats', array( $stats_dashboard, 'render' ) ); add_action( "load-$hook", array( $stats_dashboard, 'admin_init' ) ); } @@ -339,9 +354,43 @@ function stats_reports_load() { } elseif ( ! isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended // Normal page load. Load page content via JS. add_action( 'admin_print_footer_scripts', 'stats_js_load_page_via_ajax' ); + add_action( 'admin_print_footer_scripts', 'stats_script_dismiss_nudge_handler' ); } } +/** + * JavaScript to dismiss the Odyssey nudge. + * + * @access public + * @return void + */ +function stats_script_dismiss_nudge_handler() { + ?> + + get_notices_to_show(); + return isset( $stats_notices[ Stats_Notices::OPT_IN_NEW_STATS_NOTICE_ID ] ) + && $stats_notices[ Stats_Notices::OPT_IN_NEW_STATS_NOTICE_ID ]; +} + +/** + * Legacy Stats: Print the Odyssey upgrade nudge. + * + * @access public + * @param mixed $html HTML. + * @return void + */ +function stats_print_odyssey_nudge( $html ) { + if ( ! stats_should_show_odyssey_nudge() ) { + return; + } + $pos = strpos( $html, STATS_CONTENT_MARKER ); + if ( $pos === false ) { + return; + } + $learn_url = Redirect::get_url( 'jetpack-stats-learn-more' ); + $redirect_url = admin_url( 'admin.php?page=stats&enable_new_stats=1' ); + ?> + +
+
+
+ +
+ + +
+
+ +
+ Date: Thu, 23 Feb 2023 09:56:54 +0100 Subject: [PATCH 24/38] Sharing: fix display of icon-only display option (#29090) --- projects/plugins/jetpack/changelog/fix-sharing-icon-margin | 4 ++++ projects/plugins/jetpack/modules/sharedaddy/sharing.css | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 projects/plugins/jetpack/changelog/fix-sharing-icon-margin diff --git a/projects/plugins/jetpack/changelog/fix-sharing-icon-margin b/projects/plugins/jetpack/changelog/fix-sharing-icon-margin new file mode 100644 index 0000000000000..d5e866bdf6652 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-sharing-icon-margin @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +Sharing: fix display issues when choosing the Icon-only option. diff --git a/projects/plugins/jetpack/modules/sharedaddy/sharing.css b/projects/plugins/jetpack/modules/sharedaddy/sharing.css index 306811670b377..3cbbab100aebc 100644 --- a/projects/plugins/jetpack/modules/sharedaddy/sharing.css +++ b/projects/plugins/jetpack/modules/sharedaddy/sharing.css @@ -128,6 +128,12 @@ body.highlander-dark h3.sd-title:before { margin-left: 6px; } +/* Icon Only */ +.sd-social-icon .sd-content ul li a.sd-button>span { + margin-left: 0; +} + +/* Text Only */ .sd-social-text .sd-content ul li a.sd-button span { margin-left: 3px; } From da40feddf6cf85ab445389a51f2408e2f42c4d0d Mon Sep 17 00:00:00 2001 From: Piotr Stankowski <6437642+trakos@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:15:54 +0100 Subject: [PATCH 25/38] Jetpack Search: fix CSS for wc products (#29110) This change fixes CSS classes for WooCommerce product results. --- projects/packages/search/changelog/fix-search-products-css | 4 ++++ .../src/instant-search/components/search-result-product.jsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 projects/packages/search/changelog/fix-search-products-css diff --git a/projects/packages/search/changelog/fix-search-products-css b/projects/packages/search/changelog/fix-search-products-css new file mode 100644 index 0000000000000..5271a3c7ab636 --- /dev/null +++ b/projects/packages/search/changelog/fix-search-products-css @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fix CSS for products in search results diff --git a/projects/packages/search/src/instant-search/components/search-result-product.jsx b/projects/packages/search/src/instant-search/components/search-result-product.jsx index 6a9281f4e4502..2eab2f2fe604c 100644 --- a/projects/packages/search/src/instant-search/components/search-result-product.jsx +++ b/projects/packages/search/src/instant-search/components/search-result-product.jsx @@ -56,7 +56,7 @@ class SearchResultProduct extends Component { getCategories() .map( cat => 'jetpack-instant-search__search-result-category--' + cleanForSlug( cat ) ) .join( ' ' ), - ].join( '' ) } + ].join( ' ' ) } > Date: Thu, 23 Feb 2023 09:02:39 -0300 Subject: [PATCH 26/38] VideoPress: removed deprecated wp-block-bridge lib (#29107) * remove deprecated vp-block-bridge script * changelog --- ...deopress-remove-deprecated-vp-block-bridge | 4 + .../video/scripts/vp-block-bridge/Readme.md | 137 ------------------ .../video/scripts/vp-block-bridge/index.js | 136 ----------------- 3 files changed, 4 insertions(+), 273 deletions(-) create mode 100644 projects/packages/videopress/changelog/update-videopress-remove-deprecated-vp-block-bridge delete mode 100644 projects/packages/videopress/src/client/block-editor/blocks/video/scripts/vp-block-bridge/Readme.md delete mode 100644 projects/packages/videopress/src/client/block-editor/blocks/video/scripts/vp-block-bridge/index.js diff --git a/projects/packages/videopress/changelog/update-videopress-remove-deprecated-vp-block-bridge b/projects/packages/videopress/changelog/update-videopress-remove-deprecated-vp-block-bridge new file mode 100644 index 0000000000000..a89cea490f4db --- /dev/null +++ b/projects/packages/videopress/changelog/update-videopress-remove-deprecated-vp-block-bridge @@ -0,0 +1,4 @@ +Significance: patch +Type: removed + +VideoPress: removed deprecated wp-block-bridge lib diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/scripts/vp-block-bridge/Readme.md b/projects/packages/videopress/src/client/block-editor/blocks/video/scripts/vp-block-bridge/Readme.md deleted file mode 100644 index bd80ea01c3f07..0000000000000 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/scripts/vp-block-bridge/Readme.md +++ /dev/null @@ -1,137 +0,0 @@ -# VideoPress Block bridge - -This library essentially provides a way to connect events and methods defined by the Video Press player API from/to the VideoPress block. - -## Background - -### How the player is rendered - -From the PoV of the edit component of the VPBlock, the first step to implement the player is getting the raw HTML code provided by the VideoPress server via the `getEmbedPreview()` embed core-data selector. -It's HTML code that basically defines an iFrame element with relevant attributes about the video. - -```jsx -const preview = useSelect( - select => select( coreStore ).getEmbedPreview( videoPressUrl ), - [ videoPressUrl ] -); - -const { html } = preview; -``` - -Once the code gets, it's rendered via creating a Sandbox instance where the code to process is defined via the `html` property mentioned above: - -```jsx - -``` - -The most relevant thing to raise up here is the fact the video player is rendered into two nested iFrames levels. The component does one, and the other is provided by the VideoPress player API. - -### Connect the block with the player - -The VideoPress player API triggers events to its `window` object and its `window parent` object when they are different to communicate the video with the consumer, in our case, the VideoPress block. - -However, it doesn't connect with the `top` object reference meaning that if the player is nested via iFrames in more than two levels, it won't be possible to connect with the player from the higher level. - -Funnily, it happens in the our scenario of the block editor: It renders the raw HTML provided by VideoPress (which is an iFrame) via the Sandbox (which uses an iFrame too), producing the structure of the following elements: - -```html -
- - -
-``` - -### The Bridge - -This bridge script listens and re-emits the events, communicating the VideoPress player with the VideoPress Block. It's sent to the child iFrame through the `scripts` property of the Sandbox component, which takes over to run it in that context. - -```jsx -import { SandBox } from '@wordpress/components'; -import vpBlockBridge from './scripts/vp-block-bridge'; - - -``` - -## Bridge API - -```jsx -import vpBlockBridge from './scripts/vp-block-bridge'; - -function VPBlockEdit() { - useEffect( () => { - window.addEventListener( 'onVideoPressPlaying', event => { - console.log( 'video is playing...' ); - } ); - }, [] ); - - return ( - - ); -} -``` - -### Events - -The bridge triggers the following custom events: - -#### onVideoPressPlaying - -#### onVideoPressPause - -#### onVideoPressSeeking - -#### onVideoPressResize - -#### onVideoPressVolumeChange - -#### onVideoPressEnded - -#### onVideoPressTimeUpdate - -#### onVideoPressDurationChange - -#### onVideoPressProgress - -#### onVideoPressLoadingState - -### Actions - -#### vpBlockActionPlay - -#### vpBlockActionPause - -#### vpBlockActionPause - -### Actions - -```es6 -// Pause the video after ten seconds. -setTimeout( () => { - iFrameDom?.contentWindow?.postMessage( { - event: 'vpBlockActionPause', - } ); -}, 10000 ); -``` - -#### vpBlockActionPlay - -#### vpBlockActionPause - -#### vpBlockActionSetCurrentTim - -## Debug - -To debug the bridge actions you'd like to create a debug instance stored in the `debugBridgeInstance` window property: - -```es6 -import debugFactory from 'debug'; - -window.debugBridgeInstance = debugFactory( 'jetpack:vp-block:bridge' ); -``` \ No newline at end of file diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/scripts/vp-block-bridge/index.js b/projects/packages/videopress/src/client/block-editor/blocks/video/scripts/vp-block-bridge/index.js deleted file mode 100644 index 1911503557d77..0000000000000 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/scripts/vp-block-bridge/index.js +++ /dev/null @@ -1,136 +0,0 @@ -const rawScript = ` - if ( ! window?.debug ) { - window.debug = window.parent?.debugBridgeInstance ?? ( () => {} ); - } - - function initWPBlockBridge() { - debug( '🌉 🛠 building the bridge' ); - - const videoPressIFrame = document.querySelector('iframe'); - if ( ! videoPressIFrame?.contentWindow ) { - return; - } - - const videoPressWindow = videoPressIFrame.contentWindow; - - // Allowed events emitted by the videopress API. - const videoPressEventsMap = { - 'videopress_playing': { - name: 'onVideoPressPlaying', - type: 'event', - }, - 'videopress_pause': { - name: 'onVideoPressPause', - type: 'event', - }, - 'videopress_seeking': { - name: 'onVideoPressSeeking', - type: 'event', - }, - 'videopress_resize': { - name: 'onVideoPressResize', - type: 'event', - }, - 'videopress_volumechange': { - name: 'onVideoPressVolumeChange', - type: 'event', - }, - 'videopress_ended': { - name: 'onVideoPressEnded', - type: 'event', - }, - 'videopress_timeupdate': { - name: 'onVideoPressTimeUpdate', - type: 'event', - }, - 'videopress_durationchange': { - name: 'onVideoPressDurationChange', - type: 'event', - }, - 'videopress_progress': { - name: 'onVideoPressProgress', - type: 'event', - }, - 'videopress_loading_state': { - name: 'onVideoPressLoadingState', - type: 'event', - }, - videopress_toggle_fullscreen: { - name: 'onVideoPressToggleFullscreen', - type: 'event', - }, - 'vpBlockActionPlay': { - name: 'vpBlockActionPlay', - type: 'action', - videoPressAction: 'videopress_action_play', - }, - 'vpBlockActionPause': { - name: 'vpBlockActionPause', - type: 'action', - videoPressAction: 'videopress_action_pause', - }, - 'vpBlockActionSetCurrentTime': { - name: 'vpBlockActionPause', - type: 'action', - videoPressAction: 'videopress_action_set_currenttime', - }, - }; - - const allowedVideoPressEvents = Object.keys( videoPressEventsMap ); - - window.addEventListener( 'message', ( ev ) => { - const { data } = ev; - const eventName = data.event; - if ( ! allowedVideoPressEvents.includes( eventName ) ) { - return; - } - - // Rename event with the 'onVideoPress' prefix. - const vpEvent = videoPressEventsMap[ eventName ]; - const { name: vpEventName, type: vpEventType, videoPressAction } = vpEvent; - - // Dispatch event to top when it's an event - if ( vpEventType === 'event' ) { - // It preferrs to use the guid instead of the id. - const guid = data.id; - const originalEventName = data.event; - - // clean event data object - delete data.event; - delete data.id; - - // Emite custom event with the event data. - const videoPressBlockEvent = new CustomEvent( vpEventName, { - detail: { - ...data, - originalEventName, - guid, - }, - } ); - - debug( '🌉 %o [%s] ➜ %o', originalEventName, guid, vpEventName ); - - // Dispatch custom event in iFrame window... - window.dispatchEvent( videoPressBlockEvent ); - - // ...and also dipatch to the parent window, - // in case it exists. - if ( window?.parent && window.parent !== window ) { - window.parent.dispatchEvent( videoPressBlockEvent ); - } - } - - if ( vpEventType === 'action' ) { - // Overwrite event from -> to - data.event = videoPressAction; - - debug( '🌉 recieve %o -> dispatching %o [%o]', eventName, videoPressAction, data ); - videoPressWindow.postMessage( data, '*' ); - } - } ); - } - - initWPBlockBridge(); -`; - -export default URL.createObjectURL( new Blob( [ rawScript ], { type: 'text/javascript' } ) ); From 10ebff288bc7324dfc0789b4a253acf9b340cf0d Mon Sep 17 00:00:00 2001 From: Daniel Post Date: Thu, 23 Feb 2023 14:35:20 +0100 Subject: [PATCH 27/38] Jetpack Social: Add sidebar panel for Social Image Generator (#28737) * Add empty panel * changelog * [not verified] Add Custom Text and image picker barebone * Add hook to retrieve and update image generator meta * changelog * Add media picker * Fixup versions * Clean up meta handling * Simplify function * Fixup versions * Use SIG feature flag * Add to Jetpack sidebar --------- Co-authored-by: Gergely Juhasz --- ...tpack-social-image-generator-sidebar-panel | 4 + .../js-packages/publicize-components/index.js | 1 + .../social-image-generator/panel/index.js | 98 +++++++++++++++++++ .../use-image-generator-config/README.md | 20 ++++ .../hooks/use-image-generator-config/index.js | 63 ++++++++++++ .../src/store/selectors.js | 9 ++ ...tpack-social-image-generator-sidebar-panel | 4 + .../publicize/src/class-publicize-base.php | 20 +++- .../post-fields-publicize-connections.php | 1 + ...tpack-social-image-generator-sidebar-panel | 4 + ...ack-social-image-generator-sidebar-panel#2 | 5 + ...ack-social-image-generator-sidebar-panel#3 | 5 + .../extensions/plugins/publicize/index.js | 15 +++ ...tpack-social-image-generator-sidebar-panel | 4 + ...ack-social-image-generator-sidebar-panel#2 | 5 + ...ack-social-image-generator-sidebar-panel#3 | 5 + projects/plugins/social/src/js/editor.js | 19 +++- 17 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 projects/js-packages/publicize-components/changelog/add-jetpack-social-image-generator-sidebar-panel create mode 100644 projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js create mode 100644 projects/js-packages/publicize-components/src/hooks/use-image-generator-config/README.md create mode 100644 projects/js-packages/publicize-components/src/hooks/use-image-generator-config/index.js create mode 100644 projects/packages/publicize/changelog/add-jetpack-social-image-generator-sidebar-panel create mode 100644 projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel create mode 100644 projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel#2 create mode 100644 projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel#3 create mode 100644 projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel create mode 100644 projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel#2 create mode 100644 projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel#3 diff --git a/projects/js-packages/publicize-components/changelog/add-jetpack-social-image-generator-sidebar-panel b/projects/js-packages/publicize-components/changelog/add-jetpack-social-image-generator-sidebar-panel new file mode 100644 index 0000000000000..0681a320849a2 --- /dev/null +++ b/projects/js-packages/publicize-components/changelog/add-jetpack-social-image-generator-sidebar-panel @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Add Social Image Generator editor panel to post sidebar diff --git a/projects/js-packages/publicize-components/index.js b/projects/js-packages/publicize-components/index.js index df6cf145c6cb1..e31eda456c960 100644 --- a/projects/js-packages/publicize-components/index.js +++ b/projects/js-packages/publicize-components/index.js @@ -10,6 +10,7 @@ export { default as TwitterThreadListener } from './src/components/twitter'; export { default as TwitterOptions } from './src/components/twitter/options'; export { default as SocialPreviewsModal } from './src/components/social-previews/modal'; export { default as SocialPreviewsPanel } from './src/components/social-previews/panel'; +export { default as SocialImageGeneratorPanel } from './src/components/social-image-generator/panel'; export { default as PublicizePanel } from './src/components/panel'; export { default as ReviewPrompt } from './src/components/review-prompt'; diff --git a/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js b/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js new file mode 100644 index 0000000000000..8a96094efc816 --- /dev/null +++ b/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js @@ -0,0 +1,98 @@ +import { PanelBody, ToggleControl, TextControl, SelectControl } from '@wordpress/components'; +import { useCallback, Fragment } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import useImageGeneratorConfig from '../../../hooks/use-image-generator-config'; +import useMediaDetails from '../../../hooks/use-media-details'; +import MediaPicker from '../../media-picker'; + +const ALLOWED_MEDIA_TYPES = [ 'image/jpeg', 'image/png' ]; +const ADD_MEDIA_LABEL = __( 'Choose Image', 'jetpack' ); + +const SocialImageGeneratorPanel = ( { prePublish = false } ) => { + const PanelWrapper = prePublish ? Fragment : PanelBody; + const wrapperProps = prePublish ? {} : { title: __( 'Social Image Generator', 'jetpack' ) }; + const { + isEnabled, + setIsEnabled, + customText, + setCustomText, + imageType, + setImageType, + imageId, + setImageId, + } = useImageGeneratorConfig(); + + const [ mediaDetails ] = useMediaDetails( imageId ); + + const onCustomImageChange = useCallback( + media => { + setImageId( media?.id ); + }, + [ setImageId ] + ); + + const ImageOptions = () => { + return ( + <> + + + { imageType === 'custom' && ( + + ) } + + ); + }; + + return ( + + + { isEnabled && ( + <> + +
+ + + ) } +
+ ); +}; + +export default SocialImageGeneratorPanel; diff --git a/projects/js-packages/publicize-components/src/hooks/use-image-generator-config/README.md b/projects/js-packages/publicize-components/src/hooks/use-image-generator-config/README.md new file mode 100644 index 0000000000000..09a16e467990b --- /dev/null +++ b/projects/js-packages/publicize-components/src/hooks/use-image-generator-config/README.md @@ -0,0 +1,20 @@ +# useImageGeneratorConfig() hook + +React hooks to deal with image generator config. It allows you to get options related to the image generator, as well as updating them. + +```es6 +import { TextControl } from '@wordpress/components'; +import useImageGeneratorConfig from './hooks/use-image-generator-config'; + +function ImageGeneratorConfig() { + const { customText, setCustomText } = useImageGeneratorConfig(); + + return ( + + ); +} +``` diff --git a/projects/js-packages/publicize-components/src/hooks/use-image-generator-config/index.js b/projects/js-packages/publicize-components/src/hooks/use-image-generator-config/index.js new file mode 100644 index 0000000000000..97475424ec83f --- /dev/null +++ b/projects/js-packages/publicize-components/src/hooks/use-image-generator-config/index.js @@ -0,0 +1,63 @@ +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as editorStore } from '@wordpress/editor'; +import { useCallback } from '@wordpress/element'; + +const PUBLICIZE_STORE_ID = 'jetpack/publicize'; + +const getCurrentSettings = ( sigSettings, isPostPublished ) => ( { + isEnabled: sigSettings?.enabled ?? ! isPostPublished, + customText: sigSettings?.custom_text ?? null, + imageType: sigSettings?.image_type ?? null, + imageId: sigSettings?.image_id ?? null, +} ); + +/** + * @typedef {object} ImageGeneratorConfigHook + * @property {Array} postSettings - Array of post settings (custom text, image type etc). + * @property {boolean} isEnabled - True if the image generator is enabled for this post. + * @property {string} customText - Custom text for the generated image. + * @property {string} imageType - Optional. Type of the image in the generated image. + * @property {number} imageId - Optional. ID of the image in the generated image. + * @property {Function} setIsEnabled - Callback to enable or disable the image generator for a post. + * @property {Function} setCustomText - Callback to change the custom text. + * @property {Function} setImageType - Callback to change the image type. + * @property {Function} setImageId - Callback to change the image ID. + */ + +/** + * Hook to handle storing and retrieving image generator config. + * + * @returns {ImageGeneratorConfigHook} - An object with the attached media hook properties set. + */ +export default function useImageGeneratorConfig() { + const { editPost } = useDispatch( editorStore ); + + const { postSettings, currentOptions } = useSelect( select => ( { + postSettings: select( PUBLICIZE_STORE_ID ).getImageGeneratorPostSettings(), + currentOptions: select( PUBLICIZE_STORE_ID ).getJetpackSocialOptions(), + } ) ); + + const { isPostPublished } = useSelect( select => ( { + isPostPublished: select( editorStore ).isCurrentPostPublished(), + } ) ); + + const updateSettings = useCallback( + ( key, value ) => { + const settings = { ...postSettings, [ key ]: value }; + editPost( { + meta: { + jetpack_social_options: { ...currentOptions, image_generator_settings: settings }, + }, + } ); + }, + [ currentOptions, editPost, postSettings ] + ); + + return { + ...getCurrentSettings( currentOptions?.image_generator_settings, isPostPublished ), + setIsEnabled: value => updateSettings( 'enabled', value ), + setCustomText: value => updateSettings( 'custom_text', value ), + setImageType: value => updateSettings( 'image_type', value ), + setImageId: value => updateSettings( 'image_id', value ), + }; +} diff --git a/projects/js-packages/publicize-components/src/store/selectors.js b/projects/js-packages/publicize-components/src/store/selectors.js index 2b8fa90cbd2e9..6eece448e10a3 100644 --- a/projects/js-packages/publicize-components/src/store/selectors.js +++ b/projects/js-packages/publicize-components/src/store/selectors.js @@ -570,3 +570,12 @@ export function getJetpackSocialPostAlreadyShared() { export function getAttachedMedia() { return get( getJetpackSocialOptions(), [ 'attached_media' ], [] ); } + +/** + * Get a list of all image generator settings for a post. + * + * @returns {Array} An array of image generator settings. + */ +export function getImageGeneratorPostSettings() { + return getJetpackSocialOptions()?.image_generator_settings ?? []; +} diff --git a/projects/packages/publicize/changelog/add-jetpack-social-image-generator-sidebar-panel b/projects/packages/publicize/changelog/add-jetpack-social-image-generator-sidebar-panel new file mode 100644 index 0000000000000..753c26bb20192 --- /dev/null +++ b/projects/packages/publicize/changelog/add-jetpack-social-image-generator-sidebar-panel @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Add options panel for Social Image Generator to Jetpack Social sidebar. diff --git a/projects/packages/publicize/src/class-publicize-base.php b/projects/packages/publicize/src/class-publicize-base.php index c92b32b37e320..028b2cdec57c9 100644 --- a/projects/packages/publicize/src/class-publicize-base.php +++ b/projects/packages/publicize/src/class-publicize-base.php @@ -1066,7 +1066,7 @@ public function register_post_meta() { 'schema' => array( 'type' => 'object', 'properties' => array( - 'attached_media' => array( + 'attached_media' => array( 'type' => 'array', 'items' => array( 'type' => 'object', @@ -1080,6 +1080,24 @@ public function register_post_meta() { ), ), ), + 'image_generator_settings' => array( + 'type' => 'object', + 'properties' => array( + 'enabled' => array( + 'type' => 'boolean', + ), + 'custom_text' => array( + 'type' => 'string', + ), + 'image_type' => array( + 'type' => 'string', + ), + 'image_id' => array( + 'type' => 'number', + 'default' => 0, + ), + ), + ), ), ), ), diff --git a/projects/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php index ea8639232bd58..558c35237a697 100644 --- a/projects/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php +++ b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php @@ -25,6 +25,7 @@ * jetpack_publicize_message: (string) The message to use instead of the post's title when sharing. * jetpack_social_options: { * attached_media: (array) List of media that will be attached to the social media post. + * image_generator_settings: (array) List of settings related to the generated image. * } * ... * } diff --git a/projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel b/projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel new file mode 100644 index 0000000000000..7e2926d36a9b9 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Add options panel for Social Image Generator to Jetpack Social sidebar. diff --git a/projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel#2 b/projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel#2 new file mode 100644 index 0000000000000..a1c1831fa1ef7 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel#2 @@ -0,0 +1,5 @@ +Significance: patch +Type: other +Comment: Updated composer.lock. + + diff --git a/projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel#3 b/projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel#3 new file mode 100644 index 0000000000000..a1c1831fa1ef7 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-jetpack-social-image-generator-sidebar-panel#3 @@ -0,0 +1,5 @@ +Significance: patch +Type: other +Comment: Updated composer.lock. + + diff --git a/projects/plugins/jetpack/extensions/plugins/publicize/index.js b/projects/plugins/jetpack/extensions/plugins/publicize/index.js index 2afeca5339759..1da8e98d3309a 100644 --- a/projects/plugins/jetpack/extensions/plugins/publicize/index.js +++ b/projects/plugins/jetpack/extensions/plugins/publicize/index.js @@ -13,6 +13,8 @@ import { TwitterThreadListener, PublicizePanel, useSocialMediaConnections, + usePublicizeConfig, + SocialImageGeneratorPanel, } from '@automattic/jetpack-publicize-components'; import { PluginPrePublishPanel } from '@wordpress/edit-post'; import { PostTypeSupportCheck } from '@wordpress/editor'; @@ -26,6 +28,8 @@ export const name = 'publicize'; const PublicizeSettings = () => { const { hasEnabledConnections } = useSocialMediaConnections(); + const { isSocialImageGeneratorEnabled } = usePublicizeConfig(); + return ( @@ -34,6 +38,7 @@ const PublicizeSettings = () => { + { isSocialImageGeneratorEnabled && } { + + { isSocialImageGeneratorEnabled && ( + } + > + + + ) } ); }; diff --git a/projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel b/projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel new file mode 100644 index 0000000000000..0681a320849a2 --- /dev/null +++ b/projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Add Social Image Generator editor panel to post sidebar diff --git a/projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel#2 b/projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel#2 new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel#2 @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel#3 b/projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel#3 new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/social/changelog/add-jetpack-social-image-generator-sidebar-panel#3 @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/social/src/js/editor.js b/projects/plugins/social/src/js/editor.js index a28cf298f6288..d2b9570f494ed 100644 --- a/projects/plugins/social/src/js/editor.js +++ b/projects/plugins/social/src/js/editor.js @@ -2,6 +2,7 @@ import { JetpackLogo, SocialIcon, getRedirectUrl } from '@automattic/jetpack-com import { SocialPreviewsModal, SocialPreviewsPanel, + SocialImageGeneratorPanel, usePublicizeConfig, useSocialMediaConnections, PublicizePanel, @@ -55,7 +56,12 @@ const JetpackSocialSidebar = () => { const closeModal = useCallback( () => setIsModalOpened( false ), [] ); const { hasConnections, hasEnabledConnections } = useSocialMediaConnections(); - const { isPublicizeEnabled, hidePublicizeFeature, isPostAlreadyShared } = usePublicizeConfig(); + const { + isPublicizeEnabled, + hidePublicizeFeature, + isPostAlreadyShared, + isSocialImageGeneratorEnabled, + } = usePublicizeConfig(); const isPostPublished = useSelect( select => select( editorStore ).isCurrentPostPublished(), [] ); // Determine if the review request should show right before the post publishes // The publicize-enabled meta and related connections are disabled after publishing @@ -105,6 +111,7 @@ const JetpackSocialSidebar = () => { + { isSocialImageGeneratorEnabled && } @@ -120,6 +127,16 @@ const JetpackSocialSidebar = () => { + { isSocialImageGeneratorEnabled && ( + } + > + + + ) } + Date: Thu, 23 Feb 2023 10:55:18 -0300 Subject: [PATCH 28/38] Forms: Allow fields style sync (#28988) * Allow Form fields style synchronization * Adjust Mutiple/Single choice fields to handle styles correctly * changelog * Add help text to the Style Sync toggle * Improve MC/SC fields border & background styles * Update files in the Forms packages * changelog * Adjust Sync toggle help message * Fix typo * Bump versions --------- Co-authored-by: Jeremy Herve --- .../changelog/update-form-sync-field-style | 4 + projects/packages/forms/composer.json | 2 +- projects/packages/forms/package.json | 2 +- .../src/blocks/contact-form/child-blocks.js | 10 ++- .../components/jetpack-field-checkbox.js | 29 ++++++- .../components/jetpack-field-consent.js | 44 +++++++++- .../components/jetpack-field-controls.js | 48 +++++------ .../components/jetpack-field-dropdown.js | 25 ++++-- .../components/jetpack-field-multiple.js | 48 ++++++++--- .../components/jetpack-field-textarea.js | 21 ++++- .../contact-form/components/jetpack-field.js | 22 ++++- .../contact-form/components/jetpack-option.js | 2 +- .../forms/src/blocks/contact-form/editor.scss | 24 +++++- .../util/with-shared-field-attributes.js | 84 +++++++++++++++++++ .../forms/src/class-jetpack-forms.php | 2 +- .../contact-form/class-contact-form-field.php | 28 +++++-- .../forms/src/contact-form/css/grunion.css | 10 +++ .../forms/src/contact-form/js/form-styles.js | 8 +- .../changelog/update-form-sync-field-style | 4 + .../changelog/update-form-sync-field-style#2 | 5 ++ projects/plugins/jetpack/composer.lock | 4 +- .../blocks/contact-form/child-blocks.js | 10 ++- .../components/jetpack-field-checkbox.js | 29 ++++++- .../components/jetpack-field-consent.js | 44 +++++++++- .../components/jetpack-field-controls.js | 48 +++++------ .../components/jetpack-field-dropdown.js | 25 ++++-- .../components/jetpack-field-multiple.js | 48 ++++++++--- .../components/jetpack-field-textarea.js | 21 ++++- .../contact-form/components/jetpack-field.js | 22 ++++- .../contact-form/components/jetpack-option.js | 2 +- .../blocks/contact-form/editor.scss | 15 +++- .../util/with-shared-field-attributes.js | 84 +++++++++++++++++++ .../modules/contact-form/css/grunion.css | 10 +++ .../contact-form/grunion-contact-form.php | 34 ++++---- .../modules/contact-form/js/form-styles.js | 10 +-- 35 files changed, 665 insertions(+), 163 deletions(-) create mode 100644 projects/packages/forms/changelog/update-form-sync-field-style create mode 100644 projects/packages/forms/src/blocks/contact-form/util/with-shared-field-attributes.js create mode 100644 projects/plugins/jetpack/changelog/update-form-sync-field-style create mode 100644 projects/plugins/jetpack/changelog/update-form-sync-field-style#2 create mode 100644 projects/plugins/jetpack/extensions/blocks/contact-form/util/with-shared-field-attributes.js diff --git a/projects/packages/forms/changelog/update-form-sync-field-style b/projects/packages/forms/changelog/update-form-sync-field-style new file mode 100644 index 0000000000000..46ee0af7e4b86 --- /dev/null +++ b/projects/packages/forms/changelog/update-form-sync-field-style @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Allow Form fields style synchronization diff --git a/projects/packages/forms/composer.json b/projects/packages/forms/composer.json index f68d875166241..22df7e8ad3a99 100644 --- a/projects/packages/forms/composer.json +++ b/projects/packages/forms/composer.json @@ -58,7 +58,7 @@ "link-template": "https://github.com/automattic/jetpack-forms/compare/v${old}...v${new}" }, "branch-alias": { - "dev-trunk": "0.5.x-dev" + "dev-trunk": "0.6.x-dev" }, "textdomain": "jetpack-forms", "version-constants": { diff --git a/projects/packages/forms/package.json b/projects/packages/forms/package.json index c3c45350e5347..74e1ab846b480 100644 --- a/projects/packages/forms/package.json +++ b/projects/packages/forms/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-forms", - "version": "0.5.1", + "version": "0.6.0-alpha", "description": "Jetpack Forms", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/forms/#readme", "bugs": { diff --git a/projects/packages/forms/src/blocks/contact-form/child-blocks.js b/projects/packages/forms/src/blocks/contact-form/child-blocks.js index 6848837f81cad..1be87000ea308 100644 --- a/projects/packages/forms/src/blocks/contact-form/child-blocks.js +++ b/projects/packages/forms/src/blocks/contact-form/child-blocks.js @@ -5,7 +5,7 @@ import { __, _x } from '@wordpress/i18n'; import JetpackField from './components/jetpack-field'; import JetpackFieldCheckbox from './components/jetpack-field-checkbox'; import JetpackFieldConsent from './components/jetpack-field-consent'; -import { JetpackDropdownEdit } from './components/jetpack-field-dropdown'; +import JetpackDropdown from './components/jetpack-field-dropdown'; import JetpackFieldMultiple from './components/jetpack-field-multiple'; import JetpackFieldTextarea from './components/jetpack-field-textarea'; import { getIconColor } from './util/block-icons'; @@ -82,6 +82,10 @@ const FieldDefaults = { borderColor: { type: 'string', }, + shareFieldAttributes: { + type: 'boolean', + default: true, + }, }, transforms: { to: [ @@ -227,6 +231,7 @@ const EditCheckbox = props => { return ( ), - edit: JetpackDropdownEdit, + edit: JetpackDropdown, attributes: { ...FieldDefaults.attributes, toggleLabel: { diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-checkbox.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-checkbox.js index 847437aaee3d8..94bbcf9415ed3 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-checkbox.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-checkbox.js @@ -5,8 +5,9 @@ import { PanelColorSettings, } from '@wordpress/block-editor'; import { PanelBody, ToggleControl } from '@wordpress/components'; -import { withInstanceId } from '@wordpress/compose'; +import { compose, withInstanceId } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; +import { withSharedFieldAttributes } from '../util/with-shared-field-attributes'; import JetpackFieldCss from './jetpack-field-css'; import JetpackFieldLabel from './jetpack-field-label'; import JetpackFieldWidth from './jetpack-field-width'; @@ -69,8 +70,14 @@ function JetpackFieldCheckbox( props ) { help={ __( 'You can edit the "required" label in the editor', 'jetpack-forms' ) } /> - + setAttributes( { shareFieldAttributes: value } ) } + help={ __( 'Deactivate for individual styling of this block', 'jetpack-forms' ) } + /> + + /> + setAttributes( { shareFieldAttributes: value } ) } + help={ __( 'Deactivate for individual styling of this block', 'jetpack-forms' ) } + /> + setAttributes( { labelColor: value } ), + label: __( 'Label Text', 'jetpack-forms' ), + }, + ] } + /> @@ -72,4 +94,18 @@ const JetpackFieldConsent = ( { ); }; -export default withInstanceId( JetpackFieldConsent ); +export default compose( + withSharedFieldAttributes( [ + 'borderRadius', + 'borderWidth', + 'labelFontSize', + 'fieldFontSize', + 'lineHeight', + 'labelLineHeight', + 'inputColor', + 'labelColor', + 'fieldBackgroundColor', + 'borderColor', + ] ), + withInstanceId +)( JetpackFieldConsent ); diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-controls.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-controls.js index 0318899422620..3f9227d2418f4 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-controls.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-controls.js @@ -30,7 +30,6 @@ const JetpackFieldControls = ( { required, setAttributes, width, - type, } ) => { const setNumberAttribute = ( key, parse = parseInt ) => value => { const parsedValue = parse( value, 10 ); @@ -40,8 +39,6 @@ const JetpackFieldControls = ( { } ); }; - const hasBorderControls = type !== 'radio' && type !== 'checkbox'; - const colorSettings = [ { value: attributes.labelColor, @@ -107,11 +104,18 @@ const JetpackFieldControls = ( { 'jetpack-forms' ) } /> + + setAttributes( { shareFieldAttributes: value } ) } + help={ __( 'Deactivate for individual styling of this block', 'jetpack-forms' ) } + /> @@ -133,26 +137,22 @@ const JetpackFieldControls = ( { size="__unstable-large" /> - { hasBorderControls && ( - <> - - - - ) } + + diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-dropdown.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-dropdown.js index a002a455553b6..0c6fae44d3e39 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-dropdown.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-dropdown.js @@ -1,21 +1,17 @@ import { RichText } from '@wordpress/block-editor'; +import { compose } from '@wordpress/compose'; import { useEffect, useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import classnames from 'classnames'; import { isEmpty, isNil, noop, split, trim } from 'lodash'; import { setFocus } from '../util/focus'; import { useFormStyle, useFormWrapper } from '../util/form'; +import { withSharedFieldAttributes } from '../util/with-shared-field-attributes'; import JetpackFieldControls from './jetpack-field-controls'; import JetpackFieldLabel from './jetpack-field-label'; import { useJetpackFieldStyles } from './use-jetpack-field-styles'; -export const JetpackDropdownEdit = ( { - attributes, - clientId, - isSelected, - name, - setAttributes, -} ) => { +const JetpackDropdown = ( { attributes, clientId, isSelected, name, setAttributes } ) => { const { id, label, options, required, requiredText, toggleLabel, width } = attributes; const optionsWrapper = useRef(); const formStyle = useFormStyle( clientId ); @@ -160,3 +156,18 @@ export const JetpackDropdownEdit = ( {
); }; + +export default compose( + withSharedFieldAttributes( [ + 'borderRadius', + 'borderWidth', + 'labelFontSize', + 'fieldFontSize', + 'lineHeight', + 'labelLineHeight', + 'inputColor', + 'labelColor', + 'fieldBackgroundColor', + 'borderColor', + ] ) +)( JetpackDropdown ); diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-multiple.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-multiple.js index 820321eb7ec83..8356b52e38cbb 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-multiple.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-multiple.js @@ -1,9 +1,10 @@ import { Button } from '@wordpress/components'; -import { withInstanceId } from '@wordpress/compose'; +import { compose, withInstanceId } from '@wordpress/compose'; import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import classnames from 'classnames'; import { useFormStyle } from '../util/form'; +import { withSharedFieldAttributes } from '../util/with-shared-field-attributes'; import JetpackFieldControls from './jetpack-field-controls'; import JetpackFieldLabel from './jetpack-field-label'; import JetpackOption from './jetpack-option'; @@ -67,6 +68,11 @@ function JetpackFieldMultiple( props ) { }; const { blockStyle, fieldStyle } = useJetpackFieldStyles( attributes ); + const optionStyle = { + color: fieldStyle.color, + fontSize: fieldStyle.fontSize, + lineHeight: fieldStyle.lineHeight, + }; return ( <> @@ -99,20 +105,22 @@ function JetpackFieldMultiple( props ) { onAddOption={ addNewOption } isInFocus={ index === inFocus && isSelected } isSelected={ isSelected } - style={ type !== 'select' ? fieldStyle : {} } + style={ type !== 'select' ? optionStyle : {} } /> ) ) } + { isSelected && ( +
  • + +
  • + ) } - { isSelected && ( - - ) } { const { attributes, clientId, @@ -64,4 +66,19 @@ export default function JetpackFieldTextarea( props ) { /> ); -} +}; + +export default compose( + withSharedFieldAttributes( [ + 'borderRadius', + 'borderWidth', + 'labelFontSize', + 'fieldFontSize', + 'lineHeight', + 'labelLineHeight', + 'inputColor', + 'labelColor', + 'fieldBackgroundColor', + 'borderColor', + ] ) +)( JetpackFieldTextarea ); diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field.js index 2f3c68b28f5c7..73d44c555bfee 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field.js @@ -1,13 +1,14 @@ -import { createHigherOrderComponent } from '@wordpress/compose'; +import { createHigherOrderComponent, compose } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; import classnames from 'classnames'; import { isEmpty } from 'lodash'; import { useFormStyle } from '../util/form'; +import { withSharedFieldAttributes } from '../util/with-shared-field-attributes'; import JetpackFieldControls from './jetpack-field-controls'; import JetpackFieldLabel from './jetpack-field-label'; import { useJetpackFieldStyles } from './use-jetpack-field-styles'; -export default function JetpackField( props ) { +const JetpackField = props => { const { attributes, clientId, @@ -59,7 +60,22 @@ export default function JetpackField( props ) { /> ); -} +}; + +export default compose( + withSharedFieldAttributes( [ + 'borderRadius', + 'borderWidth', + 'labelFontSize', + 'fieldFontSize', + 'lineHeight', + 'labelLineHeight', + 'inputColor', + 'labelColor', + 'fieldBackgroundColor', + 'borderColor', + ] ) +)( JetpackField ); const withCustomClassName = createHigherOrderComponent( BlockListBlock => { return props => { diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-option.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-option.js index 75db80b5b5ab9..7585de46b16c6 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-option.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-option.js @@ -50,7 +50,7 @@ class JetpackOption extends Component { return (
  • { type && type !== 'select' && ( - + ) } .jetpack-field { + .jetpack-field__input, .jetpack-field__textarea { + padding-left: max(var(--jetpack--contact-form--input-padding-left, 16px), var(--jetpack--contact-form--border-radius)); + padding-right: max(var(--jetpack--contact-form--input-padding-left, 16px), var(--jetpack--contact-form--border-radius)); + } +} + .jetpack-field-label__width { .components-button-group { display: block; @@ -412,8 +417,6 @@ } &:focus { - background: #fff; - border-color: #e3e5e8; box-shadow: none; } } @@ -589,6 +592,19 @@ } } + &:not(.is-style-outlined):not(.is-style-animated) { + .jetpack-field.jetpack-field-multiple { + .jetpack-field-multiple__list { + background-color: var(--jetpack--contact-form--input-background); + border-radius: var(--jetpack--contact-form--border-radius); + border-color: var(--jetpack--contact-form--border-color); + border-style: var(--jetpack--contact-form--border-style); + border-width: var(--jetpack--contact-form--border-size); + padding: var(--jetpack--contact-form--input-padding, 16px); + } + } + } + &.is-style-outlined { .block-editor-block-list__block:not([contenteditable]):focus:after { top: -10px; diff --git a/projects/packages/forms/src/blocks/contact-form/util/with-shared-field-attributes.js b/projects/packages/forms/src/blocks/contact-form/util/with-shared-field-attributes.js new file mode 100644 index 0000000000000..2ac2dfb09dd25 --- /dev/null +++ b/projects/packages/forms/src/blocks/contact-form/util/with-shared-field-attributes.js @@ -0,0 +1,84 @@ +/** + * External dependencies + */ +import { useDispatch, useSelect } from '@wordpress/data'; +import { useCallback, useEffect } from '@wordpress/element'; +import { isEmpty, filter, first, map, pick, isNil } from 'lodash'; + +export const useSharedFieldAttributes = ( { + attributes, + clientId, + setAttributes, + sharedAttributes, +} ) => { + const { updateBlockAttributes } = useDispatch( 'core/block-editor' ); + + const siblings = useSelect( + select => { + const blockEditor = select( 'core/block-editor' ); + + const parentId = first( + blockEditor.getBlockParentsByBlockName( clientId, 'jetpack/contact-form' ) + ); + + return filter( + blockEditor.getBlocks( parentId ), + block => block.name.indexOf( 'jetpack/field' ) > -1 && block.attributes.shareFieldAttributes + ); + }, + [ clientId ] + ); + + useEffect( () => { + if ( ! isEmpty( siblings ) && attributes.shareFieldAttributes ) { + const newSharedAttributes = pick( first( siblings ).attributes, sharedAttributes ); + updateBlockAttributes( [ clientId ], newSharedAttributes ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [] ); + + return useCallback( + newAttributes => { + let blocksToUpdate; + let newSharedAttributes; + + if ( attributes.shareFieldAttributes && isNil( newAttributes.shareFieldAttributes ) ) { + blocksToUpdate = map( siblings, block => block.clientId ); + newSharedAttributes = pick( newAttributes, sharedAttributes ); + } else if ( newAttributes.shareFieldAttributes && ! isEmpty( siblings ) ) { + blocksToUpdate = [ clientId ]; + newSharedAttributes = pick( first( siblings ).attributes, sharedAttributes ); + } + + if ( ! isEmpty( blocksToUpdate ) && ! isEmpty( newSharedAttributes ) ) { + updateBlockAttributes( blocksToUpdate, newSharedAttributes ); + } + + setAttributes( newAttributes ); + }, + [ attributes, clientId, setAttributes, sharedAttributes, siblings, updateBlockAttributes ] + ); +}; + +export const withSharedFieldAttributes = sharedAttributes => WrappedComponent => ( { + attributes, + clientId, + setAttributes, + ...props +} ) => { + const syncAttributes = useSharedFieldAttributes( { + attributes, + clientId, + setAttributes, + sharedAttributes, + } ); + + return ( + + ); +}; diff --git a/projects/packages/forms/src/class-jetpack-forms.php b/projects/packages/forms/src/class-jetpack-forms.php index 126a900cab182..2b337ad35f9be 100644 --- a/projects/packages/forms/src/class-jetpack-forms.php +++ b/projects/packages/forms/src/class-jetpack-forms.php @@ -14,7 +14,7 @@ */ class Jetpack_Forms { - const PACKAGE_VERSION = '0.5.1'; + const PACKAGE_VERSION = '0.6.0-alpha'; /** * Load the contact form module. 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 3f609b9facc7c..55117bc8100a3 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 @@ -58,6 +58,13 @@ class Contact_Form_Field extends Contact_Form_Shortcode { */ public $field_styles = ''; + /** + * Styles to be applied to the field option + * + * @var string + */ + public $option_styles = ''; + /** * Styles to be applied to the field * @@ -288,24 +295,27 @@ public function render() { $this->field_styles .= 'border-width: ' . (int) $this->get_attribute( 'borderwidth' ) . 'px;'; } if ( is_numeric( $this->get_attribute( 'lineheight' ) ) ) { - $this->block_styles .= '--jetpack--contact-form--line-height: ' . esc_attr( $this->get_attribute( 'lineheight' ) ) . ';'; - $this->field_styles .= 'line-height: ' . (int) $this->get_attribute( 'lineheight' ) . ';'; + $this->block_styles .= '--jetpack--contact-form--line-height: ' . esc_attr( $this->get_attribute( 'lineheight' ) ) . ';'; + $this->field_styles .= 'line-height: ' . (int) $this->get_attribute( 'lineheight' ) . ';'; + $this->option_styles .= 'line-height: ' . (int) $this->get_attribute( 'lineheight' ) . ';'; } if ( ! empty( $this->get_attribute( 'bordercolor' ) ) ) { $this->block_styles .= '--jetpack--contact-form--border-color: ' . esc_attr( $this->get_attribute( 'bordercolor' ) ) . ';'; $this->field_styles .= 'border-color: ' . esc_attr( $this->get_attribute( 'bordercolor' ) ) . ';'; } if ( ! empty( $this->get_attribute( 'inputcolor' ) ) ) { - $this->block_styles .= '--jetpack--contact-form--text-color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; - $this->field_styles .= 'color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; + $this->block_styles .= '--jetpack--contact-form--text-color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; + $this->field_styles .= 'color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; + $this->option_styles .= 'color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; } if ( ! empty( $this->get_attribute( 'fieldbackgroundcolor' ) ) ) { $this->block_styles .= '--jetpack--contact-form--input-background: ' . esc_attr( $this->get_attribute( 'fieldbackgroundcolor' ) ) . ';'; $this->field_styles .= 'background-color: ' . esc_attr( $this->get_attribute( 'fieldbackgroundcolor' ) ) . ';'; } if ( ! empty( $this->get_attribute( 'fieldfontsize' ) ) ) { - $this->block_styles .= '--jetpack--contact-form--font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; - $this->field_styles .= 'font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; + $this->block_styles .= '--jetpack--contact-form--font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; + $this->field_styles .= 'font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; + $this->option_styles .= 'font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; } if ( ! empty( $this->get_attribute( 'labelcolor' ) ) ) { @@ -580,7 +590,7 @@ public function render_radio_field( $id, $label, $value, $class, $required, $req $field = $this->render_label( '', $id, $label, $required, $required_field_text ); $field .= '
    '; - $field_style = 'style="' . $this->field_styles . '"'; + $field_style = 'style="' . $this->option_styles . '"'; foreach ( (array) $this->get_attribute( 'options' ) as $option_index => $option ) { $option = Contact_Form_Plugin::strip_tags( $option ); @@ -632,7 +642,7 @@ private function render_consent_field( $id, $class ) { $consent_type = 'explicit' === $this->get_attribute( 'consenttype' ) ? 'explicit' : 'implicit'; $consent_message = 'explicit' === $consent_type ? $this->get_attribute( 'explicitconsentmessage' ) : $this->get_attribute( 'implicitconsentmessage' ); - $field = "
    ); }; + +export default compose( + withSharedFieldAttributes( [ + 'borderRadius', + 'borderWidth', + 'labelFontSize', + 'fieldFontSize', + 'lineHeight', + 'labelLineHeight', + 'inputColor', + 'labelColor', + 'fieldBackgroundColor', + 'borderColor', + ] ) +)( JetpackDropdown ); diff --git a/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-field-multiple.js b/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-field-multiple.js index 46250d8f0c054..af8c402d9a758 100644 --- a/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-field-multiple.js +++ b/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-field-multiple.js @@ -1,9 +1,10 @@ import { Button } from '@wordpress/components'; -import { withInstanceId } from '@wordpress/compose'; +import { compose, withInstanceId } from '@wordpress/compose'; import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import classnames from 'classnames'; import { useFormStyle } from '../util/form'; +import { withSharedFieldAttributes } from '../util/with-shared-field-attributes'; import JetpackFieldControls from './jetpack-field-controls'; import JetpackFieldLabel from './jetpack-field-label'; import JetpackOption from './jetpack-option'; @@ -67,6 +68,11 @@ function JetpackFieldMultiple( props ) { }; const { blockStyle, fieldStyle } = useJetpackFieldStyles( attributes ); + const optionStyle = { + color: fieldStyle.color, + fontSize: fieldStyle.fontSize, + lineHeight: fieldStyle.lineHeight, + }; return ( <> @@ -99,20 +105,22 @@ function JetpackFieldMultiple( props ) { onAddOption={ addNewOption } isInFocus={ index === inFocus && isSelected } isSelected={ isSelected } - style={ type !== 'select' ? fieldStyle : {} } + style={ type !== 'select' ? optionStyle : {} } /> ) ) } + { isSelected && ( +
  • + +
  • + ) } - { isSelected && ( - - ) } { const { attributes, clientId, @@ -64,4 +66,19 @@ export default function JetpackFieldTextarea( props ) { /> ); -} +}; + +export default compose( + withSharedFieldAttributes( [ + 'borderRadius', + 'borderWidth', + 'labelFontSize', + 'fieldFontSize', + 'lineHeight', + 'labelLineHeight', + 'inputColor', + 'labelColor', + 'fieldBackgroundColor', + 'borderColor', + ] ) +)( JetpackFieldTextarea ); diff --git a/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-field.js b/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-field.js index 2f3c68b28f5c7..73d44c555bfee 100644 --- a/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-field.js +++ b/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-field.js @@ -1,13 +1,14 @@ -import { createHigherOrderComponent } from '@wordpress/compose'; +import { createHigherOrderComponent, compose } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; import classnames from 'classnames'; import { isEmpty } from 'lodash'; import { useFormStyle } from '../util/form'; +import { withSharedFieldAttributes } from '../util/with-shared-field-attributes'; import JetpackFieldControls from './jetpack-field-controls'; import JetpackFieldLabel from './jetpack-field-label'; import { useJetpackFieldStyles } from './use-jetpack-field-styles'; -export default function JetpackField( props ) { +const JetpackField = props => { const { attributes, clientId, @@ -59,7 +60,22 @@ export default function JetpackField( props ) { /> ); -} +}; + +export default compose( + withSharedFieldAttributes( [ + 'borderRadius', + 'borderWidth', + 'labelFontSize', + 'fieldFontSize', + 'lineHeight', + 'labelLineHeight', + 'inputColor', + 'labelColor', + 'fieldBackgroundColor', + 'borderColor', + ] ) +)( JetpackField ); const withCustomClassName = createHigherOrderComponent( BlockListBlock => { return props => { diff --git a/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-option.js b/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-option.js index 038450dcb143f..11084f67ed6c1 100644 --- a/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-option.js +++ b/projects/plugins/jetpack/extensions/blocks/contact-form/components/jetpack-option.js @@ -50,7 +50,7 @@ class JetpackOption extends Component { return (
  • { type && type !== 'select' && ( - + ) } { + const { updateBlockAttributes } = useDispatch( 'core/block-editor' ); + + const siblings = useSelect( + select => { + const blockEditor = select( 'core/block-editor' ); + + const parentId = first( + blockEditor.getBlockParentsByBlockName( clientId, 'jetpack/contact-form' ) + ); + + return filter( + blockEditor.getBlocks( parentId ), + block => block.name.indexOf( 'jetpack/field' ) > -1 && block.attributes.shareFieldAttributes + ); + }, + [ clientId ] + ); + + useEffect( () => { + if ( ! isEmpty( siblings ) && attributes.shareFieldAttributes ) { + const newSharedAttributes = pick( first( siblings ).attributes, sharedAttributes ); + updateBlockAttributes( [ clientId ], newSharedAttributes ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [] ); + + return useCallback( + newAttributes => { + let blocksToUpdate; + let newSharedAttributes; + + if ( attributes.shareFieldAttributes && isNil( newAttributes.shareFieldAttributes ) ) { + blocksToUpdate = map( siblings, block => block.clientId ); + newSharedAttributes = pick( newAttributes, sharedAttributes ); + } else if ( newAttributes.shareFieldAttributes && ! isEmpty( siblings ) ) { + blocksToUpdate = [ clientId ]; + newSharedAttributes = pick( first( siblings ).attributes, sharedAttributes ); + } + + if ( ! isEmpty( blocksToUpdate ) && ! isEmpty( newSharedAttributes ) ) { + updateBlockAttributes( blocksToUpdate, newSharedAttributes ); + } + + setAttributes( newAttributes ); + }, + [ attributes, clientId, setAttributes, sharedAttributes, siblings, updateBlockAttributes ] + ); +}; + +export const withSharedFieldAttributes = sharedAttributes => WrappedComponent => ( { + attributes, + clientId, + setAttributes, + ...props +} ) => { + const syncAttributes = useSharedFieldAttributes( { + attributes, + clientId, + setAttributes, + sharedAttributes, + } ); + + return ( + + ); +}; diff --git a/projects/plugins/jetpack/modules/contact-form/css/grunion.css b/projects/plugins/jetpack/modules/contact-form/css/grunion.css index 0496ca8e0b547..47c4feb614524 100644 --- a/projects/plugins/jetpack/modules/contact-form/css/grunion.css +++ b/projects/plugins/jetpack/modules/contact-form/css/grunion.css @@ -367,6 +367,16 @@ flex-grow: 1; } +.contact-form :not(.is-style-outlined):not(.is-style-animated) .grunion-field-wrap .grunion-checkbox-multiple-options, +.contact-form :not(.is-style-outlined):not(.is-style-animated) .grunion-field-wrap .grunion-radio-options { + background-color: var(--jetpack--contact-form--input-background); + border-radius: var(--jetpack--contact-form--border-radius); + border-color: var(--jetpack--contact-form--border-color); + border-style: var(--jetpack--contact-form--border-style); + border-width: var(--jetpack--contact-form--border-size); + padding: var(--jetpack--contact-form--input-padding, 16px); +} + .contact-form .is-style-outlined .grunion-field-wrap .notched-label { position: absolute; right: 0; diff --git a/projects/plugins/jetpack/modules/contact-form/grunion-contact-form.php b/projects/plugins/jetpack/modules/contact-form/grunion-contact-form.php index a81446add4d24..84d97e23fb55d 100644 --- a/projects/plugins/jetpack/modules/contact-form/grunion-contact-form.php +++ b/projects/plugins/jetpack/modules/contact-form/grunion-contact-form.php @@ -4183,6 +4183,13 @@ class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode { */ public $field_styles = ''; + /** + * Styles to be applied to the field option + * + * @var string + */ + public $option_styles = ''; + /** * Styles to be applied to the field * @@ -4413,24 +4420,27 @@ public function render() { $this->field_styles .= 'border-width: ' . (int) $this->get_attribute( 'borderwidth' ) . 'px;'; } if ( is_numeric( $this->get_attribute( 'lineheight' ) ) ) { - $this->block_styles .= '--jetpack--contact-form--line-height: ' . esc_attr( $this->get_attribute( 'lineheight' ) ) . ';'; - $this->field_styles .= 'line-height: ' . (int) $this->get_attribute( 'lineheight' ) . ';'; + $this->block_styles .= '--jetpack--contact-form--line-height: ' . esc_attr( $this->get_attribute( 'lineheight' ) ) . ';'; + $this->field_styles .= 'line-height: ' . (int) $this->get_attribute( 'lineheight' ) . ';'; + $this->option_styles .= 'line-height: ' . (int) $this->get_attribute( 'lineheight' ) . ';'; } if ( ! empty( $this->get_attribute( 'bordercolor' ) ) ) { $this->block_styles .= '--jetpack--contact-form--border-color: ' . esc_attr( $this->get_attribute( 'bordercolor' ) ) . ';'; $this->field_styles .= 'border-color: ' . esc_attr( $this->get_attribute( 'bordercolor' ) ) . ';'; } if ( ! empty( $this->get_attribute( 'inputcolor' ) ) ) { - $this->block_styles .= '--jetpack--contact-form--text-color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; - $this->field_styles .= 'color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; + $this->block_styles .= '--jetpack--contact-form--text-color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; + $this->field_styles .= 'color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; + $this->option_styles .= 'color: ' . esc_attr( $this->get_attribute( 'inputcolor' ) ) . ';'; } if ( ! empty( $this->get_attribute( 'fieldbackgroundcolor' ) ) ) { $this->block_styles .= '--jetpack--contact-form--input-background: ' . esc_attr( $this->get_attribute( 'fieldbackgroundcolor' ) ) . ';'; $this->field_styles .= 'background-color: ' . esc_attr( $this->get_attribute( 'fieldbackgroundcolor' ) ) . ';'; } if ( ! empty( $this->get_attribute( 'fieldfontsize' ) ) ) { - $this->block_styles .= '--jetpack--contact-form--font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; - $this->field_styles .= 'font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; + $this->block_styles .= '--jetpack--contact-form--font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; + $this->field_styles .= 'font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; + $this->option_styles .= 'font-size: ' . esc_attr( $this->get_attribute( 'fieldfontsize' ) ) . ';'; } if ( ! empty( $this->get_attribute( 'labelcolor' ) ) ) { @@ -4705,7 +4715,7 @@ public function render_radio_field( $id, $label, $value, $class, $required, $req $field = $this->render_label( '', $id, $label, $required, $required_field_text ); $field .= '
    '; - $field_style = 'style="' . $this->field_styles . '"'; + $field_style = 'style="' . $this->option_styles . '"'; foreach ( (array) $this->get_attribute( 'options' ) as $option_index => $option ) { $option = Grunion_Contact_Form_Plugin::strip_tags( $option ); @@ -4757,7 +4767,7 @@ private function render_consent_field( $id, $class ) { $consent_type = 'explicit' === $this->get_attribute( 'consenttype' ) ? 'explicit' : 'implicit'; $consent_message = 'explicit' === $consent_type ? $this->get_attribute( 'explicitconsentmessage' ) : $this->get_attribute( 'implicitconsentmessage' ); - $field = "
    array( 'class' => true ) ) ); } if ( !empty( $log['author'] ) ) { echo ' — ' . esc_html( $log['author'] ); @@ -799,7 +799,19 @@ function zeroBSCRM_html_companyTimeline($companyID=-1,$logs=false,$companyObj=fa if (isset($log['type'])) echo esc_html( $log['type'] ); } ?> -

    ' . esc_html( $log['nicetime'] ) ?>

    +

    + array( 'class' => true ) ) ); + if ( isset( $log['author'] ) ) { + echo ' — ' . esc_html( $log['author'] ); + } + if ( isset( $log['nicetime'] ) ) { + echo ' — ' . esc_html( $log['nicetime'] ); + } + } + ?> +