From 4ff8c9007f33663bc13e20cd7e1be251b08d1c24 Mon Sep 17 00:00:00 2001 From: Imran Hossain Date: Fri, 13 Dec 2024 22:45:39 +0600 Subject: [PATCH 01/63] Fix DIY migration failing to fetch migration key and throwing 403 on page reload (#40270) * Remove check for read flag from migration key get api checks * Changelog - Remove read check from migration key access checks * Remove key_is_read_option_name altogether --- .../fix-migration-diy-throws-403-on-page-reload | 4 ++++ ...-endpoint-site-migration-wpcom-migration-key.php | 13 ------------- 2 files changed, 4 insertions(+), 13 deletions(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/fix-migration-diy-throws-403-on-page-reload diff --git a/projects/packages/jetpack-mu-wpcom/changelog/fix-migration-diy-throws-403-on-page-reload b/projects/packages/jetpack-mu-wpcom/changelog/fix-migration-diy-throws-403-on-page-reload new file mode 100644 index 0000000000000..f6c66cc0de6aa --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/fix-migration-diy-throws-403-on-page-reload @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fix migration key fetch failing when DIY migration page is reloaded diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-site-migration-wpcom-migration-key.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-site-migration-wpcom-migration-key.php index 955e839d2da66..5cd20cc4dd25c 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-site-migration-wpcom-migration-key.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-site-migration-wpcom-migration-key.php @@ -12,13 +12,6 @@ * @hide-in-jetpack */ class WPCOM_REST_API_V2_Endpoint_Site_Migration_WPCOM_Migration_Key extends WP_REST_Controller { - /** - * Option name that tracks wether the key has been read or not. - * The only possible value for the option is 'read'. - * - * @var string - */ - protected $key_is_read_option_name = 'wpcom_site_migration_wpcom_migration_key_read'; /** * Class constructor @@ -73,10 +66,6 @@ public function can_access() { return false; } - if ( 'read' === get_option( $this->key_is_read_option_name, false ) ) { - return false; - } - return true; } @@ -89,8 +78,6 @@ private function get_migration_key() { $wpcom_migration_settings = new WPCOMWPSettings(); $wpcom_migration_info = new WPCOMInfo( $wpcom_migration_settings ); - update_option( $this->key_is_read_option_name, 'read' ); - return $wpcom_migration_info->getConnectionKey(); } From 9f6b92002afa3d3812b0018923adb2d3f0a494a6 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Sun, 15 Dec 2024 21:51:05 -0800 Subject: [PATCH 02/63] Update ESLint config for TypeScript files (#40584) * Update ESLint config for TypeScript files * Change no-undefined-types to warning * Remove unsed eslint-disable * Restore 'jsdoc/require-returns' * Turn off jsdoc/no-undefined-types * Fix lints caused by turning off "jsdoc/no-undefined-types" * Clean up jetpack-mu-wpcom/eslint.config.mjs * Add changelog * Restore plugins/jetpack/.eslintignore * Turn back jsdoc/no-undefined-types ON with disabled reporting * Re-enable param rules for now --- .../changelog/update-eslint-config-for-ts-files | 4 ++++ .../components/fair-usage-notice.tsx | 6 +----- .../ai-client/src/logo-generator/store/reducer.ts | 7 +------ .../changelog/update-eslint-config-for-ts-files | 4 ++++ .../changelog/update-eslint-config-for-ts-files | 5 +++++ .../boost-score-graph/annotations-plugin.ts | 5 +++-- .../boost-score-graph/tooltips-plugin.ts | 6 +++--- .../changelog/update-eslint-config-for-ts-files | 4 ++++ .../packages/jetpack-mu-wpcom/eslint.config.mjs | 10 +--------- .../protect-card/auto-firewall-status.tsx | 7 +++---- .../protect-card/logins-blocked-status.tsx | 7 +++---- .../protect-card/scan-threats-status.tsx | 15 +++++++-------- .../my-jetpack/_inc/data/use-simple-mutation.ts | 9 ++------- .../my-jetpack/_inc/data/use-simple-query.ts | 4 ++-- .../changelog/update-eslint-config-for-ts-files | 4 ++++ .../changelog/update-eslint-config-for-ts-files | 4 ++++ .../video-thumbnail-selector-modal/index.tsx | 4 ++-- .../admin/components/video-upload-area/index.tsx | 6 +----- .../changelog/update-eslint-config-for-ts-files | 4 ++++ projects/plugins/crm/src/js/data/hooks/queries.ts | 4 ++-- .../crm/src/js/state/automations-admin/util.ts | 4 ++-- .../changelog/update-eslint-config-for-ts-files | 4 ++++ .../ai-assistant-toolbar-dropdown/index.tsx | 5 ++--- tools/js-tools/eslintrc/base.mjs | 7 +++++++ 24 files changed, 75 insertions(+), 64 deletions(-) create mode 100644 projects/js-packages/ai-client/changelog/update-eslint-config-for-ts-files create mode 100644 projects/js-packages/charts/changelog/update-eslint-config-for-ts-files create mode 100644 projects/js-packages/components/changelog/update-eslint-config-for-ts-files create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/update-eslint-config-for-ts-files create mode 100644 projects/packages/my-jetpack/changelog/update-eslint-config-for-ts-files create mode 100644 projects/packages/videopress/changelog/update-eslint-config-for-ts-files create mode 100644 projects/plugins/crm/changelog/update-eslint-config-for-ts-files create mode 100644 projects/plugins/jetpack/changelog/update-eslint-config-for-ts-files diff --git a/projects/js-packages/ai-client/changelog/update-eslint-config-for-ts-files b/projects/js-packages/ai-client/changelog/update-eslint-config-for-ts-files new file mode 100644 index 0000000000000..fefec667583fd --- /dev/null +++ b/projects/js-packages/ai-client/changelog/update-eslint-config-for-ts-files @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed lints following ESLint rule changes for TS diff --git a/projects/js-packages/ai-client/src/logo-generator/components/fair-usage-notice.tsx b/projects/js-packages/ai-client/src/logo-generator/components/fair-usage-notice.tsx index e1f40497fd1dc..ca633457d6846 100644 --- a/projects/js-packages/ai-client/src/logo-generator/components/fair-usage-notice.tsx +++ b/projects/js-packages/ai-client/src/logo-generator/components/fair-usage-notice.tsx @@ -1,9 +1,5 @@ import { Notice } from '@wordpress/components'; import useFairUsageNoticeMessage from '../hooks/use-fair-usage-notice-message.js'; -/** - * Types - */ -import type { ReactElement } from 'react'; type FairUsageNoticeProps = { variant?: 'error' | 'muted'; @@ -13,7 +9,7 @@ type FairUsageNoticeProps = { * The fair usage notice component. * @param {FairUsageNoticeProps} props - Fair usage notice component props. * @param {FairUsageNoticeProps.variant} props.variant - The variant of the notice to render. - * @return {ReactElement} the Notice component with the fair usage message. + * @return the Notice component with the fair usage message. */ export const FairUsageNotice = ( { variant = 'error' }: FairUsageNoticeProps ) => { const useFairUsageNoticeMessageElement = useFairUsageNoticeMessage(); diff --git a/projects/js-packages/ai-client/src/logo-generator/store/reducer.ts b/projects/js-packages/ai-client/src/logo-generator/store/reducer.ts index 9322c3c21a0b4..bca78da21df96 100644 --- a/projects/js-packages/ai-client/src/logo-generator/store/reducer.ts +++ b/projects/js-packages/ai-client/src/logo-generator/store/reducer.ts @@ -30,12 +30,7 @@ import { ACTION_SET_IS_LOADING_HISTORY, } from './constants.js'; import INITIAL_STATE from './initial-state.js'; -import type { - AiFeatureStateProps, - LogoGeneratorStateProp, - RequestError, - TierLimitProp, -} from './types.js'; +import type { AiFeatureStateProps, RequestError, TierLimitProp } from './types.js'; import type { SiteDetails } from '../types.js'; /** diff --git a/projects/js-packages/charts/changelog/update-eslint-config-for-ts-files b/projects/js-packages/charts/changelog/update-eslint-config-for-ts-files new file mode 100644 index 0000000000000..fefec667583fd --- /dev/null +++ b/projects/js-packages/charts/changelog/update-eslint-config-for-ts-files @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed lints following ESLint rule changes for TS diff --git a/projects/js-packages/components/changelog/update-eslint-config-for-ts-files b/projects/js-packages/components/changelog/update-eslint-config-for-ts-files new file mode 100644 index 0000000000000..454c3dde47017 --- /dev/null +++ b/projects/js-packages/components/changelog/update-eslint-config-for-ts-files @@ -0,0 +1,5 @@ +Significance: patch +Type: fixed +Comment: Fixed ESLint warning + + diff --git a/projects/js-packages/components/components/boost-score-graph/annotations-plugin.ts b/projects/js-packages/components/components/boost-score-graph/annotations-plugin.ts index a0cfe290c84fc..5c4e51486e1b9 100644 --- a/projects/js-packages/components/components/boost-score-graph/annotations-plugin.ts +++ b/projects/js-packages/components/components/boost-score-graph/annotations-plugin.ts @@ -3,11 +3,12 @@ import { Annotation } from '.'; import './style-annotation.scss'; -// eslint-disable-next-line jsdoc/require-returns /** * Custom tooltips plugin for uPlot. * - * @param {Annotation[]} annotations - The periods to display in the tooltip. + * @param annotations - The periods to display in the tooltip. + * + * @return The plugin object. */ export function annotationsPlugin( annotations: Annotation[] ) { let containerEl, annotationsContainer; diff --git a/projects/js-packages/components/components/boost-score-graph/tooltips-plugin.ts b/projects/js-packages/components/components/boost-score-graph/tooltips-plugin.ts index 9ed7f91c1ecb4..88efb26f6b8b2 100644 --- a/projects/js-packages/components/components/boost-score-graph/tooltips-plugin.ts +++ b/projects/js-packages/components/components/boost-score-graph/tooltips-plugin.ts @@ -6,10 +6,10 @@ import { Period } from '.'; /** * Custom tooltips plugin for uPlot. * - * @param {Period[]} periods - The periods to display in the tooltip. - * @return {object} The uPlot plugin object with hooks. + * @param periods - The periods to display in the tooltip. + * @return The uPlot plugin object with hooks. */ -export function tooltipsPlugin( periods ) { +export function tooltipsPlugin( periods: Period[] ) { const reactRoot = document.createElement( 'div' ); const container = document.createElement( 'div' ); let reactDom; diff --git a/projects/packages/jetpack-mu-wpcom/changelog/update-eslint-config-for-ts-files b/projects/packages/jetpack-mu-wpcom/changelog/update-eslint-config-for-ts-files new file mode 100644 index 0000000000000..fefec667583fd --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/update-eslint-config-for-ts-files @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed lints following ESLint rule changes for TS diff --git a/projects/packages/jetpack-mu-wpcom/eslint.config.mjs b/projects/packages/jetpack-mu-wpcom/eslint.config.mjs index 7f678302d6651..de5062933307c 100644 --- a/projects/packages/jetpack-mu-wpcom/eslint.config.mjs +++ b/projects/packages/jetpack-mu-wpcom/eslint.config.mjs @@ -1,4 +1,4 @@ -import makeBaseConfig, { typescriptFiles, makeEnvConfig } from 'jetpack-js-tools/eslintrc/base.mjs'; +import makeBaseConfig, { makeEnvConfig } from 'jetpack-js-tools/eslintrc/base.mjs'; export default [ ...makeBaseConfig( import.meta.url ), @@ -9,12 +9,4 @@ export default [ 'react/jsx-no-bind': 'off', }, }, - { - files: typescriptFiles, - rules: { - // Not needed for TypeScript. - 'jsdoc/require-param-type': 'off', - 'jsdoc/require-returns-type': 'off', - }, - }, ]; diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx index 5ab7c3638f187..79a5c4238a446 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx @@ -9,7 +9,6 @@ import ShieldInactive from './assets/shield-inactive.svg'; import ShieldOff from './assets/shield-off.svg'; import ShieldSuccess from './assets/shield-success.svg'; import { useProtectTooltipCopy } from './use-protect-tooltip-copy'; -import type { ReactElement, PropsWithChildren } from 'react'; export const AutoFirewallStatus = () => { const slug = 'protect'; @@ -35,10 +34,10 @@ export const AutoFirewallStatus = () => { /** * WafStatus component * - * @param {PropsWithChildren} props - The component props - * @param {'active' | 'inactive' | 'off'} props.status - The status of the WAF + * @param props - The component props + * @param props.status - The status of the WAF * - * @return {ReactElement} rendered component + * @return rendered component */ function WafStatus( { status }: { status: 'active' | 'inactive' | 'off' } ) { const slug = 'protect'; diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx index 62af60d35f2bd..384fe1329ce4b 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx @@ -10,7 +10,6 @@ import baseStyles from '../style.module.scss'; import ShieldOff from './assets/shield-off.svg'; import ShieldPartial from './assets/shield-partial.svg'; import { useProtectTooltipCopy } from './use-protect-tooltip-copy'; -import type { ReactElement, PropsWithChildren } from 'react'; export const LoginsBlockedStatus = () => { const slug = 'protect'; @@ -43,10 +42,10 @@ export const LoginsBlockedStatus = () => { /** * BlockedStatus component * - * @param {PropsWithChildren} props - The component props - * @param {'active' | 'inactive' | 'off'} props.status - The status of Brute Force Protection + * @param props - The component props + * @param props.status - The status of Brute Force Protection * - * @return {ReactElement} rendered component + * @return rendered component */ function BlockedStatus( { status }: { status: 'active' | 'inactive' | 'off' } ) { const { diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx index 4349419dc95f3..af25fe770b8e0 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx @@ -14,7 +14,6 @@ import ShieldOff from './assets/shield-off.svg'; import ShieldPartial from './assets/shield-partial.svg'; import ShieldSuccess from './assets/shield-success.svg'; import { useProtectTooltipCopy } from './use-protect-tooltip-copy'; -import type { PropsWithChildren, ReactElement } from 'react'; export const ScanAndThreatStatus = () => { const slug = 'protect'; @@ -69,11 +68,11 @@ export const ScanAndThreatStatus = () => { /** * ThreatStatus component * - * @param {PropsWithChildren} props - The component props - * @param {number} props.numThreats - The number of threats - * @param {number} props.criticalThreatCount - The number of critical threats + * @param props - The component props + * @param props.numThreats - The number of threats + * @param props.criticalThreatCount - The number of critical threats * - * @return {ReactElement} rendered component + * @return rendered component */ function ThreatStatus( { numThreats, @@ -171,10 +170,10 @@ function ThreatStatus( { /** * ScanStatus component * - * @param {PropsWithChildren} props - The component props - * @param {'success' | 'partial' | 'off'} props.status - The number of threats + * @param props - The component props + * @param props.status - The number of threats * - * @return { ReactElement} rendered component + * @return rendered component */ function ScanStatus( { status }: { status: 'success' | 'partial' | 'off' } ) { const tooltipContent = useProtectTooltipCopy(); diff --git a/projects/packages/my-jetpack/_inc/data/use-simple-mutation.ts b/projects/packages/my-jetpack/_inc/data/use-simple-mutation.ts index 2dad31322e13a..2e6b056e68fce 100644 --- a/projects/packages/my-jetpack/_inc/data/use-simple-mutation.ts +++ b/projects/packages/my-jetpack/_inc/data/use-simple-mutation.ts @@ -2,12 +2,7 @@ import { useMutation } from '@tanstack/react-query'; import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; import { useFetchingErrorNotice } from './notices/use-fetching-error-notice'; -import type { - UseMutationOptions, - UseMutateFunction, - // This variable is being used as a type declaration - UseMutationResult, -} from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutateFunction } from '@tanstack/react-query'; import type { APIFetchOptions } from '@wordpress/api-fetch'; export type APIFetchOptionsWithQueryParams = APIFetchOptions & { @@ -33,7 +28,7 @@ export type MutateCallback = UseMutateFunction< * @param {APIFetchOptions} params.query - The options to be passed to the API fetch function for the mutation. * @param {Pick} [params.options] - Optional. Mutation options from react-query, currently supports only the 'onSuccess' option. * @param {string} [params.errorMessage] - Optional. A custom error message that can be displayed if the mutation fails. - * @return {UseMutationResult} The result object from the useMutation hook, containing data and state information about the mutation (e.g., isPending, isError). + * @return {import('@tanstack/react-query').UseMutationResult} The result object from the useMutation hook, containing data and state information about the mutation (e.g., isPending, isError). */ type QueryParams< T, E, V > = { diff --git a/projects/packages/my-jetpack/_inc/data/use-simple-query.ts b/projects/packages/my-jetpack/_inc/data/use-simple-query.ts index 541ec6b8ed4af..f42c3b802a86b 100644 --- a/projects/packages/my-jetpack/_inc/data/use-simple-query.ts +++ b/projects/packages/my-jetpack/_inc/data/use-simple-query.ts @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query'; import apiFetch from '@wordpress/api-fetch'; import { useFetchingErrorNotice } from './notices/use-fetching-error-notice'; import type { WP_Error } from './types'; -import type { UseQueryOptions, UseQueryResult } from '@tanstack/react-query'; +import type { UseQueryOptions } from '@tanstack/react-query'; import type { APIFetchOptions } from '@wordpress/api-fetch'; /** @@ -18,7 +18,7 @@ import type { APIFetchOptions } from '@wordpress/api-fetch'; * @param {APIFetchOptions} params.query - The options to be passed to the API fetch function. * @param {Pick} [params.options] - Optional. Query options from react-query, currently supports only the 'enabled' option. * @param {string} [params.errorMessage] - Optional. A custom error message that can be displayed if the query fails. - * @return {UseQueryResult} The result object from the useQuery hook, containing data and state information about the query (e.g., isLoading, isError). + * @return {import('@tanstack/react-query').UseQueryResult} The result object from the useQuery hook, containing data and state information about the query (e.g., isLoading, isError). */ type QueryParams = { name: string; diff --git a/projects/packages/my-jetpack/changelog/update-eslint-config-for-ts-files b/projects/packages/my-jetpack/changelog/update-eslint-config-for-ts-files new file mode 100644 index 0000000000000..fefec667583fd --- /dev/null +++ b/projects/packages/my-jetpack/changelog/update-eslint-config-for-ts-files @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed lints following ESLint rule changes for TS diff --git a/projects/packages/videopress/changelog/update-eslint-config-for-ts-files b/projects/packages/videopress/changelog/update-eslint-config-for-ts-files new file mode 100644 index 0000000000000..fefec667583fd --- /dev/null +++ b/projects/packages/videopress/changelog/update-eslint-config-for-ts-files @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed lints following ESLint rule changes for TS diff --git a/projects/packages/videopress/src/client/admin/components/video-thumbnail-selector-modal/index.tsx b/projects/packages/videopress/src/client/admin/components/video-thumbnail-selector-modal/index.tsx index b6f57cd06f4ec..2df7e69402573 100644 --- a/projects/packages/videopress/src/client/admin/components/video-thumbnail-selector-modal/index.tsx +++ b/projects/packages/videopress/src/client/admin/components/video-thumbnail-selector-modal/index.tsx @@ -5,7 +5,7 @@ import { Button, ThemeProvider, useBreakpointMatch } from '@automattic/jetpack-c import { Modal } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import clsx from 'clsx'; -import { useState, ReactNode } from 'react'; +import { useState } from 'react'; /** * Internal dependencies */ @@ -17,7 +17,7 @@ import { VideoThumbnailSelectorModalProps } from './types'; * Video Thumbnail Selector component * * @param {VideoThumbnailSelectorModalProps} props - Component props. - * @return {ReactNode} - VideoThumbnailSelector react component. + * @return - VideoThumbnailSelector react component. */ const VideoThumbnailSelectorModal = ( { url, diff --git a/projects/packages/videopress/src/client/admin/components/video-upload-area/index.tsx b/projects/packages/videopress/src/client/admin/components/video-upload-area/index.tsx index 6631bba41c3ef..8ef3dbf654ad3 100644 --- a/projects/packages/videopress/src/client/admin/components/video-upload-area/index.tsx +++ b/projects/packages/videopress/src/client/admin/components/video-upload-area/index.tsx @@ -13,16 +13,12 @@ import { usePlan } from '../../hooks/use-plan'; import useSelectVideoFiles from '../../hooks/use-select-video-files'; import styles from './style.module.scss'; import { VideoUploadAreaProps } from './types'; -/** - * Types - */ -import type { ReactNode } from 'react'; /** * Video Upload Area component * * @param {VideoUploadAreaProps} props - Component props. - * @return {ReactNode} - VideoUploadArea react component. + * @return - VideoUploadArea react component. */ const VideoUploadArea = ( { className, onSelectFiles }: VideoUploadAreaProps ) => { const [ isSm ] = useBreakpointMatch( 'sm' ); diff --git a/projects/plugins/crm/changelog/update-eslint-config-for-ts-files b/projects/plugins/crm/changelog/update-eslint-config-for-ts-files new file mode 100644 index 0000000000000..fefec667583fd --- /dev/null +++ b/projects/plugins/crm/changelog/update-eslint-config-for-ts-files @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed lints following ESLint rule changes for TS diff --git a/projects/plugins/crm/src/js/data/hooks/queries.ts b/projects/plugins/crm/src/js/data/hooks/queries.ts index 39fd92d046965..818b74e80e79d 100644 --- a/projects/plugins/crm/src/js/data/hooks/queries.ts +++ b/projects/plugins/crm/src/js/data/hooks/queries.ts @@ -1,4 +1,4 @@ -import { useQuery, UseQueryResult } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import { getAutomationWorkflows } from 'crm/data/query-functions'; import { Workflow } from 'crm/state/automations-admin/types'; @@ -6,7 +6,7 @@ import { Workflow } from 'crm/state/automations-admin/types'; * Gets the Automation Workflows. * * @param {Function} hydrate - A function which takes an array of workflows and hydrates the store with them. - * @return {UseQueryResult} - The result of the query. + * @return {import('@tanstack/react-query').UseQueryResult} - The result of the query. */ export const useGetAutomationWorkflows = ( hydrate: ( workflows: Workflow[] ) => void ) => useQuery( { diff --git a/projects/plugins/crm/src/js/state/automations-admin/util.ts b/projects/plugins/crm/src/js/state/automations-admin/util.ts index 3f863aad39b8f..5be9d472a9746 100644 --- a/projects/plugins/crm/src/js/state/automations-admin/util.ts +++ b/projects/plugins/crm/src/js/state/automations-admin/util.ts @@ -1,10 +1,10 @@ -import { Workflow, ServerPreparedWorkflow } from 'crm/state/automations-admin/types'; +import { Workflow } from 'crm/state/automations-admin/types'; /** * Gets a workflow which has been prepared for sending to the server. * * @param {Workflow} workflow - The workflow to prepare - * @return {ServerPreparedWorkflow} The prepared workflow + * @return {import('crm/state/automations-admin/types').ServerPreparedWorkflow} The prepared workflow */ export const getServerPreparedWorkflow = ( workflow: Workflow ) => { return { diff --git a/projects/plugins/jetpack/changelog/update-eslint-config-for-ts-files b/projects/plugins/jetpack/changelog/update-eslint-config-for-ts-files new file mode 100644 index 0000000000000..fb360f8080b3e --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-eslint-config-for-ts-files @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Fixed lints following ESLint rule changes for TS diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/components/ai-assistant-toolbar-dropdown/index.tsx b/projects/plugins/jetpack/extensions/blocks/ai-assistant/components/ai-assistant-toolbar-dropdown/index.tsx index 8f30ed0e5f9c4..d2aea65e0221b 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/components/ai-assistant-toolbar-dropdown/index.tsx +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/components/ai-assistant-toolbar-dropdown/index.tsx @@ -23,7 +23,6 @@ import './style.scss'; import type { AiAssistantDropdownOnChangeOptionsArgProps } from './dropdown-content'; import type { ExtendedBlockProp } from '../../extensions/constants'; import type { PromptTypeProp } from '../../lib/prompt'; -import type { ReactElement } from 'react'; const debug = debugFactory( 'jetpack-ai-assistant:dropdown' ); @@ -35,7 +34,7 @@ type AiAssistantBlockToolbarDropdownContentProps = { /** * The dropdown component with logic for the AI Assistant block. * @param {AiAssistantBlockToolbarDropdownContentProps} props - The props. - * @return {ReactElement} The React content of the dropdown. + * @return The React content of the dropdown. */ function AiAssistantBlockToolbarDropdownContent( { onClose, @@ -165,7 +164,7 @@ type AiAssistantBlockToolbarDropdownProps = { /** * The AI Assistant dropdown component. * @param {AiAssistantBlockToolbarDropdownProps} props - The props. - * @return {ReactElement} The AI Assistant dropdown component. + * @return The AI Assistant dropdown component. */ export default function AiAssistantBlockToolbarDropdown( { blockType, diff --git a/tools/js-tools/eslintrc/base.mjs b/tools/js-tools/eslintrc/base.mjs index cc5937304c99c..8f0228389bed9 100644 --- a/tools/js-tools/eslintrc/base.mjs +++ b/tools/js-tools/eslintrc/base.mjs @@ -325,6 +325,13 @@ export default function makeBaseConfig( configurl, opts = {} ) { 'error', { allowInterfaces: 'with-single-extends' }, ], + // Mark types in jsdoc as used without reporting about any that are undefined. + 'jsdoc/no-undefined-types': [ 'warn', { disableReporting: true } ], + // TS should mostly have the type set. + 'jsdoc/require-param-type': 'off', + 'jsdoc/require-property-type': 'off', + // Let us use TS return type for better inference + 'jsdoc/require-returns-type': 'off', }, } ), // Jest. From 6ff35a2ab685a07f2a84fdd946f7f5094efdffa1 Mon Sep 17 00:00:00 2001 From: Miguel Torres <1233880+mmtr@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:38:54 +0100 Subject: [PATCH 03/63] Duplicate views: Update experiment name (#40597) --- .../changelog/update-duplicate-views-experiment-name | 4 ++++ .../features/wpcom-admin-interface/wpcom-admin-interface.php | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/update-duplicate-views-experiment-name diff --git a/projects/packages/jetpack-mu-wpcom/changelog/update-duplicate-views-experiment-name b/projects/packages/jetpack-mu-wpcom/changelog/update-duplicate-views-experiment-name new file mode 100644 index 0000000000000..05ffe09f19e66 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/update-duplicate-views-experiment-name @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + + diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php index ae71951c29798..1e1b3c0d5e033 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php @@ -358,9 +358,8 @@ function wpcom_show_admin_interface_notice() { * @return boolean */ function wpcom_is_duplicate_views_experiment_enabled() { - // TODO: We don't know yet the experiment name. $experiment_platform = 'calypso'; - $experiment_name = "{$experiment_platform}_duplicate_views_placeholder"; + $experiment_name = "{$experiment_platform}_post_onboarding_holdout_120924"; static $is_enabled = null; if ( $is_enabled !== null ) { From 9e30ed67469c7112f464015d49701106caf6c962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gergely=20M=C3=A1rk=20Juh=C3=A1sz?= <36671565+gmjuhasz@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:26:50 +0100 Subject: [PATCH 04/63] Social: Don't call endpoint for services on WPCOM (#40596) * Don't make request if we are on WPCOM * changelog * Use transient * Refactor to own class * Remove param from script data --- ...te-social-script-data-services-wpcom-check | 4 + .../src/class-publicize-script-data.php | 26 +----- .../packages/publicize/src/class-services.php | 84 +++++++++++++++++++ 3 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 projects/packages/publicize/changelog/update-social-script-data-services-wpcom-check create mode 100644 projects/packages/publicize/src/class-services.php diff --git a/projects/packages/publicize/changelog/update-social-script-data-services-wpcom-check b/projects/packages/publicize/changelog/update-social-script-data-services-wpcom-check new file mode 100644 index 0000000000000..2334298f97e5a --- /dev/null +++ b/projects/packages/publicize/changelog/update-social-script-data-services-wpcom-check @@ -0,0 +1,4 @@ +Significance: minor +Type: changed + +Script data: Don't call service endpoint on wpcom diff --git a/projects/packages/publicize/src/class-publicize-script-data.php b/projects/packages/publicize/src/class-publicize-script-data.php index 1b285f1d09801..b4e7dded6b62d 100644 --- a/projects/packages/publicize/src/class-publicize-script-data.php +++ b/projects/packages/publicize/src/class-publicize-script-data.php @@ -7,11 +7,11 @@ namespace Automattic\Jetpack\Publicize; -use Automattic\Jetpack\Connection\Client; use Automattic\Jetpack\Connection\Manager; use Automattic\Jetpack\Current_Plan; use Automattic\Jetpack\Publicize\Jetpack_Social_Settings\Settings; use Automattic\Jetpack\Publicize\Publicize_Utils as Utils; +use Automattic\Jetpack\Publicize\Services as Publicize_Services; use Automattic\Jetpack\Status; use Automattic\Jetpack\Status\Host; use Jetpack_Options; @@ -21,6 +21,8 @@ */ class Publicize_Script_Data { + const SERVICES_TRANSIENT = 'jetpack_social_services_list'; + /** * Get the publicize instance - properly typed * @@ -224,27 +226,7 @@ public static function get_shares_data() { * @return array List of external services and their settings. */ public static function get_supported_services() { - $site_id = Manager::get_site_id(); - if ( is_wp_error( $site_id ) ) { - return array(); - } - $path = sprintf( '/sites/%d/external-services', $site_id ); - $response = Client::wpcom_json_api_request_as_user( $path ); - if ( is_wp_error( $response ) ) { - return array(); - } - $body = json_decode( wp_remote_retrieve_body( $response ) ); - - $services = $body->services ?? array(); - - return array_values( - array_filter( - (array) $services, - function ( $service ) { - return isset( $service->type ) && 'publicize' === $service->type; - } - ) - ); + return Publicize_Services::get_all(); } /** diff --git a/projects/packages/publicize/src/class-services.php b/projects/packages/publicize/src/class-services.php new file mode 100644 index 0000000000000..26e49b096b40b --- /dev/null +++ b/projects/packages/publicize/src/class-services.php @@ -0,0 +1,84 @@ +get_external_services_list( 'publicize', get_current_blog_id() ) ); + + return $services; + } + + // Checking the cache. + $services = get_transient( self::SERVICES_TRANSIENT ); + if ( false !== $services && ! $force_refresh ) { + return $services; + } + + return self::fetch_and_cache_services(); + } + + /** + * Fetch services from the REST API and cache them. + * + * @return array + */ + public static function fetch_and_cache_services() { + // Fetch the services. + $site_id = Manager::get_site_id(); + if ( is_wp_error( $site_id ) ) { + return array(); + } + $path = sprintf( '/sites/%d/external-services', $site_id ); + $response = Client::wpcom_json_api_request_as_user( $path ); + if ( is_wp_error( $response ) ) { + return array(); + } + $body = json_decode( wp_remote_retrieve_body( $response ) ); + + $services = $body->services ?? array(); + + $formatted_services = array_values( + array_filter( + (array) $services, + function ( $service ) { + return isset( $service->type ) && 'publicize' === $service->type; + } + ) + ); + + if ( ! empty( $formatted_services ) ) { + set_transient( self::SERVICES_TRANSIENT, $formatted_services, DAY_IN_SECONDS ); + } + + return $formatted_services; + } +} From 7af187e52bc46d5bec76896ac918c41ad4f386ae Mon Sep 17 00:00:00 2001 From: Miguel Torres <1233880+mmtr@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:06:53 +0100 Subject: [PATCH 05/63] Duplicate views: Hide "View" switcher (#40595) --- .../update-hide-switcher-duplicated-views | 4 ++++ .../wpcom-admin-interface.php | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/update-hide-switcher-duplicated-views diff --git a/projects/packages/jetpack-mu-wpcom/changelog/update-hide-switcher-duplicated-views b/projects/packages/jetpack-mu-wpcom/changelog/update-hide-switcher-duplicated-views new file mode 100644 index 0000000000000..6c2c108ea3e4b --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/update-hide-switcher-duplicated-views @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Admin menu: Hide dashboard switcher when WP Admin view is enforced diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php index 1e1b3c0d5e033..cabfcca1dfe58 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php @@ -191,6 +191,26 @@ function wpcom_admin_get_user_option_jetpack( $value ) { add_filter( 'get_user_option_jetpack_admin_menu_preferred_views', 'wpcom_admin_get_user_option_jetpack' ); add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option', 10 ); +/** + * Hides the "View" switcher on WP Admin screens enforced by the "Remove duplicate views" experiment. + */ +function wpcom_duplicate_views_hide_view_switcher() { + if ( ! function_exists( '\Automattic\Jetpack\Masterbar\get_admin_menu_class' ) || ! function_exists( '\Automattic\Jetpack\Masterbar\should_customize_nav' ) ) { + return; + } + + $admin_menu_class = apply_filters( 'jetpack_admin_menu_class', \Automattic\Jetpack\Masterbar\get_admin_menu_class() ); + if ( \Automattic\Jetpack\Masterbar\should_customize_nav( $admin_menu_class ) ) { + $admin_menu = $admin_menu_class::get_instance(); + + $current_screen = wpcom_admin_get_current_screen(); + if ( in_array( $current_screen, WPCOM_DUPLICATED_VIEW, true ) && wpcom_is_duplicate_views_experiment_enabled() ) { + remove_filter( 'in_admin_header', array( $admin_menu, 'add_dashboard_switcher' ) ); + } + } +} +add_action( 'admin_init', 'wpcom_duplicate_views_hide_view_switcher' ); + /** * Determines whether the admin interface has been recently changed by checking the presence of the `admin-interface-changed` query param. * From b007df89a7a5d67417b2f88c18e45f13f1ef2309 Mon Sep 17 00:00:00 2001 From: Igor Zinovyev Date: Mon, 16 Dec 2024 19:10:31 +0300 Subject: [PATCH 06/63] Changelog and readme.txt edits. (#40627) --- projects/js-packages/ai-client/CHANGELOG.md | 16 ++- .../changelog/renovate-eslint-packages | 5 - .../ai-client/changelog/update-eslint-9 | 5 - .../update-eslint-config-for-ts-files | 4 - projects/js-packages/ai-client/package.json | 2 +- projects/js-packages/api/CHANGELOG.md | 5 + .../api/changelog/renovate-wordpress-monorepo | 4 - .../js-packages/api/changelog/update-eslint-9 | 5 - projects/js-packages/api/package.json | 2 +- .../CHANGELOG.md | 5 + .../changelog/update-eslint-9 | 5 - .../package.json | 2 +- projects/js-packages/base-styles/CHANGELOG.md | 5 + .../base-styles/changelog/update-eslint-9 | 5 - projects/js-packages/base-styles/package.json | 2 +- .../js-packages/boost-score-api/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../boost-score-api/changelog/update-eslint-9 | 5 - .../js-packages/boost-score-api/package.json | 2 +- projects/js-packages/components/CHANGELOG.md | 9 ++ ...ponents-threats-data-views-default-layouts | 4 - .../fix-toggle-control-help-text-indent | 4 - .../changelog/renovate-wordpress-monorepo | 4 - .../changelog/renovate-wordpress-monorepo#2 | 4 - .../components/changelog/update-eslint-9 | 5 - .../update-eslint-config-for-ts-files | 5 - projects/js-packages/components/package.json | 2 +- projects/js-packages/connection/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../connection/changelog/update-eslint-9 | 5 - projects/js-packages/connection/package.json | 2 +- .../i18n-check-webpack-plugin/CHANGELOG.md | 5 + .../changelog/update-eslint-9 | 5 - .../i18n-check-webpack-plugin/package.json | 2 +- .../i18n-loader-webpack-plugin/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../changelog/update-eslint-9 | 5 - .../i18n-loader-webpack-plugin/package.json | 2 +- projects/js-packages/idc/CHANGELOG.md | 4 + .../idc/changelog/renovate-wordpress-monorepo | 4 - .../js-packages/idc/changelog/update-eslint-9 | 5 - projects/js-packages/idc/package.json | 2 +- projects/js-packages/licensing/CHANGELOG.md | 4 + .../changelog/renovate-wordpress-monorepo | 4 - .../licensing/changelog/update-eslint-9 | 5 - projects/js-packages/licensing/package.json | 2 +- .../js-packages/partner-coupon/CHANGELOG.md | 4 + .../changelog/renovate-wordpress-monorepo | 4 - .../partner-coupon/changelog/update-eslint-9 | 5 - .../js-packages/partner-coupon/package.json | 2 +- .../publicize-components/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../changelog/renovate-wordpress-monorepo#2 | 4 - .../changelog/update-eslint-9 | 5 - .../publicize-components/package.json | 2 +- .../remove-asset-webpack-plugin/CHANGELOG.md | 5 + .../changelog/update-eslint-9 | 5 - .../remove-asset-webpack-plugin/package.json | 2 +- projects/js-packages/scan/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../scan/changelog/update-eslint-9 | 5 - projects/js-packages/scan/package.json | 2 +- projects/js-packages/script-data/CHANGELOG.md | 5 + .../script-data/changelog/update-eslint-9 | 5 - projects/js-packages/script-data/package.json | 2 +- .../shared-extension-utils/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../changelog/update-eslint-9 | 5 - .../shared-extension-utils/package.json | 2 +- .../js-packages/social-logos/CHANGELOG.md | 5 + .../social-logos/changelog/update-eslint-9 | 5 - .../js-packages/social-logos/package.json | 2 +- .../js-packages/webpack-config/CHANGELOG.md | 4 + .../changelog/renovate-wordpress-monorepo | 4 - .../webpack-config/changelog/update-eslint-9 | 5 - .../js-packages/webpack-config/package.json | 2 +- projects/packages/assets/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../packages/assets/changelog/update-eslint-9 | 5 - projects/packages/backup/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../packages/backup/changelog/update-eslint-9 | 5 - .../update-packages-fix-eslint-9-lints | 5 - .../backup/src/class-package-version.php | 2 +- projects/packages/blaze/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../packages/blaze/changelog/update-eslint-9 | 5 - projects/packages/blaze/package.json | 2 +- .../packages/blaze/src/class-dashboard.php | 2 +- .../classic-theme-helper/CHANGELOG.md | 11 ++ ...e-testimonials-cpt-in-classic-theme-helper | 4 - .../add-testimonial-shortcode-css-package | 4 - .../changelog/renovate-wordpress-monorepo | 4 - .../changelog/update-eslint-9 | 5 - .../classic-theme-helper/composer.json | 2 +- .../classic-theme-helper/package.json | 2 +- .../classic-theme-helper/src/class-main.php | 2 +- projects/packages/connection/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../connection/changelog/update-eslint-9 | 5 - .../connection/src/class-package-version.php | 2 +- projects/packages/explat/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - projects/packages/explat/package.json | 2 +- projects/packages/explat/src/class-explat.php | 2 +- projects/packages/forms/CHANGELOG.md | 8 ++ ...fix-form-block-broken-url-input-validation | 4 - .../changelog/renovate-wordpress-monorepo | 4 - .../packages/forms/changelog/update-eslint-9 | 5 - .../update-packages-fix-eslint-9-lints | 5 - projects/packages/forms/package.json | 2 +- .../forms/src/class-jetpack-forms.php | 2 +- projects/packages/image-cdn/CHANGELOG.md | 5 + .../changelog/renovate-lock-file-maintenance | 5 - .../image-cdn/src/class-image-cdn.php | 2 +- projects/packages/import/CHANGELOG.md | 5 + .../import/changelog/fix-wp-import-issue | 4 - projects/packages/import/package.json | 2 +- projects/packages/import/src/class-main.php | 2 +- projects/packages/jitm/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../packages/jitm/changelog/update-eslint-9 | 5 - projects/packages/jitm/src/class-jitm.php | 2 +- projects/packages/masterbar/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../masterbar/changelog/update-eslint-9 | 5 - .../update-packages-fix-eslint-9-lints | 5 - projects/packages/masterbar/package.json | 2 +- .../packages/masterbar/src/class-main.php | 2 +- projects/packages/my-jetpack/CHANGELOG.md | 13 +++ .../changelog/add-ai-to-complete-feature-copy | 4 - .../changelog/fix-connection-animation-flick | 4 - .../changelog/renovate-wordpress-monorepo | 4 - .../my-jetpack/changelog/update-eslint-9 | 5 - .../update-eslint-config-for-ts-files | 4 - ...purchases-elements-when-complete-is-active | 4 - .../update-packages-fix-eslint-9-lints | 5 - projects/packages/my-jetpack/package.json | 2 +- .../my-jetpack/src/class-initializer.php | 2 +- projects/packages/post-list/CHANGELOG.md | 5 + .../post-list/changelog/update-eslint-9 | 5 - .../post-list/src/class-post-list.php | 2 +- projects/packages/publicize/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../publicize/changelog/update-eslint-9 | 5 - projects/packages/publicize/package.json | 2 +- projects/packages/search/CHANGELOG.md | 8 ++ .../fix-missing-search-overlay-in-some-themes | 4 - .../changelog/renovate-wordpress-monorepo | 4 - .../packages/search/changelog/update-eslint-9 | 5 - .../update-packages-fix-eslint-9-lints | 5 - projects/packages/search/package.json | 2 +- .../packages/search/src/class-package.php | 2 +- projects/packages/sync/CHANGELOG.md | 5 + .../add-allow-coupons-gifts-cpt-types | 4 - .../sync/src/class-package-version.php | 2 +- projects/packages/videopress/CHANGELOG.md | 8 ++ .../changelog/renovate-wordpress-monorepo | 4 - .../videopress/changelog/update-eslint-9 | 5 - .../update-eslint-config-for-ts-files | 4 - .../update-packages-fix-eslint-9-lints | 5 - projects/packages/videopress/package.json | 2 +- .../videopress/src/class-package-version.php | 2 +- projects/packages/wordads/CHANGELOG.md | 5 + .../changelog/renovate-wordpress-monorepo | 4 - .../wordads/changelog/update-eslint-9 | 5 - .../update-packages-fix-eslint-9-lints | 5 - projects/packages/wordads/package.json | 2 +- .../packages/wordads/src/class-package.php | 2 +- .../changelog/prerelease#13} | 1 + .../classic-theme-helper-plugin/composer.lock | 4 +- projects/plugins/jetpack/CHANGELOG.md | 25 ++++ .../add-allow-coupons-gifts-cpt-types | 4 - .../jetpack/changelog/add-comment-type-filter | 4 - .../add-pre-option-filter-duplicate-views | 5 - ...e-testimonials-cpt-in-classic-theme-helper | 4 - .../change-jetpack-ai-track-ai-rating | 4 - .../jetpack/changelog/disable-bloganuary | 4 - .../changelog/fix-carousel-null-request | 4 - .../fix-facebook-embed-background-color | 4 - ...fix-form-block-broken-url-input-validation | 4 - .../changelog/fix-hide-seo-title-desc-fields | 4 - .../changelog/fix-manual-plugin-updates | 4 - .../fix-pexels-empty-items-on-editor | 4 - .../jetpack/changelog/fix-wp-import-issue | 4 - .../remove-cleanup-infinite-scroll-2 | 4 - .../changelog/renovate-lock-file-maintenance | 4 - .../changelog/renovate-wordpress-monorepo | 4 - .../plugins/jetpack/changelog/update-eslint-9 | 5 - .../update-eslint-config-for-ts-files | 4 - .../update-jetpack-ai-feedback-add-tooltips | 4 - .../update-jetpack-fix-eslint-9-lints | 5 - .../changelog/update-likes-add-likes-class | 4 - .../jetpack/changelog/verbum-fix-stuff | 3 - projects/plugins/jetpack/composer.json | 2 +- projects/plugins/jetpack/composer.lock | 4 +- projects/plugins/jetpack/jetpack.php | 4 +- .../class.wpcom-json-api-comment-endpoint.php | 2 +- .../modules/custom-post-types/testimonial.php | 110 +++++++++--------- projects/plugins/jetpack/package.json | 2 +- projects/plugins/jetpack/readme.txt | 15 ++- .../mu-wpcom-plugin/changelog/prerelease#6} | 3 +- .../plugins/mu-wpcom-plugin/composer.lock | 4 +- .../wpcomsh/changelog/prerelease#2} | 3 +- projects/plugins/wpcomsh/composer.lock | 4 +- 205 files changed, 367 insertions(+), 602 deletions(-) delete mode 100644 projects/js-packages/ai-client/changelog/renovate-eslint-packages delete mode 100644 projects/js-packages/ai-client/changelog/update-eslint-9 delete mode 100644 projects/js-packages/ai-client/changelog/update-eslint-config-for-ts-files delete mode 100644 projects/js-packages/api/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/api/changelog/update-eslint-9 delete mode 100644 projects/js-packages/babel-plugin-replace-textdomain/changelog/update-eslint-9 delete mode 100644 projects/js-packages/base-styles/changelog/update-eslint-9 delete mode 100644 projects/js-packages/boost-score-api/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/boost-score-api/changelog/update-eslint-9 delete mode 100644 projects/js-packages/components/changelog/fix-components-threats-data-views-default-layouts delete mode 100644 projects/js-packages/components/changelog/fix-toggle-control-help-text-indent delete mode 100644 projects/js-packages/components/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/components/changelog/renovate-wordpress-monorepo#2 delete mode 100644 projects/js-packages/components/changelog/update-eslint-9 delete mode 100644 projects/js-packages/components/changelog/update-eslint-config-for-ts-files delete mode 100644 projects/js-packages/connection/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/connection/changelog/update-eslint-9 delete mode 100644 projects/js-packages/i18n-check-webpack-plugin/changelog/update-eslint-9 delete mode 100644 projects/js-packages/i18n-loader-webpack-plugin/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/i18n-loader-webpack-plugin/changelog/update-eslint-9 delete mode 100644 projects/js-packages/idc/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/idc/changelog/update-eslint-9 delete mode 100644 projects/js-packages/licensing/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/licensing/changelog/update-eslint-9 delete mode 100644 projects/js-packages/partner-coupon/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/partner-coupon/changelog/update-eslint-9 delete mode 100644 projects/js-packages/publicize-components/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/publicize-components/changelog/renovate-wordpress-monorepo#2 delete mode 100644 projects/js-packages/publicize-components/changelog/update-eslint-9 delete mode 100644 projects/js-packages/remove-asset-webpack-plugin/changelog/update-eslint-9 delete mode 100644 projects/js-packages/scan/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/scan/changelog/update-eslint-9 delete mode 100644 projects/js-packages/script-data/changelog/update-eslint-9 delete mode 100644 projects/js-packages/shared-extension-utils/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/shared-extension-utils/changelog/update-eslint-9 delete mode 100644 projects/js-packages/social-logos/changelog/update-eslint-9 delete mode 100644 projects/js-packages/webpack-config/changelog/renovate-wordpress-monorepo delete mode 100644 projects/js-packages/webpack-config/changelog/update-eslint-9 delete mode 100644 projects/packages/assets/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/assets/changelog/update-eslint-9 delete mode 100644 projects/packages/backup/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/backup/changelog/update-eslint-9 delete mode 100644 projects/packages/backup/changelog/update-packages-fix-eslint-9-lints delete mode 100644 projects/packages/blaze/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/blaze/changelog/update-eslint-9 delete mode 100644 projects/packages/classic-theme-helper/changelog/add-require-testimonials-cpt-in-classic-theme-helper delete mode 100644 projects/packages/classic-theme-helper/changelog/add-testimonial-shortcode-css-package delete mode 100644 projects/packages/classic-theme-helper/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/classic-theme-helper/changelog/update-eslint-9 delete mode 100644 projects/packages/connection/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/connection/changelog/update-eslint-9 delete mode 100644 projects/packages/explat/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/forms/changelog/fix-form-block-broken-url-input-validation delete mode 100644 projects/packages/forms/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/forms/changelog/update-eslint-9 delete mode 100644 projects/packages/forms/changelog/update-packages-fix-eslint-9-lints delete mode 100644 projects/packages/image-cdn/changelog/renovate-lock-file-maintenance delete mode 100644 projects/packages/import/changelog/fix-wp-import-issue delete mode 100644 projects/packages/jitm/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/jitm/changelog/update-eslint-9 delete mode 100644 projects/packages/masterbar/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/masterbar/changelog/update-eslint-9 delete mode 100644 projects/packages/masterbar/changelog/update-packages-fix-eslint-9-lints delete mode 100644 projects/packages/my-jetpack/changelog/add-ai-to-complete-feature-copy delete mode 100644 projects/packages/my-jetpack/changelog/fix-connection-animation-flick delete mode 100644 projects/packages/my-jetpack/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/my-jetpack/changelog/update-eslint-9 delete mode 100644 projects/packages/my-jetpack/changelog/update-eslint-config-for-ts-files delete mode 100644 projects/packages/my-jetpack/changelog/update-my-jetpack-hide-purchases-elements-when-complete-is-active delete mode 100644 projects/packages/my-jetpack/changelog/update-packages-fix-eslint-9-lints delete mode 100644 projects/packages/post-list/changelog/update-eslint-9 delete mode 100644 projects/packages/publicize/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/publicize/changelog/update-eslint-9 delete mode 100644 projects/packages/search/changelog/fix-missing-search-overlay-in-some-themes delete mode 100644 projects/packages/search/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/search/changelog/update-eslint-9 delete mode 100644 projects/packages/search/changelog/update-packages-fix-eslint-9-lints delete mode 100644 projects/packages/sync/changelog/add-allow-coupons-gifts-cpt-types delete mode 100644 projects/packages/videopress/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/videopress/changelog/update-eslint-9 delete mode 100644 projects/packages/videopress/changelog/update-eslint-config-for-ts-files delete mode 100644 projects/packages/videopress/changelog/update-packages-fix-eslint-9-lints delete mode 100644 projects/packages/wordads/changelog/renovate-wordpress-monorepo delete mode 100644 projects/packages/wordads/changelog/update-eslint-9 delete mode 100644 projects/packages/wordads/changelog/update-packages-fix-eslint-9-lints rename projects/{packages/masterbar/changelog/add-pre-option-filter-duplicate-views => plugins/classic-theme-helper-plugin/changelog/prerelease#13} (52%) delete mode 100644 projects/plugins/jetpack/changelog/add-allow-coupons-gifts-cpt-types delete mode 100644 projects/plugins/jetpack/changelog/add-comment-type-filter delete mode 100644 projects/plugins/jetpack/changelog/add-pre-option-filter-duplicate-views delete mode 100644 projects/plugins/jetpack/changelog/add-require-testimonials-cpt-in-classic-theme-helper delete mode 100644 projects/plugins/jetpack/changelog/change-jetpack-ai-track-ai-rating delete mode 100644 projects/plugins/jetpack/changelog/disable-bloganuary delete mode 100644 projects/plugins/jetpack/changelog/fix-carousel-null-request delete mode 100644 projects/plugins/jetpack/changelog/fix-facebook-embed-background-color delete mode 100644 projects/plugins/jetpack/changelog/fix-form-block-broken-url-input-validation delete mode 100644 projects/plugins/jetpack/changelog/fix-hide-seo-title-desc-fields delete mode 100644 projects/plugins/jetpack/changelog/fix-manual-plugin-updates delete mode 100644 projects/plugins/jetpack/changelog/fix-pexels-empty-items-on-editor delete mode 100644 projects/plugins/jetpack/changelog/fix-wp-import-issue delete mode 100644 projects/plugins/jetpack/changelog/remove-cleanup-infinite-scroll-2 delete mode 100644 projects/plugins/jetpack/changelog/renovate-lock-file-maintenance delete mode 100644 projects/plugins/jetpack/changelog/renovate-wordpress-monorepo delete mode 100644 projects/plugins/jetpack/changelog/update-eslint-9 delete mode 100644 projects/plugins/jetpack/changelog/update-eslint-config-for-ts-files delete mode 100644 projects/plugins/jetpack/changelog/update-jetpack-ai-feedback-add-tooltips delete mode 100644 projects/plugins/jetpack/changelog/update-jetpack-fix-eslint-9-lints delete mode 100644 projects/plugins/jetpack/changelog/update-likes-add-likes-class delete mode 100644 projects/plugins/jetpack/changelog/verbum-fix-stuff rename projects/{js-packages/ai-client/changelog/renovate-wordpress-monorepo => plugins/mu-wpcom-plugin/changelog/prerelease#6} (51%) rename projects/{js-packages/base-styles/changelog/renovate-wordpress-monorepo => plugins/wpcomsh/changelog/prerelease#2} (51%) diff --git a/projects/js-packages/ai-client/CHANGELOG.md b/projects/js-packages/ai-client/CHANGELOG.md index 78730b50aca6a..7fb63198f9391 100644 --- a/projects/js-packages/ai-client/CHANGELOG.md +++ b/projects/js-packages/ai-client/CHANGELOG.md @@ -5,22 +5,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.25.2] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + +### Fixed +- Fixed lints following ESLint rule changes for TS. [#40584] + ## [0.25.1] - 2024-12-09 ### Changed -- AI Assistant: Add disclaimer to image generation modals [#40397] +- AI Assistant: Add disclaimer to image generation modals. [#40397] - Updated package dependencies. [#40363] ## [0.25.0] - 2024-11-25 ### Added -- AI Client: split disabled prop to allow disabling input and action button separately [#40210] +- AI Client: split disabled prop to allow disabling input and action button separately. [#40210] ### Changed -- AI Client: fix prompt cursor to text when editable [#40247] +- AI Client: fix prompt cursor to text when editable. [#40247] - Updated package dependencies. [#40288] ## [0.24.3] - 2024-11-18 ### Changed -- AI Client: add effect on AiModalInputPrompt to update/set prompt on prop update [#40113] +- AI Client: add effect on AiModalInputPrompt to update/set prompt on prop update. [#40113] ## [0.24.2] - 2024-11-11 ### Changed @@ -476,6 +483,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - AI Client: stop using smart document visibility handling on the fetchEventSource library, so it does not restart the completion when changing tabs. [#32004] - Updated package dependencies. [#31468] [#31659] [#31785] +[0.25.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.25.1...v0.25.2 [0.25.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.25.0...v0.25.1 [0.25.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.24.3...v0.25.0 [0.24.3]: https://github.com/Automattic/jetpack-ai-client/compare/v0.24.2...v0.24.3 diff --git a/projects/js-packages/ai-client/changelog/renovate-eslint-packages b/projects/js-packages/ai-client/changelog/renovate-eslint-packages deleted file mode 100644 index 5064bda051400..0000000000000 --- a/projects/js-packages/ai-client/changelog/renovate-eslint-packages +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Explicitly declare a return type for a custom React hook. Should be no functional change. - - diff --git a/projects/js-packages/ai-client/changelog/update-eslint-9 b/projects/js-packages/ai-client/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/ai-client/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/ai-client/changelog/update-eslint-config-for-ts-files b/projects/js-packages/ai-client/changelog/update-eslint-config-for-ts-files deleted file mode 100644 index fefec667583fd..0000000000000 --- a/projects/js-packages/ai-client/changelog/update-eslint-config-for-ts-files +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Fixed lints following ESLint rule changes for TS diff --git a/projects/js-packages/ai-client/package.json b/projects/js-packages/ai-client/package.json index cdca70bbde45c..f101983f9841d 100644 --- a/projects/js-packages/ai-client/package.json +++ b/projects/js-packages/ai-client/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@automattic/jetpack-ai-client", - "version": "0.25.1", + "version": "0.25.2", "description": "A JS client for consuming Jetpack AI services", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/ai-client/#readme", "bugs": { diff --git a/projects/js-packages/api/CHANGELOG.md b/projects/js-packages/api/CHANGELOG.md index cbdb8763bdd24..2117283ec4d82 100644 --- a/projects/js-packages/api/CHANGELOG.md +++ b/projects/js-packages/api/CHANGELOG.md @@ -2,6 +2,10 @@ ### This is a list detailing changes for the Jetpack RNA Components package releases. +## [0.17.21] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.17.20] - 2024-12-04 ### Changed - Updated package dependencies. [#40363] @@ -381,6 +385,7 @@ - Add the API methods left behind by the previous PR. - Initial release of jetpack-api package +[0.17.21]: https://github.com/Automattic/jetpack-api/compare/v0.17.20...v0.17.21 [0.17.20]: https://github.com/Automattic/jetpack-api/compare/v0.17.19...v0.17.20 [0.17.19]: https://github.com/Automattic/jetpack-api/compare/v0.17.18...v0.17.19 [0.17.18]: https://github.com/Automattic/jetpack-api/compare/v0.17.17...v0.17.18 diff --git a/projects/js-packages/api/changelog/renovate-wordpress-monorepo b/projects/js-packages/api/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/api/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/api/changelog/update-eslint-9 b/projects/js-packages/api/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/api/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/api/package.json b/projects/js-packages/api/package.json index 2fe822475aa03..96ec8fb3cad8a 100644 --- a/projects/js-packages/api/package.json +++ b/projects/js-packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-api", - "version": "0.17.20", + "version": "0.17.21", "description": "Jetpack Api Package", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/api/#readme", "bugs": { diff --git a/projects/js-packages/babel-plugin-replace-textdomain/CHANGELOG.md b/projects/js-packages/babel-plugin-replace-textdomain/CHANGELOG.md index 8aefafde4c1e4..fa91381349136 100644 --- a/projects/js-packages/babel-plugin-replace-textdomain/CHANGELOG.md +++ b/projects/js-packages/babel-plugin-replace-textdomain/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.42] - 2024-12-16 +### Changed +- Internal updates. + ## [1.0.41] - 2024-12-09 ### Changed - Internal updates. @@ -185,6 +189,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Initial release. - Replace missing domains too. +[1.0.42]: https://github.com/Automattic/babel-plugin-replace-textdomain/compare/v1.0.41...v1.0.42 [1.0.41]: https://github.com/Automattic/babel-plugin-replace-textdomain/compare/v1.0.40...v1.0.41 [1.0.40]: https://github.com/Automattic/babel-plugin-replace-textdomain/compare/v1.0.39...v1.0.40 [1.0.39]: https://github.com/Automattic/babel-plugin-replace-textdomain/compare/v1.0.38...v1.0.39 diff --git a/projects/js-packages/babel-plugin-replace-textdomain/changelog/update-eslint-9 b/projects/js-packages/babel-plugin-replace-textdomain/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/babel-plugin-replace-textdomain/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/babel-plugin-replace-textdomain/package.json b/projects/js-packages/babel-plugin-replace-textdomain/package.json index e900aa92aa0c4..7f8004399692b 100644 --- a/projects/js-packages/babel-plugin-replace-textdomain/package.json +++ b/projects/js-packages/babel-plugin-replace-textdomain/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/babel-plugin-replace-textdomain", - "version": "1.0.41", + "version": "1.0.42", "description": "A Babel plugin to replace the textdomain in gettext-style function calls.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/babel-plugin-replace-textdomain/#readme", "bugs": { diff --git a/projects/js-packages/base-styles/CHANGELOG.md b/projects/js-packages/base-styles/CHANGELOG.md index fdaedacfd4081..02e9d015d652a 100644 --- a/projects/js-packages/base-styles/CHANGELOG.md +++ b/projects/js-packages/base-styles/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.39] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.6.38] - 2024-12-04 ### Changed - Updated package dependencies. [#40363] @@ -337,6 +341,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated package dependencies. - Use Node 16.7.0 in tooling. This shouldn't change the behavior of the code itself. +[0.6.39]: https://github.com/Automattic/jetpack-base-styles/compare/0.6.38...0.6.39 [0.6.38]: https://github.com/Automattic/jetpack-base-styles/compare/0.6.37...0.6.38 [0.6.37]: https://github.com/Automattic/jetpack-base-styles/compare/0.6.36...0.6.37 [0.6.36]: https://github.com/Automattic/jetpack-base-styles/compare/0.6.35...0.6.36 diff --git a/projects/js-packages/base-styles/changelog/update-eslint-9 b/projects/js-packages/base-styles/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/base-styles/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/base-styles/package.json b/projects/js-packages/base-styles/package.json index 2aa1abb6dd2ff..05981d15021ab 100644 --- a/projects/js-packages/base-styles/package.json +++ b/projects/js-packages/base-styles/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-base-styles", - "version": "0.6.38", + "version": "0.6.39", "description": "Jetpack components base styles", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/base-styles/#readme", "bugs": { diff --git a/projects/js-packages/boost-score-api/CHANGELOG.md b/projects/js-packages/boost-score-api/CHANGELOG.md index cf58d0db6dc90..487cabcd91bf1 100644 --- a/projects/js-packages/boost-score-api/CHANGELOG.md +++ b/projects/js-packages/boost-score-api/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.50] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.1.49] - 2024-12-09 ### Changed - Internal updates. @@ -211,6 +215,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Create package for the boost score bar API [#30781] +[0.1.50]: https://github.com/Automattic/jetpack-boost-score-api/compare/v0.1.49...v0.1.50 [0.1.49]: https://github.com/Automattic/jetpack-boost-score-api/compare/v0.1.48...v0.1.49 [0.1.48]: https://github.com/Automattic/jetpack-boost-score-api/compare/v0.1.47...v0.1.48 [0.1.47]: https://github.com/Automattic/jetpack-boost-score-api/compare/v0.1.46...v0.1.47 diff --git a/projects/js-packages/boost-score-api/changelog/renovate-wordpress-monorepo b/projects/js-packages/boost-score-api/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/boost-score-api/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/boost-score-api/changelog/update-eslint-9 b/projects/js-packages/boost-score-api/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/boost-score-api/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/boost-score-api/package.json b/projects/js-packages/boost-score-api/package.json index e4be41355c05b..cc82d6a7d6331 100644 --- a/projects/js-packages/boost-score-api/package.json +++ b/projects/js-packages/boost-score-api/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-boost-score-api", - "version": "0.1.49", + "version": "0.1.50", "description": "A package to get the Jetpack Boost score of a site", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/boost-score-api/#readme", "bugs": { diff --git a/projects/js-packages/components/CHANGELOG.md b/projects/js-packages/components/CHANGELOG.md index 0f5b67bfade2c..7f48662114eff 100644 --- a/projects/js-packages/components/CHANGELOG.md +++ b/projects/js-packages/components/CHANGELOG.md @@ -2,6 +2,14 @@ ### This is a list detailing changes for the Jetpack RNA Components package releases. +## [0.65.0] - 2024-12-16 +### Changed +- Fixes ThreatsDataViews defaultLayouts. [#40598] +- Updated package dependencies. [#40564] [#40598] + +### Fixed +- Fix ToggleControl's help text indent to align with label text. [#40510] + ## [0.64.1] - 2024-12-09 ### Removed - Remove bulk action support from the ThreatsDataViews component. [#40483] @@ -1243,6 +1251,7 @@ ### Changed - Update node version requirement to 14.16.1 +[0.65.0]: https://github.com/Automattic/jetpack-components/compare/0.64.1...0.65.0 [0.64.1]: https://github.com/Automattic/jetpack-components/compare/0.64.0...0.64.1 [0.64.0]: https://github.com/Automattic/jetpack-components/compare/0.63.0...0.64.0 [0.63.0]: https://github.com/Automattic/jetpack-components/compare/0.62.0...0.63.0 diff --git a/projects/js-packages/components/changelog/fix-components-threats-data-views-default-layouts b/projects/js-packages/components/changelog/fix-components-threats-data-views-default-layouts deleted file mode 100644 index 64fa3036e7810..0000000000000 --- a/projects/js-packages/components/changelog/fix-components-threats-data-views-default-layouts +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: changed - -Fixes ThreatsDataViews defaultLayouts diff --git a/projects/js-packages/components/changelog/fix-toggle-control-help-text-indent b/projects/js-packages/components/changelog/fix-toggle-control-help-text-indent deleted file mode 100644 index 1eae6a38fc1ad..0000000000000 --- a/projects/js-packages/components/changelog/fix-toggle-control-help-text-indent +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Fix ToggleControl's help text indent to align with label text. diff --git a/projects/js-packages/components/changelog/renovate-wordpress-monorepo b/projects/js-packages/components/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/components/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/components/changelog/renovate-wordpress-monorepo#2 b/projects/js-packages/components/changelog/renovate-wordpress-monorepo#2 deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/components/changelog/renovate-wordpress-monorepo#2 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/components/changelog/update-eslint-9 b/projects/js-packages/components/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/components/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/components/changelog/update-eslint-config-for-ts-files b/projects/js-packages/components/changelog/update-eslint-config-for-ts-files deleted file mode 100644 index 454c3dde47017..0000000000000 --- a/projects/js-packages/components/changelog/update-eslint-config-for-ts-files +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Fixed ESLint warning - - diff --git a/projects/js-packages/components/package.json b/projects/js-packages/components/package.json index df315974e18ed..b877c9943635f 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.64.1", + "version": "0.65.0", "description": "Jetpack Components Package", "author": "Automattic", "license": "GPL-2.0-or-later", diff --git a/projects/js-packages/connection/CHANGELOG.md b/projects/js-packages/connection/CHANGELOG.md index e6a7d3aaae463..f17999bcd355d 100644 --- a/projects/js-packages/connection/CHANGELOG.md +++ b/projects/js-packages/connection/CHANGELOG.md @@ -2,6 +2,10 @@ ### This is a list detailing changes for the Jetpack RNA Connection Component releases. +## [0.36.2] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.36.1] - 2024-12-09 ### Changed - Internal updates. @@ -910,6 +914,7 @@ - `Main` and `ConnectUser` components added. - `JetpackRestApiClient` API client added. +[0.36.2]: https://github.com/Automattic/jetpack-connection-js/compare/v0.36.1...v0.36.2 [0.36.1]: https://github.com/Automattic/jetpack-connection-js/compare/v0.36.0...v0.36.1 [0.36.0]: https://github.com/Automattic/jetpack-connection-js/compare/v0.35.20...v0.36.0 [0.35.20]: https://github.com/Automattic/jetpack-connection-js/compare/v0.35.19...v0.35.20 diff --git a/projects/js-packages/connection/changelog/renovate-wordpress-monorepo b/projects/js-packages/connection/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/connection/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/connection/changelog/update-eslint-9 b/projects/js-packages/connection/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/connection/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/connection/package.json b/projects/js-packages/connection/package.json index f2ce31623be2a..fc0a69f1fe213 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.36.1", + "version": "0.36.2", "description": "Jetpack Connection Component", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/connection/#readme", "bugs": { diff --git a/projects/js-packages/i18n-check-webpack-plugin/CHANGELOG.md b/projects/js-packages/i18n-check-webpack-plugin/CHANGELOG.md index 9441d330c9b52..a2a54b57f0055 100644 --- a/projects/js-packages/i18n-check-webpack-plugin/CHANGELOG.md +++ b/projects/js-packages/i18n-check-webpack-plugin/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.16] - 2024-12-16 +### Changed +- Internal updates. + ## [1.1.15] - 2024-11-14 ### Changed - Update dependencies. @@ -234,6 +238,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial release. +[1.1.16]: https://github.com/Automattic/i18n-check-webpack-plugin/compare/v1.1.15...v1.1.16 [1.1.15]: https://github.com/Automattic/i18n-check-webpack-plugin/compare/v1.1.14...v1.1.15 [1.1.14]: https://github.com/Automattic/i18n-check-webpack-plugin/compare/v1.1.13...v1.1.14 [1.1.13]: https://github.com/Automattic/i18n-check-webpack-plugin/compare/v1.1.12...v1.1.13 diff --git a/projects/js-packages/i18n-check-webpack-plugin/changelog/update-eslint-9 b/projects/js-packages/i18n-check-webpack-plugin/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/i18n-check-webpack-plugin/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/i18n-check-webpack-plugin/package.json b/projects/js-packages/i18n-check-webpack-plugin/package.json index 553257d5acfc9..ddcd2248037d1 100644 --- a/projects/js-packages/i18n-check-webpack-plugin/package.json +++ b/projects/js-packages/i18n-check-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/i18n-check-webpack-plugin", - "version": "1.1.15", + "version": "1.1.16", "description": "A Webpack plugin to check that WordPress i18n hasn't been mangled by Webpack optimizations.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/i18n-check-webpack-plugin/#readme", "bugs": { diff --git a/projects/js-packages/i18n-loader-webpack-plugin/CHANGELOG.md b/projects/js-packages/i18n-loader-webpack-plugin/CHANGELOG.md index fe56ac1455d26..821389f74a14c 100644 --- a/projects/js-packages/i18n-loader-webpack-plugin/CHANGELOG.md +++ b/projects/js-packages/i18n-loader-webpack-plugin/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.67] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [2.0.66] - 2024-12-09 ### Changed - Internal updates. @@ -293,6 +297,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial release. +[2.0.67]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v2.0.66...v2.0.67 [2.0.66]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v2.0.65...v2.0.66 [2.0.65]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v2.0.64...v2.0.65 [2.0.64]: https://github.com/Automattic/i18n-loader-webpack-plugin/compare/v2.0.63...v2.0.64 diff --git a/projects/js-packages/i18n-loader-webpack-plugin/changelog/renovate-wordpress-monorepo b/projects/js-packages/i18n-loader-webpack-plugin/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/i18n-loader-webpack-plugin/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/i18n-loader-webpack-plugin/changelog/update-eslint-9 b/projects/js-packages/i18n-loader-webpack-plugin/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/i18n-loader-webpack-plugin/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/i18n-loader-webpack-plugin/package.json b/projects/js-packages/i18n-loader-webpack-plugin/package.json index a31c081c5d3cd..969185172136b 100644 --- a/projects/js-packages/i18n-loader-webpack-plugin/package.json +++ b/projects/js-packages/i18n-loader-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/i18n-loader-webpack-plugin", - "version": "2.0.66", + "version": "2.0.67", "description": "A Webpack plugin to load WordPress i18n when Webpack lazy-loads a bundle.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/i18n-loader-webpack-plugin/#readme", "bugs": { diff --git a/projects/js-packages/idc/CHANGELOG.md b/projects/js-packages/idc/CHANGELOG.md index bb3a26edefe04..5a3a0f7dc9f77 100644 --- a/projects/js-packages/idc/CHANGELOG.md +++ b/projects/js-packages/idc/CHANGELOG.md @@ -2,6 +2,10 @@ ### This is a list detailing changes for the Jetpack RNA IDC package releases. +## 0.12.1 - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## 0.12.0 - 2024-12-04 ### Changed - Changed text domain from 'jetpack' to 'jetpack-idc'. [#40368] diff --git a/projects/js-packages/idc/changelog/renovate-wordpress-monorepo b/projects/js-packages/idc/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/idc/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/idc/changelog/update-eslint-9 b/projects/js-packages/idc/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/idc/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/idc/package.json b/projects/js-packages/idc/package.json index ab8c0ea6e9e52..539002a3cc77e 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.12.0", + "version": "0.12.1", "description": "Jetpack Connection Component", "author": "Automattic", "license": "GPL-2.0-or-later", diff --git a/projects/js-packages/licensing/CHANGELOG.md b/projects/js-packages/licensing/CHANGELOG.md index 3c11d2acb3066..870cf8a3360b0 100644 --- a/projects/js-packages/licensing/CHANGELOG.md +++ b/projects/js-packages/licensing/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.14.2 - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## 0.14.1 - 2024-12-09 ### Changed - Internal updates. diff --git a/projects/js-packages/licensing/changelog/renovate-wordpress-monorepo b/projects/js-packages/licensing/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/licensing/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/licensing/changelog/update-eslint-9 b/projects/js-packages/licensing/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/licensing/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/licensing/package.json b/projects/js-packages/licensing/package.json index 65fc6ed963f08..b80b77578f1fd 100644 --- a/projects/js-packages/licensing/package.json +++ b/projects/js-packages/licensing/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-licensing", - "version": "0.14.1", + "version": "0.14.2", "description": "Jetpack licensing flow", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/licensing/#readme", "bugs": { diff --git a/projects/js-packages/partner-coupon/CHANGELOG.md b/projects/js-packages/partner-coupon/CHANGELOG.md index fd722c16812c8..fcffa06db85c5 100644 --- a/projects/js-packages/partner-coupon/CHANGELOG.md +++ b/projects/js-packages/partner-coupon/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.3.1 - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## 0.3.0 - 2024-12-09 ### Changed - Changed text domain from 'jetpack' to 'jetpack-partner-coupon'. [#40368] diff --git a/projects/js-packages/partner-coupon/changelog/renovate-wordpress-monorepo b/projects/js-packages/partner-coupon/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/partner-coupon/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/partner-coupon/changelog/update-eslint-9 b/projects/js-packages/partner-coupon/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/partner-coupon/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/partner-coupon/package.json b/projects/js-packages/partner-coupon/package.json index 6cbae1e08eb23..d8f25f990a018 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.3.0", + "version": "0.3.1", "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": { diff --git a/projects/js-packages/publicize-components/CHANGELOG.md b/projects/js-packages/publicize-components/CHANGELOG.md index 6355a31ead9fa..a080bacb233ed 100644 --- a/projects/js-packages/publicize-components/CHANGELOG.md +++ b/projects/js-packages/publicize-components/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.75.1] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] [#40598] + ## [0.75.0] - 2024-12-09 ### Changed - Changed text domain from 'jetpack' to 'jetpack-publicize-components'. [#40368] @@ -1038,6 +1042,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Updated package dependencies. [#24470] +[0.75.1]: https://github.com/Automattic/jetpack-publicize-components/compare/v0.75.0...v0.75.1 [0.75.0]: https://github.com/Automattic/jetpack-publicize-components/compare/v0.74.2...v0.75.0 [0.74.2]: https://github.com/Automattic/jetpack-publicize-components/compare/v0.74.1...v0.74.2 [0.74.1]: https://github.com/Automattic/jetpack-publicize-components/compare/v0.74.0...v0.74.1 diff --git a/projects/js-packages/publicize-components/changelog/renovate-wordpress-monorepo b/projects/js-packages/publicize-components/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/publicize-components/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/publicize-components/changelog/renovate-wordpress-monorepo#2 b/projects/js-packages/publicize-components/changelog/renovate-wordpress-monorepo#2 deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/publicize-components/changelog/renovate-wordpress-monorepo#2 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/publicize-components/changelog/update-eslint-9 b/projects/js-packages/publicize-components/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/publicize-components/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/publicize-components/package.json b/projects/js-packages/publicize-components/package.json index 9a9c568e62665..55e03a65b08d9 100644 --- a/projects/js-packages/publicize-components/package.json +++ b/projects/js-packages/publicize-components/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-publicize-components", - "version": "0.75.0", + "version": "0.75.1", "description": "A library of JS components required by the Publicize editor plugin", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/publicize-components/#readme", "bugs": { diff --git a/projects/js-packages/remove-asset-webpack-plugin/CHANGELOG.md b/projects/js-packages/remove-asset-webpack-plugin/CHANGELOG.md index 9090b682a210b..c17dd566ba95a 100644 --- a/projects/js-packages/remove-asset-webpack-plugin/CHANGELOG.md +++ b/projects/js-packages/remove-asset-webpack-plugin/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.25] - 2024-12-16 +### Changed +- Internal updates. + ## [1.0.24] - 2024-11-18 ### Changed - Update dependencies. [#40194] @@ -111,6 +115,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial release. +[1.0.25]: https://github.com/Automattic/remove-asset-webpack-plugin/compare/v1.0.24...v1.0.25 [1.0.24]: https://github.com/Automattic/remove-asset-webpack-plugin/compare/v1.0.23...v1.0.24 [1.0.23]: https://github.com/Automattic/remove-asset-webpack-plugin/compare/v1.0.22...v1.0.23 [1.0.22]: https://github.com/Automattic/remove-asset-webpack-plugin/compare/v1.0.21...v1.0.22 diff --git a/projects/js-packages/remove-asset-webpack-plugin/changelog/update-eslint-9 b/projects/js-packages/remove-asset-webpack-plugin/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/remove-asset-webpack-plugin/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/remove-asset-webpack-plugin/package.json b/projects/js-packages/remove-asset-webpack-plugin/package.json index 16e087f18a5a5..53e023f78385b 100644 --- a/projects/js-packages/remove-asset-webpack-plugin/package.json +++ b/projects/js-packages/remove-asset-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/remove-asset-webpack-plugin", - "version": "1.0.24", + "version": "1.0.25", "description": "A Webpack plugin to remove assets from the build.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/remove-asset-webpack-plugin/README.md#readme", "bugs": { diff --git a/projects/js-packages/scan/CHANGELOG.md b/projects/js-packages/scan/CHANGELOG.md index 6d1e49afce006..9f30e52bf0ff1 100644 --- a/projects/js-packages/scan/CHANGELOG.md +++ b/projects/js-packages/scan/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.2] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.5.1] - 2024-12-09 ### Changed - Internal updates. @@ -83,6 +87,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - Updated dependencies. [#39754] +[0.5.2]: https://github.com/Automattic/jetpack-scan/compare/v0.5.1...v0.5.2 [0.5.1]: https://github.com/Automattic/jetpack-scan/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/Automattic/jetpack-scan/compare/v0.4.1...v0.5.0 [0.4.1]: https://github.com/Automattic/jetpack-scan/compare/v0.4.0...v0.4.1 diff --git a/projects/js-packages/scan/changelog/renovate-wordpress-monorepo b/projects/js-packages/scan/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/scan/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/scan/changelog/update-eslint-9 b/projects/js-packages/scan/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/scan/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/scan/package.json b/projects/js-packages/scan/package.json index 566efad361d6f..d0e91a3b7e36a 100644 --- a/projects/js-packages/scan/package.json +++ b/projects/js-packages/scan/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@automattic/jetpack-scan", - "version": "0.5.1", + "version": "0.5.2", "description": "A JS client for consuming Jetpack Scan services", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/scan/#readme", "bugs": { diff --git a/projects/js-packages/script-data/CHANGELOG.md b/projects/js-packages/script-data/CHANGELOG.md index 085cf8745d76f..5c8559bbc6d17 100644 --- a/projects/js-packages/script-data/CHANGELOG.md +++ b/projects/js-packages/script-data/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.6] - 2024-12-16 +### Changed +- Internal updates. + ## [0.1.5] - 2024-11-14 ### Changed - Update dependencies. @@ -29,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added jetpack-script-data package to consolidate the logic for Jetpack Initial state [#38430] +[0.1.6]: https://github.com/Automattic/jetpack-script-data/compare/v0.1.5...v0.1.6 [0.1.5]: https://github.com/Automattic/jetpack-script-data/compare/v0.1.4...v0.1.5 [0.1.4]: https://github.com/Automattic/jetpack-script-data/compare/v0.1.3...v0.1.4 [0.1.3]: https://github.com/Automattic/jetpack-script-data/compare/v0.1.2...v0.1.3 diff --git a/projects/js-packages/script-data/changelog/update-eslint-9 b/projects/js-packages/script-data/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/script-data/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/script-data/package.json b/projects/js-packages/script-data/package.json index e4392315a29b1..4c7e13fb32041 100644 --- a/projects/js-packages/script-data/package.json +++ b/projects/js-packages/script-data/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-script-data", - "version": "0.1.5", + "version": "0.1.6", "description": "A library to provide data for script handles and the corresponding utility functions for Jetpack.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/script-data/#readme", "bugs": { diff --git a/projects/js-packages/shared-extension-utils/CHANGELOG.md b/projects/js-packages/shared-extension-utils/CHANGELOG.md index 80b717fa1188b..e8c2792dc80c5 100644 --- a/projects/js-packages/shared-extension-utils/CHANGELOG.md +++ b/projects/js-packages/shared-extension-utils/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.16.2] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.16.1] - 2024-12-09 ### Changed - Internal updates. @@ -507,6 +511,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Core: prepare utility for release +[0.16.2]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.16.1...0.16.2 [0.16.1]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.16.0...0.16.1 [0.16.0]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.15.20...0.16.0 [0.15.20]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.15.19...0.15.20 diff --git a/projects/js-packages/shared-extension-utils/changelog/renovate-wordpress-monorepo b/projects/js-packages/shared-extension-utils/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/shared-extension-utils/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/shared-extension-utils/changelog/update-eslint-9 b/projects/js-packages/shared-extension-utils/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/shared-extension-utils/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/shared-extension-utils/package.json b/projects/js-packages/shared-extension-utils/package.json index bf8012e0b666b..bbb3e1f0bfe42 100644 --- a/projects/js-packages/shared-extension-utils/package.json +++ b/projects/js-packages/shared-extension-utils/package.json @@ -1,6 +1,6 @@ { "name": "@automattic/jetpack-shared-extension-utils", - "version": "0.16.1", + "version": "0.16.2", "description": "Utility functions used by the block editor extensions", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/shared-extension-utils/#readme", "bugs": { diff --git a/projects/js-packages/social-logos/CHANGELOG.md b/projects/js-packages/social-logos/CHANGELOG.md index 51e2b9b212002..c55e4a0814b54 100644 --- a/projects/js-packages/social-logos/CHANGELOG.md +++ b/projects/js-packages/social-logos/CHANGELOG.md @@ -1,3 +1,7 @@ +## [3.1.15] - 2024-12-16 +### Changed +- Internal updates. + ## [3.1.14] - 2024-12-09 ### Changed - Internal updates. @@ -180,6 +184,7 @@ - Build: Refactored (aligned build system with Gridicons). +[3.1.15]: https://github.com/Automattic/social-logos/compare/v3.1.14...v3.1.15 [3.1.14]: https://github.com/Automattic/social-logos/compare/v3.1.13...v3.1.14 [3.1.13]: https://github.com/Automattic/social-logos/compare/v3.1.12...v3.1.13 [3.1.12]: https://github.com/Automattic/social-logos/compare/v3.1.11...v3.1.12 diff --git a/projects/js-packages/social-logos/changelog/update-eslint-9 b/projects/js-packages/social-logos/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/social-logos/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/social-logos/package.json b/projects/js-packages/social-logos/package.json index 4119f7506710a..d4968eaaa663e 100644 --- a/projects/js-packages/social-logos/package.json +++ b/projects/js-packages/social-logos/package.json @@ -1,6 +1,6 @@ { "name": "social-logos", - "version": "3.1.14", + "version": "3.1.15", "description": "A repository of all the social logos used on WordPress.com.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/social-logos/", "bugs": { diff --git a/projects/js-packages/webpack-config/CHANGELOG.md b/projects/js-packages/webpack-config/CHANGELOG.md index 9a1e0377150e6..33f39f35a0ed5 100644 --- a/projects/js-packages/webpack-config/CHANGELOG.md +++ b/projects/js-packages/webpack-config/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 3.5.5 - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## 3.5.4 - 2024-12-04 ### Changed - Updated package dependencies. [#40363] diff --git a/projects/js-packages/webpack-config/changelog/renovate-wordpress-monorepo b/projects/js-packages/webpack-config/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/js-packages/webpack-config/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/js-packages/webpack-config/changelog/update-eslint-9 b/projects/js-packages/webpack-config/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/js-packages/webpack-config/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/js-packages/webpack-config/package.json b/projects/js-packages/webpack-config/package.json index 2a12b97c55769..2d81edcf5fd06 100644 --- a/projects/js-packages/webpack-config/package.json +++ b/projects/js-packages/webpack-config/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-webpack-config", - "version": "3.5.4", + "version": "3.5.5", "description": "Library of pieces for webpack config in Jetpack projects.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/webpack-config/#readme", "bugs": { diff --git a/projects/packages/assets/CHANGELOG.md b/projects/packages/assets/CHANGELOG.md index dae9ca57d214e..e37f020b549df 100644 --- a/projects/packages/assets/CHANGELOG.md +++ b/projects/packages/assets/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.0.2] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [4.0.1] - 2024-12-04 ### Changed - Updated package dependencies. [#40363] @@ -540,6 +544,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Statically access asset tools +[4.0.2]: https://github.com/Automattic/jetpack-assets/compare/v4.0.1...v4.0.2 [4.0.1]: https://github.com/Automattic/jetpack-assets/compare/v4.0.0...v4.0.1 [4.0.0]: https://github.com/Automattic/jetpack-assets/compare/v3.0.0...v4.0.0 [3.0.0]: https://github.com/Automattic/jetpack-assets/compare/v2.3.14...v3.0.0 diff --git a/projects/packages/assets/changelog/renovate-wordpress-monorepo b/projects/packages/assets/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/assets/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/assets/changelog/update-eslint-9 b/projects/packages/assets/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/assets/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/backup/CHANGELOG.md b/projects/packages/backup/CHANGELOG.md index f3442d65ad7e6..04a15c45503a9 100644 --- a/projects/packages/backup/CHANGELOG.md +++ b/projects/packages/backup/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.0.5] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [4.0.4] - 2024-12-09 ### Changed - Updated package dependencies. [#40363] @@ -754,6 +758,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add API endpoints and Jetpack Backup package for managing Help… +[4.0.5]: https://github.com/Automattic/jetpack-backup/compare/v4.0.4...v4.0.5 [4.0.4]: https://github.com/Automattic/jetpack-backup/compare/v4.0.3...v4.0.4 [4.0.3]: https://github.com/Automattic/jetpack-backup/compare/v4.0.2...v4.0.3 [4.0.2]: https://github.com/Automattic/jetpack-backup/compare/v4.0.1...v4.0.2 diff --git a/projects/packages/backup/changelog/renovate-wordpress-monorepo b/projects/packages/backup/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/backup/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/backup/changelog/update-eslint-9 b/projects/packages/backup/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/backup/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/backup/changelog/update-packages-fix-eslint-9-lints b/projects/packages/backup/changelog/update-packages-fix-eslint-9-lints deleted file mode 100644 index b3176fbef2f88..0000000000000 --- a/projects/packages/backup/changelog/update-packages-fix-eslint-9-lints +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Fix some JS lints ahead of eslint 9 upgrade. - - diff --git a/projects/packages/backup/src/class-package-version.php b/projects/packages/backup/src/class-package-version.php index 865175c83c64b..de3e717b16381 100644 --- a/projects/packages/backup/src/class-package-version.php +++ b/projects/packages/backup/src/class-package-version.php @@ -16,7 +16,7 @@ */ class Package_Version { - const PACKAGE_VERSION = '4.0.4'; + const PACKAGE_VERSION = '4.0.5'; const PACKAGE_SLUG = 'backup'; diff --git a/projects/packages/blaze/CHANGELOG.md b/projects/packages/blaze/CHANGELOG.md index 409c6c3bf65f9..8fdf025ed7ef3 100644 --- a/projects/packages/blaze/CHANGELOG.md +++ b/projects/packages/blaze/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.25.4] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.25.3] - 2024-12-04 ### Changed - Updated package dependencies. [#40363] @@ -497,6 +501,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Updated package dependencies. [#27906] +[0.25.4]: https://github.com/automattic/jetpack-blaze/compare/v0.25.3...v0.25.4 [0.25.3]: https://github.com/automattic/jetpack-blaze/compare/v0.25.2...v0.25.3 [0.25.2]: https://github.com/automattic/jetpack-blaze/compare/v0.25.1...v0.25.2 [0.25.1]: https://github.com/automattic/jetpack-blaze/compare/v0.25.0...v0.25.1 diff --git a/projects/packages/blaze/changelog/renovate-wordpress-monorepo b/projects/packages/blaze/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/blaze/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/blaze/changelog/update-eslint-9 b/projects/packages/blaze/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/blaze/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/blaze/package.json b/projects/packages/blaze/package.json index 69cc22b5ad0e1..9d73720732ff5 100644 --- a/projects/packages/blaze/package.json +++ b/projects/packages/blaze/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-blaze", - "version": "0.25.3", + "version": "0.25.4", "description": "Attract high-quality traffic to your site using Blaze. Using this service, you can advertise a post or page on some of the millions of pages across WordPress.com and Tumblr from just $5 per day.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/blaze/#readme", "bugs": { diff --git a/projects/packages/blaze/src/class-dashboard.php b/projects/packages/blaze/src/class-dashboard.php index b32823e778f37..10cbec22d7c13 100644 --- a/projects/packages/blaze/src/class-dashboard.php +++ b/projects/packages/blaze/src/class-dashboard.php @@ -21,7 +21,7 @@ class Dashboard { * * @var string */ - const PACKAGE_VERSION = '0.25.3'; + const PACKAGE_VERSION = '0.25.4'; /** * List of dependencies needed to render the dashboard in wp-admin. diff --git a/projects/packages/classic-theme-helper/CHANGELOG.md b/projects/packages/classic-theme-helper/CHANGELOG.md index 578fd8d0105d1..702edf2a17030 100644 --- a/projects/packages/classic-theme-helper/CHANGELOG.md +++ b/projects/packages/classic-theme-helper/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.8.0] - 2024-12-16 +### Added +- Jetpack Testimonials: Ensuring functionality runs via the Classic Theme Helper package. [#40388] + +### Changed +- Updated package dependencies. [#40564] + +### Fixed +- Testimonials: Include shortcode CSS file. [#40592] + ## [0.7.4] - 2024-12-09 ### Fixed - Content Options: Ensure excerpt_length is cast to an int if it is not already, to prevent fatal errors. [#40389] @@ -167,6 +177,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Add wordpress folder on gitignore. [#37177] +[0.8.0]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.7.4...v0.8.0 [0.7.4]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.7.3...v0.7.4 [0.7.3]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.7.2...v0.7.3 [0.7.2]: https://github.com/Automattic/jetpack-classic-theme-helper/compare/v0.7.1...v0.7.2 diff --git a/projects/packages/classic-theme-helper/changelog/add-require-testimonials-cpt-in-classic-theme-helper b/projects/packages/classic-theme-helper/changelog/add-require-testimonials-cpt-in-classic-theme-helper deleted file mode 100644 index ea5d8e8eb1945..0000000000000 --- a/projects/packages/classic-theme-helper/changelog/add-require-testimonials-cpt-in-classic-theme-helper +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: added - -Jetpack Testimonials: Ensuring functionality runs via the Classic Theme Helper package. diff --git a/projects/packages/classic-theme-helper/changelog/add-testimonial-shortcode-css-package b/projects/packages/classic-theme-helper/changelog/add-testimonial-shortcode-css-package deleted file mode 100644 index 2458f5419f19d..0000000000000 --- a/projects/packages/classic-theme-helper/changelog/add-testimonial-shortcode-css-package +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Testimonials: Include shortcode CSS file. diff --git a/projects/packages/classic-theme-helper/changelog/renovate-wordpress-monorepo b/projects/packages/classic-theme-helper/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/classic-theme-helper/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/classic-theme-helper/changelog/update-eslint-9 b/projects/packages/classic-theme-helper/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/classic-theme-helper/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/classic-theme-helper/composer.json b/projects/packages/classic-theme-helper/composer.json index 81ce4ccd47f30..c63a006f9f61b 100644 --- a/projects/packages/classic-theme-helper/composer.json +++ b/projects/packages/classic-theme-helper/composer.json @@ -55,7 +55,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.7.x-dev" + "dev-trunk": "0.8.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-classic-theme-helper/compare/v${old}...v${new}" diff --git a/projects/packages/classic-theme-helper/package.json b/projects/packages/classic-theme-helper/package.json index 1b528b5cbc478..504deb824d9f2 100644 --- a/projects/packages/classic-theme-helper/package.json +++ b/projects/packages/classic-theme-helper/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-classic-theme-helper", - "version": "0.7.4", + "version": "0.8.0", "description": "Features used with classic themes", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/classic-theme-helper/#readme", "bugs": { diff --git a/projects/packages/classic-theme-helper/src/class-main.php b/projects/packages/classic-theme-helper/src/class-main.php index d3332faf30d54..3c934378d21f7 100644 --- a/projects/packages/classic-theme-helper/src/class-main.php +++ b/projects/packages/classic-theme-helper/src/class-main.php @@ -14,7 +14,7 @@ */ class Main { - const PACKAGE_VERSION = '0.7.4'; + const PACKAGE_VERSION = '0.8.0'; /** * Modules to include. diff --git a/projects/packages/connection/CHANGELOG.md b/projects/packages/connection/CHANGELOG.md index 9709309650faa..eaaba4aa9a81e 100644 --- a/projects/packages/connection/CHANGELOG.md +++ b/projects/packages/connection/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [6.2.1] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [6.2.0] - 2024-12-09 ### Added - Added a mechanism to use callbacks for package options. [#40474] @@ -1267,6 +1271,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Separate the connection library into its own package. +[6.2.1]: https://github.com/Automattic/jetpack-connection/compare/v6.2.0...v6.2.1 [6.2.0]: https://github.com/Automattic/jetpack-connection/compare/v6.1.1...v6.2.0 [6.1.1]: https://github.com/Automattic/jetpack-connection/compare/v6.1.0...v6.1.1 [6.1.0]: https://github.com/Automattic/jetpack-connection/compare/v6.0.1...v6.1.0 diff --git a/projects/packages/connection/changelog/renovate-wordpress-monorepo b/projects/packages/connection/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/connection/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/connection/changelog/update-eslint-9 b/projects/packages/connection/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/connection/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/connection/src/class-package-version.php b/projects/packages/connection/src/class-package-version.php index c73d4fd758d67..361d29d6aaffe 100644 --- a/projects/packages/connection/src/class-package-version.php +++ b/projects/packages/connection/src/class-package-version.php @@ -12,7 +12,7 @@ */ class Package_Version { - const PACKAGE_VERSION = '6.2.0'; + const PACKAGE_VERSION = '6.2.1'; const PACKAGE_SLUG = 'connection'; diff --git a/projects/packages/explat/CHANGELOG.md b/projects/packages/explat/CHANGELOG.md index 8a748977ee51e..73be2e14dab4a 100644 --- a/projects/packages/explat/CHANGELOG.md +++ b/projects/packages/explat/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.3] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.2.2] - 2024-12-04 ### Changed - Updated package dependencies. [#40363] [#40372] @@ -87,6 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ExPlat: add condition to prevent fetching the experiment assignment if there's not anon id (meaning that Tracks is likely disabled) [#38327] - Updated package dependencies. [#38132] +[0.2.3]: https://github.com/Automattic/jetpack-explat/compare/v0.2.2...v0.2.3 [0.2.2]: https://github.com/Automattic/jetpack-explat/compare/v0.2.1...v0.2.2 [0.2.1]: https://github.com/Automattic/jetpack-explat/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/Automattic/jetpack-explat/compare/v0.1.15...v0.2.0 diff --git a/projects/packages/explat/changelog/renovate-wordpress-monorepo b/projects/packages/explat/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/explat/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/explat/package.json b/projects/packages/explat/package.json index 361b0fb915075..b939c4fd83692 100644 --- a/projects/packages/explat/package.json +++ b/projects/packages/explat/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-explat", - "version": "0.2.2", + "version": "0.2.3", "description": "A package for running A/B tests on the Experimentation Platform (ExPlat) in the plugin.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/explat/#readme", "bugs": { diff --git a/projects/packages/explat/src/class-explat.php b/projects/packages/explat/src/class-explat.php index 24be8ccfa112a..93f38609b9959 100644 --- a/projects/packages/explat/src/class-explat.php +++ b/projects/packages/explat/src/class-explat.php @@ -20,7 +20,7 @@ class ExPlat { * * @var string */ - const PACKAGE_VERSION = '0.2.2'; + const PACKAGE_VERSION = '0.2.3'; /** * Initializer. diff --git a/projects/packages/forms/CHANGELOG.md b/projects/packages/forms/CHANGELOG.md index d64f43f6774ba..3cec146229b1d 100644 --- a/projects/packages/forms/CHANGELOG.md +++ b/projects/packages/forms/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.34.4] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + +### Fixed +- Form Block: fixed validation of URL input types to allow query strings. [#40490] + ## [0.34.3] - 2024-12-09 ### Changed - Updated package dependencies. [#40363] @@ -733,6 +740,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a new jetpack/forms package [#28409] - Added a public load_contact_form method for initializing the contact form module. [#28416] +[0.34.4]: https://github.com/automattic/jetpack-forms/compare/v0.34.3...v0.34.4 [0.34.3]: https://github.com/automattic/jetpack-forms/compare/v0.34.2...v0.34.3 [0.34.2]: https://github.com/automattic/jetpack-forms/compare/v0.34.1...v0.34.2 [0.34.1]: https://github.com/automattic/jetpack-forms/compare/v0.34.0...v0.34.1 diff --git a/projects/packages/forms/changelog/fix-form-block-broken-url-input-validation b/projects/packages/forms/changelog/fix-form-block-broken-url-input-validation deleted file mode 100644 index 0e77ccb963690..0000000000000 --- a/projects/packages/forms/changelog/fix-form-block-broken-url-input-validation +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Form Block: fixed validation of URL input types to allow query strings. diff --git a/projects/packages/forms/changelog/renovate-wordpress-monorepo b/projects/packages/forms/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/forms/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/forms/changelog/update-eslint-9 b/projects/packages/forms/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/forms/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/forms/changelog/update-packages-fix-eslint-9-lints b/projects/packages/forms/changelog/update-packages-fix-eslint-9-lints deleted file mode 100644 index b3176fbef2f88..0000000000000 --- a/projects/packages/forms/changelog/update-packages-fix-eslint-9-lints +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Fix some JS lints ahead of eslint 9 upgrade. - - diff --git a/projects/packages/forms/package.json b/projects/packages/forms/package.json index 4d988a1635797..4337aa6cc92e6 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.34.3", + "version": "0.34.4", "description": "Jetpack Forms", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/forms/#readme", "bugs": { diff --git a/projects/packages/forms/src/class-jetpack-forms.php b/projects/packages/forms/src/class-jetpack-forms.php index 4b4b4ab34fcac..6e1800cda9f1c 100644 --- a/projects/packages/forms/src/class-jetpack-forms.php +++ b/projects/packages/forms/src/class-jetpack-forms.php @@ -15,7 +15,7 @@ */ class Jetpack_Forms { - const PACKAGE_VERSION = '0.34.3'; + const PACKAGE_VERSION = '0.34.4'; /** * Load the contact form module. diff --git a/projects/packages/image-cdn/CHANGELOG.md b/projects/packages/image-cdn/CHANGELOG.md index d7814df7eac21..fb145175ab692 100644 --- a/projects/packages/image-cdn/CHANGELOG.md +++ b/projects/packages/image-cdn/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.2] - 2024-12-16 +### Changed +- Internal updates. + ## [0.7.1] - 2024-11-25 ### Changed - Updated dependencies. [#40286] @@ -158,6 +162,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add image CDN package. [#29561] +[0.7.2]: https://github.com/Automattic/jetpack-image-cdn/compare/v0.7.1...v0.7.2 [0.7.1]: https://github.com/Automattic/jetpack-image-cdn/compare/v0.7.0...v0.7.1 [0.7.0]: https://github.com/Automattic/jetpack-image-cdn/compare/v0.6.0...v0.7.0 [0.6.0]: https://github.com/Automattic/jetpack-image-cdn/compare/v0.5.3...v0.6.0 diff --git a/projects/packages/image-cdn/changelog/renovate-lock-file-maintenance b/projects/packages/image-cdn/changelog/renovate-lock-file-maintenance deleted file mode 100644 index 9ded746d9449f..0000000000000 --- a/projects/packages/image-cdn/changelog/renovate-lock-file-maintenance +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Add a cast to make Phan happy. Should be no change to functionality. - - diff --git a/projects/packages/image-cdn/src/class-image-cdn.php b/projects/packages/image-cdn/src/class-image-cdn.php index f5b8b7049a88a..11d5af0a126cc 100644 --- a/projects/packages/image-cdn/src/class-image-cdn.php +++ b/projects/packages/image-cdn/src/class-image-cdn.php @@ -12,7 +12,7 @@ */ final class Image_CDN { - const PACKAGE_VERSION = '0.7.1'; + const PACKAGE_VERSION = '0.7.2'; /** * Singleton. diff --git a/projects/packages/import/CHANGELOG.md b/projects/packages/import/CHANGELOG.md index 83bde2fa484d6..4d96561e5d532 100644 --- a/projects/packages/import/CHANGELOG.md +++ b/projects/packages/import/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.9.2] - 2024-12-16 +### Fixed +- Import: setting WP_IMPORTING when doing an import. [#40563] + ## [0.9.1] - 2024-11-25 ### Changed - Updated dependencies. [#40286] @@ -130,6 +134,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed various imported resources hierarchies [#29012] +[0.9.2]: https://github.com/Automattic/jetpack-import/compare/v0.9.1...v0.9.2 [0.9.1]: https://github.com/Automattic/jetpack-import/compare/v0.9.0...v0.9.1 [0.9.0]: https://github.com/Automattic/jetpack-import/compare/v0.8.11...v0.9.0 [0.8.11]: https://github.com/Automattic/jetpack-import/compare/v0.8.10...v0.8.11 diff --git a/projects/packages/import/changelog/fix-wp-import-issue b/projects/packages/import/changelog/fix-wp-import-issue deleted file mode 100644 index 0943345dc4bca..0000000000000 --- a/projects/packages/import/changelog/fix-wp-import-issue +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Import: setting WP_IMPORTING when doing an import diff --git a/projects/packages/import/package.json b/projects/packages/import/package.json index 07d5129b0fdcd..1e754e2218de6 100644 --- a/projects/packages/import/package.json +++ b/projects/packages/import/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-import", - "version": "0.9.1", + "version": "0.9.2", "description": "Set of REST API routes used in WPCOM Unified Importer.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/import/#readme", "bugs": { diff --git a/projects/packages/import/src/class-main.php b/projects/packages/import/src/class-main.php index 0dc07cc84aa48..b3314f3ff2b1c 100644 --- a/projects/packages/import/src/class-main.php +++ b/projects/packages/import/src/class-main.php @@ -20,7 +20,7 @@ class Main { * * @var string */ - const PACKAGE_VERSION = '0.9.1'; + const PACKAGE_VERSION = '0.9.2'; /** * A list of all the routes. diff --git a/projects/packages/jitm/CHANGELOG.md b/projects/packages/jitm/CHANGELOG.md index 6a938c9394fb3..322c7d80ea067 100644 --- a/projects/packages/jitm/CHANGELOG.md +++ b/projects/packages/jitm/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.0.3] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [4.0.2] - 2024-12-04 ### Changed - Updated package dependencies. [#40363] @@ -807,6 +811,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update Jetpack to use new JITM package +[4.0.3]: https://github.com/Automattic/jetpack-jitm/compare/v4.0.2...v4.0.3 [4.0.2]: https://github.com/Automattic/jetpack-jitm/compare/v4.0.1...v4.0.2 [4.0.1]: https://github.com/Automattic/jetpack-jitm/compare/v4.0.0...v4.0.1 [4.0.0]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.29...v4.0.0 diff --git a/projects/packages/jitm/changelog/renovate-wordpress-monorepo b/projects/packages/jitm/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/jitm/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/jitm/changelog/update-eslint-9 b/projects/packages/jitm/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/jitm/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/jitm/src/class-jitm.php b/projects/packages/jitm/src/class-jitm.php index 12a0b901825fb..30043bfe2b951 100644 --- a/projects/packages/jitm/src/class-jitm.php +++ b/projects/packages/jitm/src/class-jitm.php @@ -20,7 +20,7 @@ */ class JITM { - const PACKAGE_VERSION = '4.0.2'; + const PACKAGE_VERSION = '4.0.3'; /** * The configuration method that is called from the jetpack-config package. diff --git a/projects/packages/masterbar/CHANGELOG.md b/projects/packages/masterbar/CHANGELOG.md index 6d933f84bc038..91f13051c85c8 100644 --- a/projects/packages/masterbar/CHANGELOG.md +++ b/projects/packages/masterbar/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.10.3] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.10.2] - 2024-12-04 ### Changed - Updated package dependencies. [#40363] @@ -182,6 +186,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Notifications: Change Icon [#37676] - Updated package dependencies. [#37669] [#37706] +[0.10.3]: https://github.com/Automattic/jetpack-masterbar/compare/v0.10.2...v0.10.3 [0.10.2]: https://github.com/Automattic/jetpack-masterbar/compare/v0.10.1...v0.10.2 [0.10.1]: https://github.com/Automattic/jetpack-masterbar/compare/v0.10.0...v0.10.1 [0.10.0]: https://github.com/Automattic/jetpack-masterbar/compare/v0.9.9...v0.10.0 diff --git a/projects/packages/masterbar/changelog/renovate-wordpress-monorepo b/projects/packages/masterbar/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/masterbar/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/masterbar/changelog/update-eslint-9 b/projects/packages/masterbar/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/masterbar/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/masterbar/changelog/update-packages-fix-eslint-9-lints b/projects/packages/masterbar/changelog/update-packages-fix-eslint-9-lints deleted file mode 100644 index b3176fbef2f88..0000000000000 --- a/projects/packages/masterbar/changelog/update-packages-fix-eslint-9-lints +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Fix some JS lints ahead of eslint 9 upgrade. - - diff --git a/projects/packages/masterbar/package.json b/projects/packages/masterbar/package.json index 74a0061afd8c1..1b31349359348 100644 --- a/projects/packages/masterbar/package.json +++ b/projects/packages/masterbar/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-masterbar", - "version": "0.10.2", + "version": "0.10.3", "description": "The WordPress.com Toolbar feature replaces the default admin bar and offers quick links to the Reader, all your sites, your WordPress.com profile, and notifications.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/masterbar/#readme", "bugs": { diff --git a/projects/packages/masterbar/src/class-main.php b/projects/packages/masterbar/src/class-main.php index 87eb931da0ae7..1a58e128ea78f 100644 --- a/projects/packages/masterbar/src/class-main.php +++ b/projects/packages/masterbar/src/class-main.php @@ -14,7 +14,7 @@ */ class Main { - const PACKAGE_VERSION = '0.10.2'; + const PACKAGE_VERSION = '0.10.3'; /** * Initializer. diff --git a/projects/packages/my-jetpack/CHANGELOG.md b/projects/packages/my-jetpack/CHANGELOG.md index c454301157913..6fe8e843cb673 100644 --- a/projects/packages/my-jetpack/CHANGELOG.md +++ b/projects/packages/my-jetpack/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.1.2] - 2024-12-16 +### Added +- Add AI to Complete feature copy. [#40577] + +### Changed +- Remove purchase related elements when Complete is on site. [#40554] +- Updated package dependencies. [#40564] + +### Fixed +- Fixed lints following ESLint rule changes for TS. [#40584] +- My Jetpack: fix animation flick on connection screen in My Jetpack. [#40533] + ## [5.1.1] - 2024-12-04 ### Changed - Updated package dependencies. [#40363] @@ -1856,6 +1868,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Created package +[5.1.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/5.1.1...5.1.2 [5.1.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/5.1.0...5.1.1 [5.1.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/5.0.4...5.1.0 [5.0.4]: https://github.com/Automattic/jetpack-my-jetpack/compare/5.0.3...5.0.4 diff --git a/projects/packages/my-jetpack/changelog/add-ai-to-complete-feature-copy b/projects/packages/my-jetpack/changelog/add-ai-to-complete-feature-copy deleted file mode 100644 index bce5afbecc09b..0000000000000 --- a/projects/packages/my-jetpack/changelog/add-ai-to-complete-feature-copy +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: added - -Add AI to Complete feature copy diff --git a/projects/packages/my-jetpack/changelog/fix-connection-animation-flick b/projects/packages/my-jetpack/changelog/fix-connection-animation-flick deleted file mode 100644 index 1b0f9d2be77c2..0000000000000 --- a/projects/packages/my-jetpack/changelog/fix-connection-animation-flick +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -My Jetpack: fix animation flick on connection screen in My Jetpack. diff --git a/projects/packages/my-jetpack/changelog/renovate-wordpress-monorepo b/projects/packages/my-jetpack/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/my-jetpack/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/my-jetpack/changelog/update-eslint-9 b/projects/packages/my-jetpack/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/my-jetpack/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/my-jetpack/changelog/update-eslint-config-for-ts-files b/projects/packages/my-jetpack/changelog/update-eslint-config-for-ts-files deleted file mode 100644 index fefec667583fd..0000000000000 --- a/projects/packages/my-jetpack/changelog/update-eslint-config-for-ts-files +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Fixed lints following ESLint rule changes for TS diff --git a/projects/packages/my-jetpack/changelog/update-my-jetpack-hide-purchases-elements-when-complete-is-active b/projects/packages/my-jetpack/changelog/update-my-jetpack-hide-purchases-elements-when-complete-is-active deleted file mode 100644 index da1eace88d760..0000000000000 --- a/projects/packages/my-jetpack/changelog/update-my-jetpack-hide-purchases-elements-when-complete-is-active +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Remove purchase related elements when Complete is on site diff --git a/projects/packages/my-jetpack/changelog/update-packages-fix-eslint-9-lints b/projects/packages/my-jetpack/changelog/update-packages-fix-eslint-9-lints deleted file mode 100644 index b3176fbef2f88..0000000000000 --- a/projects/packages/my-jetpack/changelog/update-packages-fix-eslint-9-lints +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Fix some JS lints ahead of eslint 9 upgrade. - - diff --git a/projects/packages/my-jetpack/package.json b/projects/packages/my-jetpack/package.json index ab0123621f7c2..fd24a11565d10 100644 --- a/projects/packages/my-jetpack/package.json +++ b/projects/packages/my-jetpack/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-my-jetpack", - "version": "5.1.1", + "version": "5.1.2", "description": "WP Admin page with information and configuration shared among all Jetpack stand-alone plugins", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/my-jetpack/#readme", "bugs": { diff --git a/projects/packages/my-jetpack/src/class-initializer.php b/projects/packages/my-jetpack/src/class-initializer.php index 5139af566c26c..3d8bba78d485a 100644 --- a/projects/packages/my-jetpack/src/class-initializer.php +++ b/projects/packages/my-jetpack/src/class-initializer.php @@ -42,7 +42,7 @@ class Initializer { * * @var string */ - const PACKAGE_VERSION = '5.1.1'; + const PACKAGE_VERSION = '5.1.2'; /** * HTML container ID for the IDC screen on My Jetpack page. diff --git a/projects/packages/post-list/CHANGELOG.md b/projects/packages/post-list/CHANGELOG.md index cd495cf9aa89a..ca107904e5521 100644 --- a/projects/packages/post-list/CHANGELOG.md +++ b/projects/packages/post-list/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.2] - 2024-12-16 +### Changed +- Internal updates. + ## [0.7.1] - 2024-11-25 ### Changed - Updated dependencies. [#40286] @@ -126,6 +130,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated the default columns displayed on the post and page list screens - Refactored thumbnail preview to function server side. All javascript removed. +[0.7.2]: https://github.com/automattic/jetpack-post-list/compare/v0.7.1...v0.7.2 [0.7.1]: https://github.com/automattic/jetpack-post-list/compare/v0.7.0...v0.7.1 [0.7.0]: https://github.com/automattic/jetpack-post-list/compare/v0.6.5...v0.7.0 [0.6.5]: https://github.com/automattic/jetpack-post-list/compare/v0.6.4...v0.6.5 diff --git a/projects/packages/post-list/changelog/update-eslint-9 b/projects/packages/post-list/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/post-list/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/post-list/src/class-post-list.php b/projects/packages/post-list/src/class-post-list.php index 5651a2372719a..d8eea02d1f6da 100644 --- a/projects/packages/post-list/src/class-post-list.php +++ b/projects/packages/post-list/src/class-post-list.php @@ -15,7 +15,7 @@ */ class Post_List { - const PACKAGE_VERSION = '0.7.1'; + const PACKAGE_VERSION = '0.7.2'; const FEATURE = 'enhanced_post_list'; /** diff --git a/projects/packages/publicize/CHANGELOG.md b/projects/packages/publicize/CHANGELOG.md index 3d16f5301164a..3416ad5578cf8 100644 --- a/projects/packages/publicize/CHANGELOG.md +++ b/projects/packages/publicize/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.56.5] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.56.4] - 2024-12-09 ### Changed - Updated package dependencies. [#40363] @@ -783,6 +787,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated package dependencies. - Update package.json metadata. +[0.56.5]: https://github.com/Automattic/jetpack-publicize/compare/v0.56.4...v0.56.5 [0.56.4]: https://github.com/Automattic/jetpack-publicize/compare/v0.56.3...v0.56.4 [0.56.3]: https://github.com/Automattic/jetpack-publicize/compare/v0.56.2...v0.56.3 [0.56.2]: https://github.com/Automattic/jetpack-publicize/compare/v0.56.1...v0.56.2 diff --git a/projects/packages/publicize/changelog/renovate-wordpress-monorepo b/projects/packages/publicize/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/publicize/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/publicize/changelog/update-eslint-9 b/projects/packages/publicize/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/publicize/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/publicize/package.json b/projects/packages/publicize/package.json index 194547a7a1686..0f0950a6dbbf9 100644 --- a/projects/packages/publicize/package.json +++ b/projects/packages/publicize/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-publicize", - "version": "0.56.4", + "version": "0.56.5", "description": "Publicize makes it easy to share your site’s posts on several social media networks automatically when you publish a new post.", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/publicize/#readme", "bugs": { diff --git a/projects/packages/search/CHANGELOG.md b/projects/packages/search/CHANGELOG.md index b6bf9cfdf9f00..2e2292fdee780 100644 --- a/projects/packages/search/CHANGELOG.md +++ b/projects/packages/search/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.47.4] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + +### Fixed +- Fix missing instant search dialog for themes like Rebalance. [#40472] + ## [0.47.3] - 2024-12-09 ### Changed - Updated package dependencies. [#40363] @@ -1096,6 +1103,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated package dependencies. - Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't. +[0.47.4]: https://github.com/Automattic/jetpack-search/compare/v0.47.3...v0.47.4 [0.47.3]: https://github.com/Automattic/jetpack-search/compare/v0.47.2...v0.47.3 [0.47.2]: https://github.com/Automattic/jetpack-search/compare/v0.47.1...v0.47.2 [0.47.1]: https://github.com/Automattic/jetpack-search/compare/v0.47.0...v0.47.1 diff --git a/projects/packages/search/changelog/fix-missing-search-overlay-in-some-themes b/projects/packages/search/changelog/fix-missing-search-overlay-in-some-themes deleted file mode 100644 index 2fa58d10f6081..0000000000000 --- a/projects/packages/search/changelog/fix-missing-search-overlay-in-some-themes +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Fix missing instant search dialog for themes like Rebalance diff --git a/projects/packages/search/changelog/renovate-wordpress-monorepo b/projects/packages/search/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/search/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/search/changelog/update-eslint-9 b/projects/packages/search/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/search/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/search/changelog/update-packages-fix-eslint-9-lints b/projects/packages/search/changelog/update-packages-fix-eslint-9-lints deleted file mode 100644 index b3176fbef2f88..0000000000000 --- a/projects/packages/search/changelog/update-packages-fix-eslint-9-lints +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Fix some JS lints ahead of eslint 9 upgrade. - - diff --git a/projects/packages/search/package.json b/projects/packages/search/package.json index 280d742a4744e..eea2b0186af2f 100644 --- a/projects/packages/search/package.json +++ b/projects/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "jetpack-search", - "version": "0.47.3", + "version": "0.47.4", "description": "Package for Jetpack Search products", "main": "main.js", "directories": { diff --git a/projects/packages/search/src/class-package.php b/projects/packages/search/src/class-package.php index 0c8bd0c30bd23..cfd81b6ea69f7 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.47.3'; + const VERSION = '0.47.4'; const SLUG = 'search'; /** diff --git a/projects/packages/sync/CHANGELOG.md b/projects/packages/sync/CHANGELOG.md index 6227f85825af2..1f255f85f9f5e 100644 --- a/projects/packages/sync/CHANGELOG.md +++ b/projects/packages/sync/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.1.1] - 2024-12-16 +### Changed +- Internal updates. + ## [4.1.0] - 2024-12-09 ### Added - WordPress.com Features: add Holiday Snow functionality. [#40478] @@ -1355,6 +1359,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Packages: Move sync to a classmapped package +[4.1.1]: https://github.com/Automattic/jetpack-sync/compare/v4.1.0...v4.1.1 [4.1.0]: https://github.com/Automattic/jetpack-sync/compare/v4.0.2...v4.1.0 [4.0.2]: https://github.com/Automattic/jetpack-sync/compare/v4.0.1...v4.0.2 [4.0.1]: https://github.com/Automattic/jetpack-sync/compare/v4.0.0...v4.0.1 diff --git a/projects/packages/sync/changelog/add-allow-coupons-gifts-cpt-types b/projects/packages/sync/changelog/add-allow-coupons-gifts-cpt-types deleted file mode 100644 index dbda2687742d8..0000000000000 --- a/projects/packages/sync/changelog/add-allow-coupons-gifts-cpt-types +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: added - - diff --git a/projects/packages/sync/src/class-package-version.php b/projects/packages/sync/src/class-package-version.php index acfc7f927e7ab..17a6cba1f66df 100644 --- a/projects/packages/sync/src/class-package-version.php +++ b/projects/packages/sync/src/class-package-version.php @@ -12,7 +12,7 @@ */ class Package_Version { - const PACKAGE_VERSION = '4.1.0'; + const PACKAGE_VERSION = '4.1.1'; const PACKAGE_SLUG = 'sync'; diff --git a/projects/packages/videopress/CHANGELOG.md b/projects/packages/videopress/CHANGELOG.md index 1772173a37541..38c822588fe3c 100644 --- a/projects/packages/videopress/CHANGELOG.md +++ b/projects/packages/videopress/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.25.5] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + +### Fixed +- Fixed lints following ESLint rule changes for TS [#40584] + ## [0.25.4] - 2024-12-09 ### Changed - Updated package dependencies. [#40363] @@ -1497,6 +1504,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Created empty package [#24952] +[0.25.5]: https://github.com/Automattic/jetpack-videopress/compare/v0.25.4...v0.25.5 [0.25.4]: https://github.com/Automattic/jetpack-videopress/compare/v0.25.3...v0.25.4 [0.25.3]: https://github.com/Automattic/jetpack-videopress/compare/v0.25.2...v0.25.3 [0.25.2]: https://github.com/Automattic/jetpack-videopress/compare/v0.25.1...v0.25.2 diff --git a/projects/packages/videopress/changelog/renovate-wordpress-monorepo b/projects/packages/videopress/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/videopress/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/videopress/changelog/update-eslint-9 b/projects/packages/videopress/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/videopress/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/videopress/changelog/update-eslint-config-for-ts-files b/projects/packages/videopress/changelog/update-eslint-config-for-ts-files deleted file mode 100644 index fefec667583fd..0000000000000 --- a/projects/packages/videopress/changelog/update-eslint-config-for-ts-files +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Fixed lints following ESLint rule changes for TS diff --git a/projects/packages/videopress/changelog/update-packages-fix-eslint-9-lints b/projects/packages/videopress/changelog/update-packages-fix-eslint-9-lints deleted file mode 100644 index b3176fbef2f88..0000000000000 --- a/projects/packages/videopress/changelog/update-packages-fix-eslint-9-lints +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Fix some JS lints ahead of eslint 9 upgrade. - - diff --git a/projects/packages/videopress/package.json b/projects/packages/videopress/package.json index bac246c717c73..0379e51d3947b 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.25.4", + "version": "0.25.5", "description": "VideoPress package", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/videopress/#readme", "bugs": { diff --git a/projects/packages/videopress/src/class-package-version.php b/projects/packages/videopress/src/class-package-version.php index efca5de065658..22f2263679e4e 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.25.4'; + const PACKAGE_VERSION = '0.25.5'; const PACKAGE_SLUG = 'videopress'; diff --git a/projects/packages/wordads/CHANGELOG.md b/projects/packages/wordads/CHANGELOG.md index 6f64613c08a91..0d1e4eb8914bc 100644 --- a/projects/packages/wordads/CHANGELOG.md +++ b/projects/packages/wordads/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.4] - 2024-12-16 +### Changed +- Updated package dependencies. [#40564] + ## [0.4.3] - 2024-12-09 ### Changed - Updated package dependencies. [#40363] @@ -450,6 +454,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - PHPCS: Fix `WordPress.Security.ValidatedSanitizedInput` - Updated package dependencies. +[0.4.4]: https://github.com/Automattic/jetpack-wordads/compare/v0.4.3...v0.4.4 [0.4.3]: https://github.com/Automattic/jetpack-wordads/compare/v0.4.2...v0.4.3 [0.4.2]: https://github.com/Automattic/jetpack-wordads/compare/v0.4.1...v0.4.2 [0.4.1]: https://github.com/Automattic/jetpack-wordads/compare/v0.4.0...v0.4.1 diff --git a/projects/packages/wordads/changelog/renovate-wordpress-monorepo b/projects/packages/wordads/changelog/renovate-wordpress-monorepo deleted file mode 100644 index c47cb18e82997..0000000000000 --- a/projects/packages/wordads/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: changed - -Updated package dependencies. diff --git a/projects/packages/wordads/changelog/update-eslint-9 b/projects/packages/wordads/changelog/update-eslint-9 deleted file mode 100644 index 1cb10572ab69e..0000000000000 --- a/projects/packages/wordads/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: changed -Comment: Update eslint config for eslint 9. - - diff --git a/projects/packages/wordads/changelog/update-packages-fix-eslint-9-lints b/projects/packages/wordads/changelog/update-packages-fix-eslint-9-lints deleted file mode 100644 index b3176fbef2f88..0000000000000 --- a/projects/packages/wordads/changelog/update-packages-fix-eslint-9-lints +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fixed -Comment: Fix some JS lints ahead of eslint 9 upgrade. - - diff --git a/projects/packages/wordads/package.json b/projects/packages/wordads/package.json index dacdf1ff4839a..2c5e5db77eaa9 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.4.3", + "version": "0.4.4", "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", diff --git a/projects/packages/wordads/src/class-package.php b/projects/packages/wordads/src/class-package.php index f595a4884e934..a9774761ca700 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.4.3'; + const VERSION = '0.4.4'; const SLUG = 'wordads'; /** diff --git a/projects/packages/masterbar/changelog/add-pre-option-filter-duplicate-views b/projects/plugins/classic-theme-helper-plugin/changelog/prerelease#13 similarity index 52% rename from projects/packages/masterbar/changelog/add-pre-option-filter-duplicate-views rename to projects/plugins/classic-theme-helper-plugin/changelog/prerelease#13 index 05ffe09f19e66..9aa70e3ec1f75 100644 --- a/projects/packages/masterbar/changelog/add-pre-option-filter-duplicate-views +++ b/projects/plugins/classic-theme-helper-plugin/changelog/prerelease#13 @@ -1,4 +1,5 @@ Significance: patch Type: changed +Comment: Updated composer.lock. diff --git a/projects/plugins/classic-theme-helper-plugin/composer.lock b/projects/plugins/classic-theme-helper-plugin/composer.lock index d5c2bb5d47a7b..72ba0b820a3c3 100644 --- a/projects/plugins/classic-theme-helper-plugin/composer.lock +++ b/projects/plugins/classic-theme-helper-plugin/composer.lock @@ -202,7 +202,7 @@ "dist": { "type": "path", "url": "../../packages/classic-theme-helper", - "reference": "313ac02e74dbf44eec1ddbf485af492b66d96a9a" + "reference": "198fd841c5341850e247c46168d77b1bc6a13a34" }, "require": { "automattic/jetpack-assets": "@dev", @@ -220,7 +220,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.7.x-dev" + "dev-trunk": "0.8.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-classic-theme-helper/compare/v${old}...v${new}" diff --git a/projects/plugins/jetpack/CHANGELOG.md b/projects/plugins/jetpack/CHANGELOG.md index 30f7c119ffd4f..9f68eef50a7ff 100644 --- a/projects/plugins/jetpack/CHANGELOG.md +++ b/projects/plugins/jetpack/CHANGELOG.md @@ -2,6 +2,31 @@ ### This is a list detailing changes for all Jetpack releases. +## 14.2-a.3 - 2024-12-16 +### Enhancements +- Social: Improved Jetpack likes behavior for better theme integration if the post has likes. [#40544] + +### Improved compatibility +- Jetpack Testimonials: Ensure feature loads via the Classic Theme Helper package instead of the module. [#40388] + +### Bug fixes +- Facebook Embeds: Add a white background to embeds to avoid transparent background interfering with readability. [#40547] +- Form Block: fixed validation of URL input types to allow query strings. [#40490] +- Import: Correctly setting the WP_IMPORTING constant when doing an import. [#40563] +- SEO: Ensure that SEO fields are not visible when another SEO plugin is active. [#40567] + +### Other changes +- API Endpoints: Make sure manual plugin updates applied when auto update is disabled. [#40534] +- Block Editor: We now trigger an error in cases where the media property does not exist so that the editor does not crash under some circumstances. [#40588] +- Blogging prompts endpoint: disable bloganuary for 2025 and beyond. [#40491] +- Carousel: Fixed a bug where under rare circumstances, the carousel would have made null requests when loading an image. [#40535] +- Comments: Make list of allowable comment types filterable so custom types can be returned if needed. [#40530] +- Infinite scroll: Remove deprecated code. [#40528] +- Jetpack AI: Adding tooltips to AI feedback component. [#40506] +- Jetpack AI: Add tracking to the thumbs up/down component, saving the component and rating to Tracks. [#40553] +- General: Fixed lints following ESLint rule changes for TS. [#40584] +- Updated package dependencies. [#40515] [#40564] + ## 14.2-a.1 - 2024-12-09 ### Enhancements - Stats: Allow fetching stats for specific sites when programatically fetching stats using Jetpack's tools. [#40441] diff --git a/projects/plugins/jetpack/changelog/add-allow-coupons-gifts-cpt-types b/projects/plugins/jetpack/changelog/add-allow-coupons-gifts-cpt-types deleted file mode 100644 index 1ef8afea050a2..0000000000000 --- a/projects/plugins/jetpack/changelog/add-allow-coupons-gifts-cpt-types +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - - diff --git a/projects/plugins/jetpack/changelog/add-comment-type-filter b/projects/plugins/jetpack/changelog/add-comment-type-filter deleted file mode 100644 index fa15ffe923d49..0000000000000 --- a/projects/plugins/jetpack/changelog/add-comment-type-filter +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - -Comments: Make list of allowable comment types filterable so custom types can be returned if needed. diff --git a/projects/plugins/jetpack/changelog/add-pre-option-filter-duplicate-views b/projects/plugins/jetpack/changelog/add-pre-option-filter-duplicate-views deleted file mode 100644 index 0bf3bd691e178..0000000000000 --- a/projects/plugins/jetpack/changelog/add-pre-option-filter-duplicate-views +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: other -Comment: Changes related to WoA - - diff --git a/projects/plugins/jetpack/changelog/add-require-testimonials-cpt-in-classic-theme-helper b/projects/plugins/jetpack/changelog/add-require-testimonials-cpt-in-classic-theme-helper deleted file mode 100644 index 513f45cc6c883..0000000000000 --- a/projects/plugins/jetpack/changelog/add-require-testimonials-cpt-in-classic-theme-helper +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: compat - -Jetpack Testimonials: Ensure feature loads via the Classic Theme Helper package instead of the module. diff --git a/projects/plugins/jetpack/changelog/change-jetpack-ai-track-ai-rating b/projects/plugins/jetpack/changelog/change-jetpack-ai-track-ai-rating deleted file mode 100644 index 42fb985eadd49..0000000000000 --- a/projects/plugins/jetpack/changelog/change-jetpack-ai-track-ai-rating +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - -Jetpack AI: Add tracking to the thumbs up/down component, saving the component and rating to Tracks diff --git a/projects/plugins/jetpack/changelog/disable-bloganuary b/projects/plugins/jetpack/changelog/disable-bloganuary deleted file mode 100644 index 69862a4bbf557..0000000000000 --- a/projects/plugins/jetpack/changelog/disable-bloganuary +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: other - -Blogging prompts endpoint: disable bloganuary for 2025 and beyond. diff --git a/projects/plugins/jetpack/changelog/fix-carousel-null-request b/projects/plugins/jetpack/changelog/fix-carousel-null-request deleted file mode 100644 index 8696ab8d13acf..0000000000000 --- a/projects/plugins/jetpack/changelog/fix-carousel-null-request +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - -Carousel: Under rare circumstances, the carousel will make null requests when loading an image diff --git a/projects/plugins/jetpack/changelog/fix-facebook-embed-background-color b/projects/plugins/jetpack/changelog/fix-facebook-embed-background-color deleted file mode 100644 index 49cf13c2c69a1..0000000000000 --- a/projects/plugins/jetpack/changelog/fix-facebook-embed-background-color +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: bugfix - -Facebook Embeds: add a white background to embeds to avoid transparent background interfering with readability. diff --git a/projects/plugins/jetpack/changelog/fix-form-block-broken-url-input-validation b/projects/plugins/jetpack/changelog/fix-form-block-broken-url-input-validation deleted file mode 100644 index 18148f5e92c1e..0000000000000 --- a/projects/plugins/jetpack/changelog/fix-form-block-broken-url-input-validation +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: bugfix - -Form Block: fixed validation of URL input types to allow query strings. diff --git a/projects/plugins/jetpack/changelog/fix-hide-seo-title-desc-fields b/projects/plugins/jetpack/changelog/fix-hide-seo-title-desc-fields deleted file mode 100644 index 758aff99f81a4..0000000000000 --- a/projects/plugins/jetpack/changelog/fix-hide-seo-title-desc-fields +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: bugfix - -SEO: Ensure that SEO fields are not visible when another SEO plugin is active. diff --git a/projects/plugins/jetpack/changelog/fix-manual-plugin-updates b/projects/plugins/jetpack/changelog/fix-manual-plugin-updates deleted file mode 100644 index b9e7c7872762f..0000000000000 --- a/projects/plugins/jetpack/changelog/fix-manual-plugin-updates +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - -API Endpoints: make sure manual plugin updates applied when auto update is disabled diff --git a/projects/plugins/jetpack/changelog/fix-pexels-empty-items-on-editor b/projects/plugins/jetpack/changelog/fix-pexels-empty-items-on-editor deleted file mode 100644 index 190832ebc3a6a..0000000000000 --- a/projects/plugins/jetpack/changelog/fix-pexels-empty-items-on-editor +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - -We now trigger an error in cases where the media property does not exist so that the editor does not crash under some circumstances diff --git a/projects/plugins/jetpack/changelog/fix-wp-import-issue b/projects/plugins/jetpack/changelog/fix-wp-import-issue deleted file mode 100644 index 06f1f5564ab35..0000000000000 --- a/projects/plugins/jetpack/changelog/fix-wp-import-issue +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: bugfix - -Import: setting WP_IMPORTING when doing an import diff --git a/projects/plugins/jetpack/changelog/remove-cleanup-infinite-scroll-2 b/projects/plugins/jetpack/changelog/remove-cleanup-infinite-scroll-2 deleted file mode 100644 index f4b67870ce2c3..0000000000000 --- a/projects/plugins/jetpack/changelog/remove-cleanup-infinite-scroll-2 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: other - -Infinite scroll: remove deprecated code. diff --git a/projects/plugins/jetpack/changelog/renovate-lock-file-maintenance b/projects/plugins/jetpack/changelog/renovate-lock-file-maintenance deleted file mode 100644 index 1eaea6a769e84..0000000000000 --- a/projects/plugins/jetpack/changelog/renovate-lock-file-maintenance +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - -Updated package dependencies. diff --git a/projects/plugins/jetpack/changelog/renovate-wordpress-monorepo b/projects/plugins/jetpack/changelog/renovate-wordpress-monorepo deleted file mode 100644 index 1eaea6a769e84..0000000000000 --- a/projects/plugins/jetpack/changelog/renovate-wordpress-monorepo +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - -Updated package dependencies. diff --git a/projects/plugins/jetpack/changelog/update-eslint-9 b/projects/plugins/jetpack/changelog/update-eslint-9 deleted file mode 100644 index 02180870fd73b..0000000000000 --- a/projects/plugins/jetpack/changelog/update-eslint-9 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: other -Comment: Update eslint config for eslint 9. - - diff --git a/projects/plugins/jetpack/changelog/update-eslint-config-for-ts-files b/projects/plugins/jetpack/changelog/update-eslint-config-for-ts-files deleted file mode 100644 index fb360f8080b3e..0000000000000 --- a/projects/plugins/jetpack/changelog/update-eslint-config-for-ts-files +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - -Fixed lints following ESLint rule changes for TS diff --git a/projects/plugins/jetpack/changelog/update-jetpack-ai-feedback-add-tooltips b/projects/plugins/jetpack/changelog/update-jetpack-ai-feedback-add-tooltips deleted file mode 100644 index 3c9d8b16dac09..0000000000000 --- a/projects/plugins/jetpack/changelog/update-jetpack-ai-feedback-add-tooltips +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: other - -Jetpack AI: Adding tooltips to AI feedback component diff --git a/projects/plugins/jetpack/changelog/update-jetpack-fix-eslint-9-lints b/projects/plugins/jetpack/changelog/update-jetpack-fix-eslint-9-lints deleted file mode 100644 index c6edddc29cc11..0000000000000 --- a/projects/plugins/jetpack/changelog/update-jetpack-fix-eslint-9-lints +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: other -Comment: Fix some JS lints ahead of the eslint 9 upgrade. - - diff --git a/projects/plugins/jetpack/changelog/update-likes-add-likes-class b/projects/plugins/jetpack/changelog/update-likes-add-likes-class deleted file mode 100644 index ee37aa6901e02..0000000000000 --- a/projects/plugins/jetpack/changelog/update-likes-add-likes-class +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: enhancement - -Added a `liked` class to the Jetpack likes iFrame wrapper DIV based on if the post has any likes. \ No newline at end of file diff --git a/projects/plugins/jetpack/changelog/verbum-fix-stuff b/projects/plugins/jetpack/changelog/verbum-fix-stuff deleted file mode 100644 index e6db986ea1edd..0000000000000 --- a/projects/plugins/jetpack/changelog/verbum-fix-stuff +++ /dev/null @@ -1,3 +0,0 @@ -Significance: patch -Type: other -Comment: fix redirect and awaiting cookie setting diff --git a/projects/plugins/jetpack/composer.json b/projects/plugins/jetpack/composer.json index 10dfe35ae8d16..b959b821e3b88 100644 --- a/projects/plugins/jetpack/composer.json +++ b/projects/plugins/jetpack/composer.json @@ -106,7 +106,7 @@ "platform": { "ext-intl": "0.0.0" }, - "autoloader-suffix": "f11009ded9fc4592b6a05b61ce272b3c_jetpackⓥ14_2_a_1", + "autoloader-suffix": "f11009ded9fc4592b6a05b61ce272b3c_jetpackⓥ14_2_a_3", "allow-plugins": { "automattic/jetpack-autoloader": true, "automattic/jetpack-composer-plugin": true diff --git a/projects/plugins/jetpack/composer.lock b/projects/plugins/jetpack/composer.lock index 76b1ad98c0969..40486833b178b 100644 --- a/projects/plugins/jetpack/composer.lock +++ b/projects/plugins/jetpack/composer.lock @@ -704,7 +704,7 @@ "dist": { "type": "path", "url": "../../packages/classic-theme-helper", - "reference": "313ac02e74dbf44eec1ddbf485af492b66d96a9a" + "reference": "198fd841c5341850e247c46168d77b1bc6a13a34" }, "require": { "automattic/jetpack-assets": "@dev", @@ -722,7 +722,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.7.x-dev" + "dev-trunk": "0.8.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-classic-theme-helper/compare/v${old}...v${new}" diff --git a/projects/plugins/jetpack/jetpack.php b/projects/plugins/jetpack/jetpack.php index 8950e92e1125f..06f51f1637176 100644 --- a/projects/plugins/jetpack/jetpack.php +++ b/projects/plugins/jetpack/jetpack.php @@ -4,7 +4,7 @@ * Plugin URI: https://jetpack.com * Description: Security, performance, and marketing tools made by WordPress experts. Jetpack keeps your site protected so you can focus on more important things. * Author: Automattic - * Version: 14.2-a.1 + * Version: 14.2-a.3 * Author URI: https://jetpack.com * License: GPL2+ * Text Domain: jetpack @@ -34,7 +34,7 @@ define( 'JETPACK__MINIMUM_WP_VERSION', '6.6' ); define( 'JETPACK__MINIMUM_PHP_VERSION', '7.2' ); -define( 'JETPACK__VERSION', '14.2-a.1' ); +define( 'JETPACK__VERSION', '14.2-a.3' ); /** * Constant used to fetch the connection owner token diff --git a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-comment-endpoint.php b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-comment-endpoint.php index aa7b38d594a67..5fe7d7ca443a2 100644 --- a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-comment-endpoint.php +++ b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-comment-endpoint.php @@ -74,7 +74,7 @@ public function get_comment( $comment_id, $context ) { /** * Filter the comment types that are allowed to be returned. * - * @since $$next-version$$ + * @since 14.2 * * @module json-api * diff --git a/projects/plugins/jetpack/modules/custom-post-types/testimonial.php b/projects/plugins/jetpack/modules/custom-post-types/testimonial.php index be27b5d8ecd95..363e8c058116a 100644 --- a/projects/plugins/jetpack/modules/custom-post-types/testimonial.php +++ b/projects/plugins/jetpack/modules/custom-post-types/testimonial.php @@ -26,10 +26,10 @@ class Jetpack_Testimonial { /** * Initialize class. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public static function init() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return Automattic\Jetpack\Classic_Theme_Helper\Jetpack_Testimonial::init(); } @@ -81,10 +81,10 @@ public static function __callStatic( $name, $arguments ) { * Registers the custom post types and adds action/filter handlers, but * only if the site supports it. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function maybe_register_cpt() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->maybe_register_cpt(); } @@ -92,12 +92,12 @@ public function maybe_register_cpt() { * Add a checkbox field in 'Settings' > 'Writing' * for enabling CPT functionality. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @return void */ public function settings_api_init() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->settings_api_init(); } @@ -105,12 +105,12 @@ public function settings_api_init() { * HTML code to display a checkbox true/false option * for the CPT setting. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @return void */ public function setting_html() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->setting_html(); } @@ -121,212 +121,212 @@ public function setting_html() { * @return array `$post_types` with our type added. */ public function allow_cpt_rest_api_type( $post_types ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return $this->new_instance->allow_cpt_rest_api_type( $post_types ); } /** * Bump Testimonial > New Activation stat * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function new_activation_stat_bump() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->new_activation_stat_bump(); } /** * Bump Testimonial > Option On/Off stats to get total active * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param mixed $old The old option value. * @param mixed $new The new option value. */ public function update_option_stat_bump( $old, $new ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->update_option_stat_bump( $old, $new ); } /** * Bump Testimonial > Published Testimonials stat when testimonials are published * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function new_testimonial_stat_bump() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->new_testimonial_stat_bump(); } /** * Flush permalinks when CPT option is turned on/off * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function flush_rules_on_enable() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->flush_rules_on_enable(); } /** * Count published testimonials and flush permalinks when first testimonial is published * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function flush_rules_on_first_testimonial() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->flush_rules_on_first_testimonial(); } /** * Flush permalinks when CPT supported theme is activated * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function flush_rules_on_switch() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->flush_rules_on_switch(); } /** * On plugin/theme activation, check if current theme supports CPT * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public static function activation_post_type_support() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); Automattic\Jetpack\Classic_Theme_Helper\Jetpack_Testimonial::activation_post_type_support(); } /** * On theme switch, check if CPT item exists and disable if not * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function deactivation_post_type_support() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->deactivation_post_type_support(); } /** * Register Post Type * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function register_post_types() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->register_post_types(); } /** * Update messages for the Testimonial admin. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param array $messages Existing post update messages. * @return array Updated `$messages`. */ public function updated_messages( $messages ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return $this->new_instance->updated_messages( $messages ); } /** * Change ‘Enter Title Here’ text for the Testimonial. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param string $title Placeholder text. Default 'Add title'. * @return string Replacement title. */ public function change_default_title( $title ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return $this->new_instance->change_default_title( $title ); } /** * Change ‘Title’ column label on all Testimonials page. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param array $columns An array of column names. * @return array Updated array. */ public function edit_title_column_label( $columns ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return $this->new_instance->edit_title_column_label( $columns ); } /** * Follow CPT reading setting on CPT archive page * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param WP_Query $query A WP_Query instance. */ public function query_reading_setting( $query ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->query_reading_setting( $query ); } /** * If Infinite Scroll is set to 'click', use our custom reading setting instead of core's `posts_per_page`. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param array $settings Array of Infinite Scroll settings. * @return array Updated `$settings`. */ public function infinite_scroll_click_posts_per_page( $settings ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return $this->new_instance->infinite_scroll_click_posts_per_page( $settings ); } /** * Add CPT to Dotcom sitemap * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param array $post_types Array of post types included in sitemap. * @return array Updated `$post_types`. */ public function add_to_sitemap( $post_types ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return $this->new_instance->add_to_sitemap( $post_types ); } /** * Adds a submenu link to the Customizer. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function add_customize_page() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->add_customize_page(); } /** * Adds testimonial section to the Customizer. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param WP_Customize_Manager $wp_customize Customizer instance. */ public function customize_register( $wp_customize ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $this->new_instance->customize_register( $wp_customize ); } /** * Add Featured image to theme mod if necessary. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param array $opt The value of the current theme modification. * @return array Updated `$opt`. */ public function coerce_testimonial_image_to_url( $opt ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return $this->new_instance->coerce_testimonial_image_to_url( $opt ); } @@ -334,14 +334,14 @@ public function coerce_testimonial_image_to_url( $opt ) { * Our [testimonial] shortcode. * Prints Testimonial data styled to look good on *any* theme. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param array $atts Shortcode attributes. * * @return string HTML from `self::jetpack_testimonial_shortcode_html()`. */ public static function jetpack_testimonial_shortcode( $atts ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return Automattic\Jetpack\Classic_Theme_Helper\Jetpack_Testimonial::jetpack_testimonial_shortcode( $atts ); } } @@ -349,10 +349,10 @@ public static function jetpack_testimonial_shortcode( $atts ) { /** * Additional Testimonial customizer options. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ function jetpack_testimonial_custom_control_classes() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); /** * Clean the title parameter. */ @@ -360,13 +360,13 @@ class Jetpack_Testimonial_Title_Control extends WP_Customize_Control { /** * Sanitize content passed to control. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param string $value Control value. * @return string Sanitized value. */ public static function sanitize_content( $value ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return Automattic\Jetpack\Classic_Theme_Helper\Jetpack_Testimonial_Title_Control::sanitize_content( $value ); } } @@ -385,10 +385,10 @@ class Jetpack_Testimonial_Textarea_Control extends WP_Customize_Control { /** * Render the control's content. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. */ public function render_content() { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); $testimonial_textarea_control = new Automattic\Jetpack\Classic_Theme_Helper\Jetpack_Testimonial_Textarea_Control( $this->manager, $this->id, $this->args ); $testimonial_textarea_control->render_content(); } @@ -396,13 +396,13 @@ public function render_content() { /** * Sanitize content passed to control. * - * @deprecated $$next-version$$ Moved to Classic Theme Helper package. + * @deprecated 14.2 Moved to Classic Theme Helper package. * * @param string $value Control value. * @return string Sanitized value. */ public static function sanitize_content( $value ) { - _deprecated_function( __FUNCTION__, 'jetpack-$$next-version$$' ); + _deprecated_function( __FUNCTION__, 'jetpack-14.2' ); return Automattic\Jetpack\Classic_Theme_Helper\Jetpack_Testimonial_Textarea_Control::sanitize_content( $value ); } } diff --git a/projects/plugins/jetpack/package.json b/projects/plugins/jetpack/package.json index ee73f177d997e..948861ec400b5 100644 --- a/projects/plugins/jetpack/package.json +++ b/projects/plugins/jetpack/package.json @@ -1,6 +1,6 @@ { "name": "Jetpack", - "version": "14.2.0-a.1", + "version": "14.2.0-a.3", "private": true, "description": "[Jetpack](https://jetpack.com/) is a WordPress plugin that supercharges your self-hosted WordPress site with the awesome cloud power of [WordPress.com](https://wordpress.com).", "homepage": "https://jetpack.com", diff --git a/projects/plugins/jetpack/readme.txt b/projects/plugins/jetpack/readme.txt index d2e95fe39bc5c..a43dca27e6b46 100644 --- a/projects/plugins/jetpack/readme.txt +++ b/projects/plugins/jetpack/readme.txt @@ -326,19 +326,18 @@ Jetpack Backup can do a full website migration to a new host, migrate theme file == Changelog == -### 14.2-a.1 - 2024-12-09 +### 14.2-a.3 - 2024-12-16 #### Enhancements -- Stats: Allow fetching stats for specific sites when programatically fetching stats using Jetpack's tools. -- Stats: Sunset Legacy Stats experience. -- Stats: Enable sparkline chart in the WP Admin bar. +- Social: Improved Jetpack likes behavior for better theme integration if the post has likes. #### Improved compatibility -- External media: Google Photos Picker: Update UX opening picker right after pressing "change selection" CTA. -- SEO: Ensure support for adding an SEO title and description for custom post types. -- WordPress 6.7 Compatibility: Fix notices caused by translation calls happening too early in the load order. +- Jetpack Testimonials: Ensure feature loads via the Classic Theme Helper package instead of the module. #### Bug fixes -- Google Fonts: Clean up the Google Fonts data if either the Google Fonts module is disabled or Jetpack is disabled. +- Facebook Embeds: Add a white background to embeds to avoid transparent background interfering with readability. +- Form Block: fixed validation of URL input types to allow query strings. +- Import: Correctly setting the WP_IMPORTING constant when doing an import. +- SEO: Ensure that SEO fields are not visible when another SEO plugin is active. -------- diff --git a/projects/js-packages/ai-client/changelog/renovate-wordpress-monorepo b/projects/plugins/mu-wpcom-plugin/changelog/prerelease#6 similarity index 51% rename from projects/js-packages/ai-client/changelog/renovate-wordpress-monorepo rename to projects/plugins/mu-wpcom-plugin/changelog/prerelease#6 index c47cb18e82997..9aa70e3ec1f75 100644 --- a/projects/js-packages/ai-client/changelog/renovate-wordpress-monorepo +++ b/projects/plugins/mu-wpcom-plugin/changelog/prerelease#6 @@ -1,4 +1,5 @@ Significance: patch Type: changed +Comment: Updated composer.lock. + -Updated package dependencies. diff --git a/projects/plugins/mu-wpcom-plugin/composer.lock b/projects/plugins/mu-wpcom-plugin/composer.lock index 0831dabea7207..84180127a014a 100644 --- a/projects/plugins/mu-wpcom-plugin/composer.lock +++ b/projects/plugins/mu-wpcom-plugin/composer.lock @@ -407,7 +407,7 @@ "dist": { "type": "path", "url": "../../packages/classic-theme-helper", - "reference": "313ac02e74dbf44eec1ddbf485af492b66d96a9a" + "reference": "198fd841c5341850e247c46168d77b1bc6a13a34" }, "require": { "automattic/jetpack-assets": "@dev", @@ -425,7 +425,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.7.x-dev" + "dev-trunk": "0.8.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-classic-theme-helper/compare/v${old}...v${new}" diff --git a/projects/js-packages/base-styles/changelog/renovate-wordpress-monorepo b/projects/plugins/wpcomsh/changelog/prerelease#2 similarity index 51% rename from projects/js-packages/base-styles/changelog/renovate-wordpress-monorepo rename to projects/plugins/wpcomsh/changelog/prerelease#2 index c47cb18e82997..9aa70e3ec1f75 100644 --- a/projects/js-packages/base-styles/changelog/renovate-wordpress-monorepo +++ b/projects/plugins/wpcomsh/changelog/prerelease#2 @@ -1,4 +1,5 @@ Significance: patch Type: changed +Comment: Updated composer.lock. + -Updated package dependencies. diff --git a/projects/plugins/wpcomsh/composer.lock b/projects/plugins/wpcomsh/composer.lock index 3a6c4cc371348..5956b66464ed3 100644 --- a/projects/plugins/wpcomsh/composer.lock +++ b/projects/plugins/wpcomsh/composer.lock @@ -472,7 +472,7 @@ "dist": { "type": "path", "url": "../../packages/classic-theme-helper", - "reference": "313ac02e74dbf44eec1ddbf485af492b66d96a9a" + "reference": "198fd841c5341850e247c46168d77b1bc6a13a34" }, "require": { "automattic/jetpack-assets": "@dev", @@ -490,7 +490,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "0.7.x-dev" + "dev-trunk": "0.8.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/jetpack-classic-theme-helper/compare/v${old}...v${new}" From 7f0e83e387b2983eaa74a5a0c0dcc71158c69c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donncha=20=C3=93=20Caoimh?= <5656673+donnchawp@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:47:21 +0000 Subject: [PATCH 07/63] Boost: Add tracks event to clickable events on settings page (#40246) * Add tracks event to clicking of speed score refresh * changelog * Add critical_css_regenerate_clicked Tracks event * Add Tracks events for page cache module * Add tracking to Image CDN adjust quality click * Add tracks event to "Contact Us" button * Add the tracksEventName to the quality settings story too * Update projects/plugins/boost/app/assets/src/js/features/page-cache/meta/meta.tsx Co-authored-by: Peter Petrov * Update projects/plugins/boost/app/assets/src/js/features/image-cdn/quality-settings/quality-settings.tsx Co-authored-by: Peter Petrov * Record boost even when speed score alert shown * Updates to Tracks are general * Record clicks on "Contact Support" in CCCS * Add tracking on back button * add changelog * Revert changes done by prettier * Add Tracks record of Critical CSS link clicks * Track clicks on "advanced recommendations" link * Record clicks on css error links * Record clicks on the defer js link * Change it for ESLint * Fix renamed tracks prop * Record clicks on tip links * Links open themselves, not JS. Remove the window.open * Typo fix to changelog * Call the correct function to track C CSS clicks * Replace with neater code. props @dilirity * Record the scores and direction of change * Update projects/plugins/boost/changelog/update-boost-add-tracks-events Actions take^h^h^h^htook place in the past. Co-authored-by: Adnan Haque <3737780+haqadn@users.noreply.github.com> * Don't track scores * Add tracks event on "see logs" click * Remove this as it's needed now. eslint update? --------- Co-authored-by: Peter Petrov Co-authored-by: Adnan Haque <3737780+haqadn@users.noreply.github.com> --- .../show-stopper-error/show-stopper-error.tsx | 13 +++++- .../features/critical-css/status/status.tsx | 14 ++++++- .../quality-settings/quality-settings.tsx | 1 + .../src/js/features/page-cache/meta/meta.tsx | 13 +++++- .../features/speed-score/pop-out/pop-out.tsx | 8 +++- .../js/features/speed-score/speed-score.tsx | 8 +++- .../features/ui/back-button/back-button.tsx | 5 +++ .../layout/settings-page/support/support.tsx | 2 + .../src/js/layout/settings-page/tips/tips.tsx | 23 +++++++++-- .../app/assets/src/js/pages/index/index.tsx | 40 ++++++++++++++++--- .../changelog/update-boost-add-tracks-events | 4 ++ 11 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 projects/plugins/boost/changelog/update-boost-add-tracks-events diff --git a/projects/plugins/boost/app/assets/src/js/features/critical-css/show-stopper-error/show-stopper-error.tsx b/projects/plugins/boost/app/assets/src/js/features/critical-css/show-stopper-error/show-stopper-error.tsx index 66adda816b59a..5e640257c58f6 100644 --- a/projects/plugins/boost/app/assets/src/js/features/critical-css/show-stopper-error/show-stopper-error.tsx +++ b/projects/plugins/boost/app/assets/src/js/features/critical-css/show-stopper-error/show-stopper-error.tsx @@ -76,7 +76,15 @@ const Description = ( { errorSet }: { errorSet: ErrorSet } ) => { b: , } ) }{ ' ' } { displayUrls.map( ( { href, label }, index ) => ( - + { + recordBoostEvent( 'critical_css_error_link_clicked', {} ); + } } + href={ href } + target="_blank" + rel="noreferrer" + key={ index } + > { label } ) ) } @@ -203,6 +211,9 @@ const OtherErrors = ( { cssState, retry, showRetry, supportLink }: ShowStopperEr href={ supportLink } target="_blank" rel="noreferrer" + onClick={ () => { + recordBoostEvent( 'critical_css_contact_support', {} ); + } } > { __( 'Contact Support', 'jetpack-boost' ) } diff --git a/projects/plugins/boost/app/assets/src/js/features/critical-css/status/status.tsx b/projects/plugins/boost/app/assets/src/js/features/critical-css/status/status.tsx index 2c305c1ab9559..d3b7d596ced7f 100644 --- a/projects/plugins/boost/app/assets/src/js/features/critical-css/status/status.tsx +++ b/projects/plugins/boost/app/assets/src/js/features/critical-css/status/status.tsx @@ -11,6 +11,7 @@ import { getProvidersWithErrors } from '../lib/critical-css-errors'; import ShowStopperError from '../show-stopper-error/show-stopper-error'; import { Button } from '@automattic/jetpack-components'; import styles from './status.module.scss'; +import { recordBoostEvent } from '$lib/utils/analytics'; type StatusTypes = { cssState: CriticalCssState; @@ -38,6 +39,15 @@ const Status: React.FC< StatusTypes > = ( { cssState.providers.filter( provider => provider.status === 'success' ).length || 0; const providersWithErrors = getProvidersWithErrors( cssState ); + const handleClickRegenerate = () => { + recordBoostEvent( 'critical_css_regenerate_clicked', {} ); + regenerateAction.mutate(); + }; + + const handleAdvancedClick = () => { + recordBoostEvent( 'critical_css_advanced_link_clicked', {} ); + }; + // If there has been a fatal error, show it. if ( showFatalError ) { return ( @@ -91,7 +101,7 @@ const Status: React.FC< StatusTypes > = ( { providersWithErrors.length ), { - advanced: , + advanced: , } ) } @@ -104,7 +114,7 @@ const Status: React.FC< StatusTypes > = ( { variant={ highlightRegenerateButton ? 'primary' : 'link' } size="small" weight="regular" - onClick={ () => regenerateAction.mutate() } + onClick={ handleClickRegenerate } icon={ highlightRegenerateButton ? undefined : } disabled={ cssState.status === 'pending' } > diff --git a/projects/plugins/boost/app/assets/src/js/features/image-cdn/quality-settings/quality-settings.tsx b/projects/plugins/boost/app/assets/src/js/features/image-cdn/quality-settings/quality-settings.tsx index f96ddf366229e..8e44f312efcc1 100644 --- a/projects/plugins/boost/app/assets/src/js/features/image-cdn/quality-settings/quality-settings.tsx +++ b/projects/plugins/boost/app/assets/src/js/features/image-cdn/quality-settings/quality-settings.tsx @@ -53,6 +53,7 @@ const QualitySettings = ( { isPremium }: QualitySettingsProps ) => { toggleText={ __( 'Adjust Quality', 'jetpack-boost' ) } header={
} summary={ } + tracksEvent="image_cdn_panel_toggle" >
Adjust image quality per format
diff --git a/projects/plugins/boost/app/assets/src/js/features/page-cache/meta/meta.tsx b/projects/plugins/boost/app/assets/src/js/features/page-cache/meta/meta.tsx index fd5038c8afb6b..d802d49d60066 100644 --- a/projects/plugins/boost/app/assets/src/js/features/page-cache/meta/meta.tsx +++ b/projects/plugins/boost/app/assets/src/js/features/page-cache/meta/meta.tsx @@ -25,9 +25,14 @@ const Meta = () => { const [ clearedCacheMessage, runClearPageCacheAction ] = useClearPageCacheAction(); const clearPageCache = () => { + recordBoostEvent( 'page_cache_clear_clicked', {} ); runClearPageCacheAction.mutate(); }; + const handleSeeLogsClick = () => { + recordBoostEvent( 'page_cache_see_logs_clicked', {} ); + }; + const totalBypassPatterns = bypassPatterns?.length || 0; const getSummary = () => { @@ -121,7 +126,11 @@ const Meta = () => { { __( 'Activate logging to track all your cache events.', 'jetpack-boost' ) } { logging && ( - + { __( 'See Logs', 'jetpack-boost' ) } ) } @@ -193,6 +202,7 @@ const BypassPatterns = ( { }, [ showErrorNotice ] ); function save() { + recordBoostEvent( 'page_cache_exceptions_save_clicked', {} ); setPatterns( inputValue ); } @@ -264,6 +274,7 @@ const BypassPatternsExample = ( { children }: BypassPatternsExampleProps ) => { href="#" className={ styles[ 'example-button' ] } onClick={ e => { + recordBoostEvent( 'page_cache_see_example_clicked', {} ); e.preventDefault(); setShow( ! show ); } } diff --git a/projects/plugins/boost/app/assets/src/js/features/speed-score/pop-out/pop-out.tsx b/projects/plugins/boost/app/assets/src/js/features/speed-score/pop-out/pop-out.tsx index af12ebfe3615a..7b2338d27e4e5 100644 --- a/projects/plugins/boost/app/assets/src/js/features/speed-score/pop-out/pop-out.tsx +++ b/projects/plugins/boost/app/assets/src/js/features/speed-score/pop-out/pop-out.tsx @@ -6,6 +6,7 @@ import { ReactNode, useState } from 'react'; import { Button } from '@wordpress/components'; import { useDismissibleAlertState } from '$features/performance-history/lib/hooks'; import { getRedirectUrl } from '@automattic/jetpack-components'; +import { recordBoostEvent } from '$lib/utils/analytics'; type Props = { scoreChange: number | false; // Speed score shift to show, or false if none. @@ -69,7 +70,6 @@ function PopOut( { scoreChange }: Props ) { * Dismissed means that the user asked to never show us this alert again. */ const [ isDismissed, dismissAlert ] = useDismissibleAlertState( message.id ); - /* * Hide the alert for now. The alert will show up again if the user refreshes the page. */ @@ -77,6 +77,12 @@ function PopOut( { scoreChange }: Props ) { const hideAlert = () => setClose( true ); + if ( hasScoreChanged ) { + recordBoostEvent( 'speed_score_alert_shown', { + score_direction: scoreChange > 0 ? 'up' : 'down', + } ); + } + const animationStyles = useSpring( { from: { right: '-100%', diff --git a/projects/plugins/boost/app/assets/src/js/features/speed-score/speed-score.tsx b/projects/plugins/boost/app/assets/src/js/features/speed-score/speed-score.tsx index 57beb9aa899c1..7010f0af185fb 100644 --- a/projects/plugins/boost/app/assets/src/js/features/speed-score/speed-score.tsx +++ b/projects/plugins/boost/app/assets/src/js/features/speed-score/speed-score.tsx @@ -21,6 +21,7 @@ import { queryClient } from '@automattic/jetpack-react-data-sync-client'; import ErrorBoundary from '$features/error-boundary/error-boundary'; import PopOut from './pop-out/pop-out'; import { useCornerstonePages } from '$features/cornerstone-pages/lib/stores/cornerstone-pages'; +import { recordBoostEvent } from '$lib/utils/analytics'; const SpeedScore = () => { const [ cornerstonePages ] = useCornerstonePages(); @@ -55,6 +56,11 @@ const SpeedScore = () => { } }, [ site.online, status ] ); + const handleClickRefresh = () => { + recordBoostEvent( 'speed_score_refresh_clicked', {} ); + loadScore( true ); + }; + // Ask the API to recompute the score. const refreshScore = useCallback( async () => { if ( site.online ) { @@ -100,7 +106,7 @@ const SpeedScore = () => { size="small" weight="regular" className={ styles[ 'action-button' ] } - onClick={ () => loadScore( true ) } + onClick={ handleClickRefresh } disabled={ status === 'loading' } icon={ } > diff --git a/projects/plugins/boost/app/assets/src/js/features/ui/back-button/back-button.tsx b/projects/plugins/boost/app/assets/src/js/features/ui/back-button/back-button.tsx index 83cb3472a619c..6ca35c669fd8f 100644 --- a/projects/plugins/boost/app/assets/src/js/features/ui/back-button/back-button.tsx +++ b/projects/plugins/boost/app/assets/src/js/features/ui/back-button/back-button.tsx @@ -1,6 +1,7 @@ import { __ } from '@wordpress/i18n'; import LeftArrow from '$svg/left-arrow'; import { useNavigate } from 'react-router-dom'; +import { recordBoostEvent } from '$lib/utils/analytics'; type BackButtonProps = { route?: string; @@ -9,6 +10,10 @@ type BackButtonProps = { const BackButton: React.FC< BackButtonProps > = ( { route = '/' } ) => { const navigate = useNavigate(); const handleBack = () => { + recordBoostEvent( 'back_button_clicked', { + current_page: window.location.href.replace( window.location.origin, '' ), + destination: route, + } ); navigate( route ); }; diff --git a/projects/plugins/boost/app/assets/src/js/layout/settings-page/support/support.tsx b/projects/plugins/boost/app/assets/src/js/layout/settings-page/support/support.tsx index 6f5c73f3afb06..be15bca0b9e01 100644 --- a/projects/plugins/boost/app/assets/src/js/layout/settings-page/support/support.tsx +++ b/projects/plugins/boost/app/assets/src/js/layout/settings-page/support/support.tsx @@ -1,9 +1,11 @@ import { __ } from '@wordpress/i18n'; import { Button } from '@automattic/jetpack-components'; import styles from './support.module.scss'; +import { recordBoostEvent } from '$lib/utils/analytics'; const Support = () => { const openPaidSupport = () => { + recordBoostEvent( 'support_contact_us_clicked', {} ); const supportUrl = 'https://jetpackme.wordpress.com/contact-support/'; window.open( supportUrl, '_blank' ); }; diff --git a/projects/plugins/boost/app/assets/src/js/layout/settings-page/tips/tips.tsx b/projects/plugins/boost/app/assets/src/js/layout/settings-page/tips/tips.tsx index a11acbbc54495..941e531730b71 100644 --- a/projects/plugins/boost/app/assets/src/js/layout/settings-page/tips/tips.tsx +++ b/projects/plugins/boost/app/assets/src/js/layout/settings-page/tips/tips.tsx @@ -2,6 +2,7 @@ import { getRedirectUrl } from '@automattic/jetpack-components'; import { createInterpolateElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import styles from './tips.module.scss'; +import { recordBoostEvent } from '$lib/utils/analytics'; const Tips = () => { const pingdomLink = getRedirectUrl( 'jetpack-boost-pingdom' ); @@ -22,8 +23,15 @@ const Tips = () => { 'jetpack-boost' ), { - // eslint-disable-next-line jsx-a11y/anchor-has-content - link: , + link: ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + recordBoostEvent( 'pingdom_link_clicked', {} ) } + href={ pingdomLink } + target="_blank" + rel="noopener noreferrer" + /> + ), } ) }
@@ -37,8 +45,15 @@ const Tips = () => { 'jetpack-boost' ), { - // eslint-disable-next-line jsx-a11y/anchor-has-content - link: , + link: ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + recordBoostEvent( 'why_speed_link_clicked', {} ) } + href={ whySpeedLink } + target="_blank" + rel="noopener noreferrer" + /> + ), } ) } diff --git a/projects/plugins/boost/app/assets/src/js/pages/index/index.tsx b/projects/plugins/boost/app/assets/src/js/pages/index/index.tsx index 3bce8566cc662..4373e1baeac18 100644 --- a/projects/plugins/boost/app/assets/src/js/pages/index/index.tsx +++ b/projects/plugins/boost/app/assets/src/js/pages/index/index.tsx @@ -17,6 +17,7 @@ import PremiumTooltip from '$features/premium-tooltip/premium-tooltip'; import Upgraded from '$features/ui/upgraded/upgraded'; import PageCacheModule from '$features/page-cache/page-cache'; import Pill from '$features/ui/pill/pill'; +import { recordBoostEvent } from '$lib/utils/analytics'; const Index = () => { const criticalCssLink = getRedirectUrl( 'jetpack-boost-critical-css' ); @@ -36,6 +37,10 @@ const Index = () => { const hasPremiumCdnFeatures = premiumFeatures.includes( 'image-cdn-liar' ) && premiumFeatures.includes( 'image-cdn-quality' ); + const handleCriticalCssLink = () => { + recordBoostEvent( 'critical_css_link_clicked', {} ); + }; + return (
@@ -52,8 +57,16 @@ const Index = () => { 'jetpack-boost' ), { - // eslint-disable-next-line jsx-a11y/anchor-has-content - link: , + link: ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + + ), } ) }

@@ -102,8 +115,16 @@ const Index = () => { 'jetpack-boost' ), { - // eslint-disable-next-line jsx-a11y/anchor-has-content - link:
, + link: ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + + ), } ) }

@@ -135,8 +156,15 @@ const Index = () => { 'jetpack-boost' ), { - // eslint-disable-next-line jsx-a11y/anchor-has-content - link:
, + link: ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + recordBoostEvent( 'defer_js_link_clicked', {} ) } + href={ deferJsLink } + target="_blank" + rel="noopener noreferrer" + /> + ), } ) }

diff --git a/projects/plugins/boost/changelog/update-boost-add-tracks-events b/projects/plugins/boost/changelog/update-boost-add-tracks-events new file mode 100644 index 0000000000000..7d24b2973f91e --- /dev/null +++ b/projects/plugins/boost/changelog/update-boost-add-tracks-events @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +General: Added tracks events to clickable elements on the settings page. From 09c215fe99ae3ec3d551e0df2d0acbe83629e8cc Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Mon, 16 Dec 2024 14:52:30 -0500 Subject: [PATCH 08/63] actions: Document fine-grained permissions (#40633) We had documented the OAuth-style scopes where it was likely to matter, but never mentioned the newer "permissions" for GitHub Apps and fine-grained access tokens. --- projects/github-actions/pr-is-up-to-date/README.md | 8 ++++++++ .../update-github-actions-docs-with-permissions | 4 ++++ projects/github-actions/required-review/README.md | 13 ++++++++++--- .../update-github-actions-docs-with-permissions | 4 ++++ .../github-actions/test-results-to-slack/README.md | 8 ++++++++ .../update-github-actions-docs-with-permissions | 4 ++++ 6 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 projects/github-actions/pr-is-up-to-date/changelog/update-github-actions-docs-with-permissions create mode 100644 projects/github-actions/required-review/changelog/update-github-actions-docs-with-permissions create mode 100644 projects/github-actions/test-results-to-slack/changelog/update-github-actions-docs-with-permissions diff --git a/projects/github-actions/pr-is-up-to-date/README.md b/projects/github-actions/pr-is-up-to-date/README.md index d836b4914f75c..1bb52eed66124 100644 --- a/projects/github-actions/pr-is-up-to-date/README.md +++ b/projects/github-actions/pr-is-up-to-date/README.md @@ -41,6 +41,14 @@ jobs: This action is intended to be triggered by `pull_request_target` or `pull_request` targeting the specified branch, and by a `push` to the specified tags. It will not work for pushes to anything else. +### Permissions required + +This action needs access to read pull request data and create status checks. + +For OAuth apps and classic access tokens, that's `repo:status`. + +For GitHub Apps and fine-grained access tokens, that's read and write for repository "Commit statuses" (`statuses`) and read-only for "Pull requests" (`pull-requests`). + ### On pull request ```yaml diff --git a/projects/github-actions/pr-is-up-to-date/changelog/update-github-actions-docs-with-permissions b/projects/github-actions/pr-is-up-to-date/changelog/update-github-actions-docs-with-permissions new file mode 100644 index 0000000000000..41e21f7c7a781 --- /dev/null +++ b/projects/github-actions/pr-is-up-to-date/changelog/update-github-actions-docs-with-permissions @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Update docs with permissions for GitHub Apps and fine-grained access tokens. diff --git a/projects/github-actions/required-review/README.md b/projects/github-actions/required-review/README.md index 1eb49ded89baa..8a9a0699ac5ad 100644 --- a/projects/github-actions/required-review/README.md +++ b/projects/github-actions/required-review/README.md @@ -70,12 +70,19 @@ This action is intended to be triggered by the `pull_request_review` event. request-reviews: true # GitHub Access Token. The user associated with this token will show up - # as the "creator" of the status check, and must have access to read - # pull request data, create status checks (`repo:status`), and to read - # your organization's teams (`read:org`). + # as the "creator" of the status check, and must have the permissions + # documented below. token: ${{ secrets.SOME_TOKEN }} ``` +### Permissions required + +This action needs access to read pull request data, request reviewers, create status checks, and to read your organization's teams. + +For OAuth apps and classic access tokens, that's `repo:status` and `read:org`. + +For GitHub Apps and fine-grained access tokens, that's read and write for repository "Commit statuses" (`statuses`) and "Pull requests" (`pull-requests`), and read-only for organization "Members". + ## Requirements Format The requirements consist of an array of requirement objects. A requirement object has the following keys: diff --git a/projects/github-actions/required-review/changelog/update-github-actions-docs-with-permissions b/projects/github-actions/required-review/changelog/update-github-actions-docs-with-permissions new file mode 100644 index 0000000000000..41e21f7c7a781 --- /dev/null +++ b/projects/github-actions/required-review/changelog/update-github-actions-docs-with-permissions @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Update docs with permissions for GitHub Apps and fine-grained access tokens. diff --git a/projects/github-actions/test-results-to-slack/README.md b/projects/github-actions/test-results-to-slack/README.md index fb845da77fac9..6837b229475a4 100644 --- a/projects/github-actions/test-results-to-slack/README.md +++ b/projects/github-actions/test-results-to-slack/README.md @@ -86,6 +86,14 @@ The action relies on the following parameters. - (Optional) `playwright_report_path` is the path to the JSON report, output from Playwright test runner JSON reporter. See [Playwright's docs](https://playwright.dev/docs/test-reporters#json-reporter) for details on how to generate this file. If specified, it will be parsed and failures details will be included in the message. You can use the glob pattern to specify multiple files. For example: `playwright_report_path: 'artifacts/**/report.json'`. - (Optional) `playwright_output_dir` is the path to the Playwright's configured output directory, where results and attachments are saved. It is needed when the artefacts are downloaded from a previous job, and the absolute paths to attachments found in the JSON report are not valid anymore. This path will be used to convert the paths to those attachments. You can use the glob pattern. For example: `playwright_output_dir: 'artifacts/**/results'` +### GitHub permissions required + +This action needs access to list jobs for workflow runs. + +For OAuth apps and classic access tokens, no special scopes are needed. + +For GitHub Apps and fine-grained access tokens, that's read-only for repository "Actions" (`actions`). + ### Slack token You will need to [create a Slack bot for your workspace](https://slack.com/intl/en-hu/help/articles/115005265703-Create-a-bot-for-your-workspace) for the action to use. The bot will need the following scopes: diff --git a/projects/github-actions/test-results-to-slack/changelog/update-github-actions-docs-with-permissions b/projects/github-actions/test-results-to-slack/changelog/update-github-actions-docs-with-permissions new file mode 100644 index 0000000000000..41e21f7c7a781 --- /dev/null +++ b/projects/github-actions/test-results-to-slack/changelog/update-github-actions-docs-with-permissions @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Update docs with permissions for GitHub Apps and fine-grained access tokens. From 5e270fb3d34bb3f35e6304392cbdf5d95681b4c1 Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:08:15 +1300 Subject: [PATCH 09/63] Charts: Adding pie charts (#40581) * Charts - Adding basic pie chart * Charts - Updating charts and adding a tooltip hook * Charts - Adding doughts to pies * Charts - Fixing dependency * changelog * Charts - Adding chart TS interface * Charts - Adding shared chart TS interface * Charts - Cleanup * Charts - Cleanup 2 * Charts - updating tooltip prop and simplifying event handlers --- pnpm-lock.yaml | 3 + .../charts/changelog/add-charts-pie-chart | 4 + projects/js-packages/charts/package.json | 1 + .../src/components/bar-chart/bar-chart.tsx | 70 ++-- .../bar-chart/stories/index.stories.tsx | 16 +- .../bar-chart/stories/sample-data.ts | 311 ++++++++++++++++++ .../src/components/line-chart/line-chart.tsx | 25 +- .../line-chart/stories/index.stories.tsx | 10 +- .../line-chart/stories/sample-data.ts | 173 ++++++++++ .../charts/src/components/pie-chart/index.tsx | 1 + .../src/components/pie-chart/pie-chart.tsx | 113 +++++++ .../pie-chart/stories/index.stories.tsx | 94 ++++++ .../pie-semi-circle-chart.tsx | 30 +- .../stories/index.stories.tsx | 2 +- .../charts/src/components/shared/types.d.ts | 33 ++ .../src/hooks/use-chart-mouse-handler.ts | 90 +++++ projects/js-packages/charts/src/index.ts | 13 +- .../charts/src/providers/theme/themes.ts | 9 +- 18 files changed, 880 insertions(+), 118 deletions(-) create mode 100644 projects/js-packages/charts/changelog/add-charts-pie-chart create mode 100644 projects/js-packages/charts/src/components/bar-chart/stories/sample-data.ts create mode 100644 projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts create mode 100644 projects/js-packages/charts/src/components/pie-chart/index.tsx create mode 100644 projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx create mode 100644 projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx create mode 100644 projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4af216da4b55c..d2a2a658b2b2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -304,6 +304,9 @@ importers: '@visx/group': specifier: ^3.12.0 version: 3.12.0(react@18.3.1) + '@visx/responsive': + specifier: 3.12.0 + version: 3.12.0(react@18.3.1) '@visx/scale': specifier: ^3.12.0 version: 3.12.0 diff --git a/projects/js-packages/charts/changelog/add-charts-pie-chart b/projects/js-packages/charts/changelog/add-charts-pie-chart new file mode 100644 index 0000000000000..81e8c5063e8ae --- /dev/null +++ b/projects/js-packages/charts/changelog/add-charts-pie-chart @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Adding new chart type - pie chart. diff --git a/projects/js-packages/charts/package.json b/projects/js-packages/charts/package.json index 41e7c7118aaa1..e51243ee01bc2 100644 --- a/projects/js-packages/charts/package.json +++ b/projects/js-packages/charts/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@react-spring/web": "9.7.3", + "@visx/responsive": "3.12.0", "@visx/axis": "^3.12.0", "@visx/group": "^3.12.0", "@visx/scale": "^3.12.0", diff --git a/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx b/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx index ee4fe451fe464..984fddd5ac8f4 100644 --- a/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx +++ b/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx @@ -5,46 +5,25 @@ import { scaleBand, scaleLinear } from '@visx/scale'; import { Bar } from '@visx/shape'; import { useTooltip } from '@visx/tooltip'; import clsx from 'clsx'; -import { FC, useCallback } from 'react'; +import { FC, useCallback, type MouseEvent } from 'react'; import { useChartTheme } from '../../providers/theme'; import { BaseTooltip } from '../tooltip'; import styles from './bar-chart.module.scss'; -import type { DataPoint } from '../shared/types'; +import type { BaseChartProps, DataPoint } from '../shared/types'; -type BarChartProps = { +interface BarChartProps extends BaseChartProps { /** * Array of data points to display in the chart */ data: DataPoint[]; - /** - * Width of the chart in pixels - */ - width: number; - /** - * Height of the chart in pixels - */ - height: number; - /** - * Chart margins - */ - margin?: { - top?: number; - right?: number; - bottom?: number; - left?: number; - }; - /** - * Whether to show tooltips on hover - */ - showTooltips?: boolean; -}; +} const BarChart: FC< BarChartProps > = ( { data, width, height, margin = { top: 20, right: 20, bottom: 40, left: 40 }, - showTooltips = false, + withTooltips = false, } ) => { const theme = useChartTheme(); const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = @@ -66,7 +45,7 @@ const BarChart: FC< BarChartProps > = ( { } ); const handleMouseMove = useCallback( - ( event: React.MouseEvent, datum: DataPoint ) => { + ( event: MouseEvent< SVGRectElement >, datum: DataPoint ) => { const coords = localPoint( event ); if ( ! coords ) return; @@ -83,35 +62,32 @@ const BarChart: FC< BarChartProps > = ( { hideTooltip(); }, [ hideTooltip ] ); - const handleBarMouseMove = useCallback( - ( d: DataPoint ) => ( event: React.MouseEvent< SVGRectElement > ) => { - handleMouseMove( event, d ); - }, - [ handleMouseMove ] - ); - return (
- { data.map( d => ( - - ) ) } + { data.map( d => { + const handleBarMouseMove = event => handleMouseMove( event, d ); + + return ( + + ); + } ) } - { showTooltips && tooltipOpen && tooltipData && ( + { withTooltips && tooltipOpen && tooltipData && ( {} // eslint-disable-next-line @typescript-eslint/no-explicit-any const renderTooltip: any = ( { tooltipData } ) => { diff --git a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx index 40a11b9c67364..d5c1a7fb6e2fb 100644 --- a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx @@ -1,13 +1,7 @@ import { LineChart } from '../index'; +import sampleData from './sample-data'; import type { Meta } from '@storybook/react'; -const data = [ - { date: new Date( '2023-01-01' ), value: 10 }, - { date: new Date( '2023-02-01' ), value: 20 }, - { date: new Date( '2023-03-01' ), value: 15 }, - { date: new Date( '2023-04-01' ), value: 25 }, -]; - export default { title: 'JS Packages/Charts/Types/Line Chart', component: LineChart, @@ -30,5 +24,5 @@ Default.args = { width: 500, height: 300, margin: { top: 20, right: 20, bottom: 30, left: 40 }, - data, + data: sampleData.mars, }; diff --git a/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts b/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts new file mode 100644 index 0000000000000..53d7f8d97d72e --- /dev/null +++ b/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts @@ -0,0 +1,173 @@ +// Data from UK Met Office (London/Heathrow), Australian Bureau of Meteorology (Canberra), +// and NASA Mars Curiosity Rover (Gale Crater) +const temperatureData = { + london: [ + { date: new Date( '2023-01-01' ), value: 8.2 }, + { date: new Date( '2023-01-08' ), value: 7.9 }, + { date: new Date( '2023-01-15' ), value: 5.1 }, + { date: new Date( '2023-01-22' ), value: 4.8 }, + { date: new Date( '2023-01-29' ), value: 6.3 }, + { date: new Date( '2023-02-05' ), value: 7.2 }, + { date: new Date( '2023-02-12' ), value: 9.4 }, + { date: new Date( '2023-02-19' ), value: 8.7 }, + { date: new Date( '2023-02-26' ), value: 7.1 }, + { date: new Date( '2023-03-05' ), value: 8.3 }, + { date: new Date( '2023-03-12' ), value: 9.5 }, + { date: new Date( '2023-03-19' ), value: 11.2 }, + { date: new Date( '2023-03-26' ), value: 12.8 }, + { date: new Date( '2023-04-02' ), value: 13.4 }, + { date: new Date( '2023-04-09' ), value: 14.1 }, + { date: new Date( '2023-04-16' ), value: 15.3 }, + { date: new Date( '2023-04-23' ), value: 14.8 }, + { date: new Date( '2023-04-30' ), value: 15.7 }, + { date: new Date( '2023-05-07' ), value: 16.9 }, + { date: new Date( '2023-05-14' ), value: 17.2 }, + { date: new Date( '2023-05-21' ), value: 18.4 }, + { date: new Date( '2023-05-28' ), value: 19.1 }, + { date: new Date( '2023-06-04' ), value: 20.3 }, + { date: new Date( '2023-06-11' ), value: 21.5 }, + { date: new Date( '2023-06-18' ), value: 22.8 }, + { date: new Date( '2023-06-25' ), value: 21.9 }, + { date: new Date( '2023-07-02' ), value: 23.1 }, + { date: new Date( '2023-07-09' ), value: 22.7 }, + { date: new Date( '2023-07-16' ), value: 24.2 }, + { date: new Date( '2023-07-23' ), value: 23.8 }, + { date: new Date( '2023-07-30' ), value: 22.9 }, + { date: new Date( '2023-08-06' ), value: 23.4 }, + { date: new Date( '2023-08-13' ), value: 22.8 }, + { date: new Date( '2023-08-20' ), value: 21.9 }, + { date: new Date( '2023-08-27' ), value: 20.7 }, + { date: new Date( '2023-09-03' ), value: 19.8 }, + { date: new Date( '2023-09-10' ), value: 18.9 }, + { date: new Date( '2023-09-17' ), value: 17.6 }, + { date: new Date( '2023-09-24' ), value: 16.8 }, + { date: new Date( '2023-10-01' ), value: 15.9 }, + { date: new Date( '2023-10-08' ), value: 14.7 }, + { date: new Date( '2023-10-15' ), value: 13.8 }, + { date: new Date( '2023-10-22' ), value: 12.9 }, + { date: new Date( '2023-10-29' ), value: 11.7 }, + { date: new Date( '2023-11-05' ), value: 10.8 }, + { date: new Date( '2023-11-12' ), value: 9.9 }, + { date: new Date( '2023-11-19' ), value: 8.7 }, + { date: new Date( '2023-11-26' ), value: 7.8 }, + { date: new Date( '2023-12-03' ), value: 6.9 }, + { date: new Date( '2023-12-10' ), value: 5.8 }, + { date: new Date( '2023-12-17' ), value: 4.9 }, + { date: new Date( '2023-12-24' ), value: 5.7 }, + { date: new Date( '2023-12-31' ), value: 6.2 }, + ], + + canberra: [ + { date: new Date( '2023-01-01' ), value: 28.5 }, + { date: new Date( '2023-01-08' ), value: 29.2 }, + { date: new Date( '2023-01-15' ), value: 30.1 }, + { date: new Date( '2023-01-22' ), value: 29.8 }, + { date: new Date( '2023-01-29' ), value: 28.9 }, + { date: new Date( '2023-02-05' ), value: 27.8 }, + { date: new Date( '2023-02-12' ), value: 26.9 }, + { date: new Date( '2023-02-19' ), value: 25.7 }, + { date: new Date( '2023-02-26' ), value: 24.8 }, + { date: new Date( '2023-03-05' ), value: 23.9 }, + { date: new Date( '2023-03-12' ), value: 22.8 }, + { date: new Date( '2023-03-19' ), value: 21.7 }, + { date: new Date( '2023-03-26' ), value: 20.8 }, + { date: new Date( '2023-04-02' ), value: 19.6 }, + { date: new Date( '2023-04-09' ), value: 18.4 }, + { date: new Date( '2023-04-16' ), value: 17.2 }, + { date: new Date( '2023-04-23' ), value: 16.1 }, + { date: new Date( '2023-04-30' ), value: 15.3 }, + { date: new Date( '2023-05-07' ), value: 14.2 }, + { date: new Date( '2023-05-14' ), value: 13.1 }, + { date: new Date( '2023-05-21' ), value: 12.3 }, + { date: new Date( '2023-05-28' ), value: 11.4 }, + { date: new Date( '2023-06-04' ), value: 10.2 }, + { date: new Date( '2023-06-11' ), value: 9.1 }, + { date: new Date( '2023-06-18' ), value: 8.3 }, + { date: new Date( '2023-06-25' ), value: 7.8 }, + { date: new Date( '2023-07-02' ), value: 7.1 }, + { date: new Date( '2023-07-09' ), value: 6.9 }, + { date: new Date( '2023-07-16' ), value: 7.2 }, + { date: new Date( '2023-07-23' ), value: 8.1 }, + { date: new Date( '2023-07-30' ), value: 9.3 }, + { date: new Date( '2023-08-06' ), value: 10.4 }, + { date: new Date( '2023-08-13' ), value: 11.6 }, + { date: new Date( '2023-08-20' ), value: 12.8 }, + { date: new Date( '2023-08-27' ), value: 13.9 }, + { date: new Date( '2023-09-03' ), value: 15.2 }, + { date: new Date( '2023-09-10' ), value: 16.4 }, + { date: new Date( '2023-09-17' ), value: 17.6 }, + { date: new Date( '2023-09-24' ), value: 18.9 }, + { date: new Date( '2023-10-01' ), value: 20.1 }, + { date: new Date( '2023-10-08' ), value: 21.3 }, + { date: new Date( '2023-10-15' ), value: 22.5 }, + { date: new Date( '2023-10-22' ), value: 23.7 }, + { date: new Date( '2023-10-29' ), value: 24.8 }, + { date: new Date( '2023-11-05' ), value: 25.9 }, + { date: new Date( '2023-11-12' ), value: 26.7 }, + { date: new Date( '2023-11-19' ), value: 27.8 }, + { date: new Date( '2023-11-26' ), value: 28.6 }, + { date: new Date( '2023-12-03' ), value: 29.4 }, + { date: new Date( '2023-12-10' ), value: 30.2 }, + { date: new Date( '2023-12-17' ), value: 29.8 }, + { date: new Date( '2023-12-24' ), value: 28.9 }, + { date: new Date( '2023-12-31' ), value: 29.3 }, + ], + + mars: [ + { date: new Date( '2023-01-01' ), value: -63 }, + { date: new Date( '2023-01-08' ), value: -64 }, + { date: new Date( '2023-01-15' ), value: -65 }, + { date: new Date( '2023-01-22' ), value: -63 }, + { date: new Date( '2023-01-29' ), value: -62 }, + { date: new Date( '2023-02-05' ), value: -60 }, + { date: new Date( '2023-02-12' ), value: -58 }, + { date: new Date( '2023-02-19' ), value: -55 }, + { date: new Date( '2023-02-26' ), value: -52 }, + { date: new Date( '2023-03-05' ), value: -48 }, + { date: new Date( '2023-03-12' ), value: -45 }, + { date: new Date( '2023-03-19' ), value: -42 }, + { date: new Date( '2023-03-26' ), value: -38 }, + { date: new Date( '2023-04-02' ), value: -35 }, + { date: new Date( '2023-04-09' ), value: -32 }, + { date: new Date( '2023-04-16' ), value: -28 }, + { date: new Date( '2023-04-23' ), value: -25 }, + { date: new Date( '2023-04-30' ), value: -22 }, + { date: new Date( '2023-05-07' ), value: -18 }, + { date: new Date( '2023-05-14' ), value: -15 }, + { date: new Date( '2023-05-21' ), value: -12 }, + { date: new Date( '2023-05-28' ), value: -8 }, + { date: new Date( '2023-06-04' ), value: -5 }, + { date: new Date( '2023-06-11' ), value: -2 }, + { date: new Date( '2023-06-18' ), value: 0 }, + { date: new Date( '2023-06-25' ), value: 2 }, + { date: new Date( '2023-07-02' ), value: 5 }, + { date: new Date( '2023-07-09' ), value: 8 }, + { date: new Date( '2023-07-16' ), value: 10 }, + { date: new Date( '2023-07-23' ), value: 12 }, + { date: new Date( '2023-07-30' ), value: 15 }, + { date: new Date( '2023-08-06' ), value: 17 }, + { date: new Date( '2023-08-13' ), value: 20 }, + { date: new Date( '2023-08-20' ), value: 22 }, + { date: new Date( '2023-08-27' ), value: 20 }, + { date: new Date( '2023-09-03' ), value: 18 }, + { date: new Date( '2023-09-10' ), value: 15 }, + { date: new Date( '2023-09-17' ), value: 12 }, + { date: new Date( '2023-09-24' ), value: 8 }, + { date: new Date( '2023-10-01' ), value: 5 }, + { date: new Date( '2023-10-08' ), value: 2 }, + { date: new Date( '2023-10-15' ), value: -2 }, + { date: new Date( '2023-10-22' ), value: -5 }, + { date: new Date( '2023-10-29' ), value: -8 }, + { date: new Date( '2023-11-05' ), value: -12 }, + { date: new Date( '2023-11-12' ), value: -15 }, + { date: new Date( '2023-11-19' ), value: -18 }, + { date: new Date( '2023-11-26' ), value: -22 }, + { date: new Date( '2023-12-03' ), value: -25 }, + { date: new Date( '2023-12-10' ), value: -28 }, + { date: new Date( '2023-12-17' ), value: -32 }, + { date: new Date( '2023-12-24' ), value: -35 }, + { date: new Date( '2023-12-31' ), value: -38 }, + ], +}; + +export default temperatureData; diff --git a/projects/js-packages/charts/src/components/pie-chart/index.tsx b/projects/js-packages/charts/src/components/pie-chart/index.tsx new file mode 100644 index 0000000000000..c5b0025459ea3 --- /dev/null +++ b/projects/js-packages/charts/src/components/pie-chart/index.tsx @@ -0,0 +1 @@ +export { default as PieChart } from './pie-chart'; diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx new file mode 100644 index 0000000000000..bffe55ea4de25 --- /dev/null +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx @@ -0,0 +1,113 @@ +import { Group } from '@visx/group'; +import { Pie } from '@visx/shape'; +import { SVGProps } from 'react'; +import useChartMouseHandler from '../../hooks/use-chart-mouse-handler'; +import { useChartTheme, defaultTheme } from '../../providers/theme'; +import { Tooltip } from '../tooltip'; +import type { BaseChartProps, DataPoint } from '../shared/types'; + +// TODO: add animation + +interface PieChartProps extends BaseChartProps< DataPoint[] > { + /** + * Inner radius in pixels. If > 0, creates a donut chart. Defaults to 0. + */ + innerRadius?: number; +} + +/** + * Renders a pie or donut chart using the provided data. + * + * @param {PieChartProps} props - Component props + * @return {JSX.Element} The rendered chart component + */ +const PieChart = ( { + data, + width, + height, + withTooltips = false, + innerRadius = 0, +}: PieChartProps ) => { + const providerTheme = useChartTheme(); + const { onMouseMove, onMouseLeave, tooltipOpen, tooltipData, tooltipLeft, tooltipTop } = + useChartMouseHandler( { + withTooltips, + } ); + + // Calculate radius based on width/height + const radius = Math.min( width, height ) / 2; + const centerX = width / 2; + const centerY = height / 2; + + const accessors = { + value: d => d.value, + // Use the color property from the data object as a last resort. The theme provides colours by default. + fill: d => d.color || providerTheme.colors[ d.index ], + }; + + return ( +
+ + + + { pie => { + return pie.arcs.map( ( arc, index ) => { + const [ centroidX, centroidY ] = pie.path.centroid( arc ); + const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.25; + const handleMouseMove = event => onMouseMove( event, arc.data ); + + const pathProps: SVGProps< SVGPathElement > = { + d: pie.path( arc ) || '', + fill: accessors.fill( arc ), + }; + + if ( withTooltips ) { + pathProps.onMouseMove = handleMouseMove; + pathProps.onMouseLeave = onMouseLeave; + } + + return ( + + + { hasSpaceForLabel && ( + + { arc.data.label } + + ) } + + ); + } ); + } } + + + + { withTooltips && tooltipOpen && tooltipData && ( + + ) } +
+ ); +}; + +export default PieChart; diff --git a/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx new file mode 100644 index 0000000000000..ddad97895531d --- /dev/null +++ b/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx @@ -0,0 +1,94 @@ +import { ThemeProvider, jetpackTheme, wooTheme } from '../../../providers/theme'; +import { PieChart } from '../index'; +import type { Meta, StoryObj } from '@storybook/react'; + +const data = [ + { label: 'A', value: 30 }, + { label: 'B', value: 20 }, + { label: 'C', value: 15 }, + { label: 'D', value: 35 }, +]; + +type StoryType = StoryObj< typeof PieChart >; + +export default { + title: 'JS Packages/Charts/Types/Pie Chart', + component: PieChart, + parameters: { + layout: 'centered', + }, + argTypes: { + theme: { + control: 'select', + options: { + default: undefined, + jetpack: jetpackTheme, + woo: wooTheme, + }, + defaultValue: undefined, + }, + }, + decorators: [ + ( Story, { args } ) => ( + +
+ +
+
+ ), + ], +} satisfies Meta< typeof PieChart >; + +export const Default: StoryType = { + args: { + width: 400, + height: 400, + withTooltips: false, + data, + theme: 'default', + innerRadius: 0, + }, +}; + +export const Doughnut: StoryType = { + args: { + ...Default.args, + innerRadius: 80, + }, + parameters: { + docs: { + description: { + story: 'Doughnut chart variant with inner radius of 80px.', + }, + }, + }, +}; + +export const WithTooltips: StoryType = { + args: { + ...Default.args, + withTooltips: true, + }, + parameters: { + docs: { + description: { + story: 'Pie chart with interactive tooltips that appear on hover.', + }, + }, + }, +}; + +export const WithTooltipsDoughnut: StoryType = { + args: { + ...Default.args, + withTooltips: true, + innerRadius: 100, + }, + parameters: { + docs: { + description: { + story: 'Doughnut chart with interactive tooltips that appear on hover.', + }, + }, + }, +}; diff --git a/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx b/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx index 988e15412b003..4eb3b27ed3263 100644 --- a/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx @@ -8,23 +8,9 @@ import { FC, useCallback } from 'react'; import { useChartTheme } from '../../providers/theme/theme-provider'; import { BaseTooltip } from '../tooltip'; import styles from './pie-semi-circle-chart.module.scss'; -import type { DataPointPercentage } from '../shared/types'; +import type { BaseChartProps, DataPointPercentage } from '../shared/types'; -type ArcData = PieArcDatum< DataPointPercentage >; - -interface PieSemiCircleChartProps { - /** - * Array of data points to display in the chart - */ - data: DataPointPercentage[]; - /** - * Width of the chart in pixels - */ - width: number; - /** - * Height of the chart in pixels - */ - height: number; +interface PieSemiCircleChartProps extends BaseChartProps< DataPointPercentage[] > { /** * Label text to display above the chart */ @@ -33,19 +19,17 @@ interface PieSemiCircleChartProps { * Note text to display below the label */ note: string; - /** - * Whether to show tooltips - */ - showTooltips?: boolean; } +type ArcData = PieArcDatum< DataPointPercentage >; + const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( { data, width, height, label, note, - showTooltips = false, + withTooltips = false, } ) => { const providerTheme = useChartTheme(); const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = @@ -106,7 +90,7 @@ const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( { data={ dataWithIndex } pieValue={ accessors.value } outerRadius={ width / 2 } // half of the diameter (width) - innerRadius={ ( width / 2 ) * 0.6 } // 70% of the radius + innerRadius={ ( width / 2 ) * 0.6 } // 60% of the radius cornerRadius={ 3 } padAngle={ 0.03 } startAngle={ -Math.PI / 2 } @@ -147,7 +131,7 @@ const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( { - { showTooltips && tooltipOpen && tooltipData && ( + { withTooltips && tooltipOpen && tooltipData && ( = { + /** + * Array of data points to display in the chart + */ + data: T extends DataPoint | DataPointDate ? T[] : T; + /** + * Width of the chart in pixels + */ + width: number; + /** + * Height of the chart in pixels + */ + height: number; + /** + * Chart margins + */ + margin?: { + top: number; + right: number; + bottom: number; + left: number; + }; + /** + * Whether to show tooltips on hover. False by default. + */ + withTooltips?: boolean; +}; diff --git a/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts b/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts new file mode 100644 index 0000000000000..8a1739a90e4ec --- /dev/null +++ b/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts @@ -0,0 +1,90 @@ +import { localPoint } from '@visx/event'; +import { useTooltip } from '@visx/tooltip'; +import { useCallback, type MouseEvent } from 'react'; +import type { DataPoint } from '../components/shared/types'; + +type UseChartMouseHandlerProps = { + /** + * Whether tooltips are enabled + */ + withTooltips: boolean; +}; + +type UseChartMouseHandlerReturn = { + /** + * Handler for mouse move events + */ + onMouseMove: ( event: React.MouseEvent< SVGElement >, data: DataPoint ) => void; + /** + * Handler for mouse leave events + */ + onMouseLeave: () => void; + /** + * Whether the tooltip is currently open + */ + tooltipOpen: boolean; + /** + * The current tooltip data + */ + tooltipData: DataPoint | null; + /** + * The current tooltip left position + */ + tooltipLeft: number | undefined; + /** + * The current tooltip top position + */ + tooltipTop: number | undefined; +}; + +/** + * Hook to handle mouse interactions for chart components + * + * @param {UseChartMouseHandlerProps} props - Hook configuration + * @return {UseChartMouseHandlerReturn} Object containing handlers and tooltip state + */ +const useChartMouseHandler = ( { + withTooltips, +}: UseChartMouseHandlerProps ): UseChartMouseHandlerReturn => { + const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = + useTooltip< DataPoint >(); + + // TODO: either debounce/throttle or use useTooltipInPortal with built-in debounce + const onMouseMove = useCallback( + ( event: MouseEvent< SVGElement >, data: DataPoint ) => { + if ( ! withTooltips ) { + return; + } + + const coords = localPoint( event ); + if ( ! coords ) { + return; + } + + showTooltip( { + tooltipData: data, + tooltipLeft: coords.x, + tooltipTop: coords.y - 10, + } ); + }, + [ withTooltips, showTooltip ] + ); + + const onMouseLeave = useCallback( () => { + if ( ! withTooltips ) { + return; + } + hideTooltip(); + }, [ withTooltips, hideTooltip ] ); + + return { + onMouseMove, + onMouseLeave, + tooltipOpen, + tooltipData, + tooltipLeft, + tooltipTop, + }; +}; + +export default useChartMouseHandler; diff --git a/projects/js-packages/charts/src/index.ts b/projects/js-packages/charts/src/index.ts index 8dc8f3221948a..b52a51461252c 100644 --- a/projects/js-packages/charts/src/index.ts +++ b/projects/js-packages/charts/src/index.ts @@ -1,6 +1,17 @@ +// Charts export { default as BarChart } from './components/bar-chart'; export { LineChart } from './components/line-chart'; +export { PieChart } from './components/pie-chart'; export { PieSemiCircleChart } from './components/pie-semi-circle-chart'; -export type * from './components/shared/types'; + +// Chart components export { BaseTooltip } from './components/tooltip'; + +// Providers +export { ThemeProvider } from './providers/theme'; + +// Hooks + +// Types +export type * from './components/shared/types'; export type { BaseTooltipProps } from './components/tooltip'; diff --git a/projects/js-packages/charts/src/providers/theme/themes.ts b/projects/js-packages/charts/src/providers/theme/themes.ts index b41d14bd845a1..58bcf3c3fcb31 100644 --- a/projects/js-packages/charts/src/providers/theme/themes.ts +++ b/projects/js-packages/charts/src/providers/theme/themes.ts @@ -4,7 +4,8 @@ import type { ChartTheme } from '../../components/shared/types'; * Default theme configuration */ const defaultTheme: ChartTheme = { - backgroundColor: '#FFFFFF', + backgroundColor: '#FFFFFF', // chart background color + labelBackgroundColor: '#FFFFFF', // label background color colors: [ '#98C8DF', '#006DAB', '#A6DC80', '#1F9828', '#FF8C8F' ], gridStyles: { stroke: '#787C82', @@ -19,7 +20,8 @@ const defaultTheme: ChartTheme = { * Jetpack theme configuration */ const jetpackTheme: ChartTheme = { - backgroundColor: '#FFFFFF', + backgroundColor: '#FFFFFF', // chart background color + labelBackgroundColor: '#FFFFFF', // label background color colors: [ '#98C8DF', '#006DAB', '#A6DC80', '#1F9828', '#FF8C8F' ], gridStyles: { stroke: '#787C82', @@ -34,7 +36,8 @@ const jetpackTheme: ChartTheme = { * Woo theme configuration */ const wooTheme: ChartTheme = { - backgroundColor: '#FFFFFF', + backgroundColor: '#FFFFFF', // chart background color + labelBackgroundColor: '#FFFFFF', // label background color colors: [ '#80C8FF', '#B999FF', '#3858E9' ], gridStyles: { stroke: '#787C82', From 058df5932e72dac5ac60a5cfb739c85eacf7f4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Tue, 17 Dec 2024 00:29:14 +0000 Subject: [PATCH 10/63] Charts: update PieSemiCircleChart component (#40625) * remove height property from pie semi circle chart * improve width story control * introduce clockwise property * introduce thickness property * add pad to thickness equation * changelog --------- Co-authored-by: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> --- .../changelog/update-charts-pie-iteration | 4 +++ .../pie-semi-circle-chart.tsx | 33 +++++++++++++----- .../stories/index.stories.tsx | 34 ++++++++++++++----- .../charts/src/components/shared/types.d.ts | 2 +- 4 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 projects/js-packages/charts/changelog/update-charts-pie-iteration diff --git a/projects/js-packages/charts/changelog/update-charts-pie-iteration b/projects/js-packages/charts/changelog/update-charts-pie-iteration new file mode 100644 index 0000000000000..04e1f08148d44 --- /dev/null +++ b/projects/js-packages/charts/changelog/update-charts-pie-iteration @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Update PieSemiCircleChart component diff --git a/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx b/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx index 4eb3b27ed3263..c89c4062ece98 100644 --- a/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx @@ -19,6 +19,15 @@ interface PieSemiCircleChartProps extends BaseChartProps< DataPointPercentage[] * Note text to display below the label */ note: string; + /** + * Direction of chart rendering + * true for clockwise, false for counter-clockwise + */ + clockwise?: boolean; + /** + * Thickness of the pie chart. A value between 0 and 1 + */ + thickness?: number; } type ArcData = PieArcDatum< DataPointPercentage >; @@ -26,17 +35,21 @@ type ArcData = PieArcDatum< DataPointPercentage >; const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( { data, width, - height, label, note, withTooltips = false, + clockwise = true, + thickness = 0.4, } ) => { const providerTheme = useChartTheme(); const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip< DataPointPercentage >(); const centerX = width / 2; - const centerY = height; + const height = width / 2; + const radius = width / 2; + const pad = 0.03; + const innerRadius = radius * ( 1 - thickness + pad ); // Map the data to include index for color assignment const dataWithIndex = data.map( ( d, index ) => ( { @@ -44,6 +57,10 @@ const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( { index, } ) ); + // Set the clockwise direction based on the prop + const startAngle = clockwise ? -Math.PI / 2 : Math.PI / 2; + const endAngle = clockwise ? Math.PI / 2 : -Math.PI / 2; + const accessors = { value: ( d: DataPointPercentage & { index: number } ) => d.value, sort: ( @@ -84,17 +101,17 @@ const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( {
{ /* Main chart group that contains both the pie and text elements */ } - + { /* Pie chart */ } data={ dataWithIndex } pieValue={ accessors.value } - outerRadius={ width / 2 } // half of the diameter (width) - innerRadius={ ( width / 2 ) * 0.6 } // 60% of the radius + outerRadius={ radius } + innerRadius={ innerRadius } cornerRadius={ 3 } - padAngle={ 0.03 } - startAngle={ -Math.PI / 2 } - endAngle={ Math.PI / 2 } + padAngle={ pad } + startAngle={ startAngle } + endAngle={ endAngle } pieSort={ accessors.sort } > { pie => { diff --git a/projects/js-packages/charts/src/components/pie-semi-circle-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/pie-semi-circle-chart/stories/index.stories.tsx index 428f845e361e6..886e1c2f21119 100644 --- a/projects/js-packages/charts/src/components/pie-semi-circle-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/pie-semi-circle-chart/stories/index.stories.tsx @@ -2,12 +2,6 @@ import { PieSemiCircleChart } from '../index'; import type { Meta } from '@storybook/react'; const data = [ - { - label: 'Windows', - value: 80000, - valueDisplay: '80K', - percentage: 2, - }, { label: 'MacOS', value: 30000, @@ -20,6 +14,12 @@ const data = [ valueDisplay: '22K', percentage: 1, }, + { + label: 'Windows', + value: 80000, + valueDisplay: '80K', + percentage: 2, + }, ]; export default { @@ -35,6 +35,24 @@ export default {
), ], + argTypes: { + width: { + control: { + type: 'range', + min: 0, + max: 1000, + step: 10, + }, + }, + thickness: { + control: { + type: 'range', + min: 0, + max: 1, + step: 0.01, + }, + }, + }, } satisfies Meta< typeof PieSemiCircleChart >; const Template = args => ; @@ -42,16 +60,16 @@ const Template = args => ; export const Default = Template.bind( {} ); Default.args = { width: 500, - height: 300, data, label: 'OS', note: 'Windows +10%', + thickness: 0.4, + clockwise: true, }; export const WithTooltips = Template.bind( {} ); WithTooltips.args = { width: 500, - height: 300, data, label: 'OS', note: 'Windows +10%', diff --git a/projects/js-packages/charts/src/components/shared/types.d.ts b/projects/js-packages/charts/src/components/shared/types.d.ts index aeb74223d6d76..6b4bd04bc76c6 100644 --- a/projects/js-packages/charts/src/components/shared/types.d.ts +++ b/projects/js-packages/charts/src/components/shared/types.d.ts @@ -68,7 +68,7 @@ export type BaseChartProps< T = DataPoint | DataPointDate > = { /** * Height of the chart in pixels */ - height: number; + height?: number; /** * Chart margins */ From 9582f2547190355af9b0ba2f105676d0bfc65b91 Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:40:33 +1300 Subject: [PATCH 11/63] Charts: Adding multi series support for line charts (#40605) * Charts - Adding basic pie chart * Charts - Updating charts and adding a tooltip hook * Charts - Adding doughts to pies * Charts - Fixing dependency * changelog * Charts - Adding chart TS interface * Charts - Adding support for multiple data sets * Charts - Adding shared chart TS interface * Charts - Cleanup * Charts - Cleanup 2 * Charts - Fixing tooltips for multiple data sets, adding className prop * Charts - Updating examples * Charts - updating tooltip prop and simplifying event handlers * changelog --- .../charts/changelog/add-charts-multi-series | 4 + .../src/components/bar-chart/bar-chart.tsx | 3 +- .../line-chart/line-chart.module.scss | 22 +- .../src/components/line-chart/line-chart.tsx | 109 ++-- .../line-chart/stories/index.stories.tsx | 31 +- .../line-chart/stories/sample-data.ts | 513 ++++++++++++------ .../pie-chart/pie-chart.module.scss | 3 + .../src/components/pie-chart/pie-chart.tsx | 9 +- .../pie-semi-circle-chart.tsx | 5 +- .../stories/index.stories.tsx | 2 +- .../charts/src/components/shared/types.d.ts | 14 + 11 files changed, 494 insertions(+), 221 deletions(-) create mode 100644 projects/js-packages/charts/changelog/add-charts-multi-series create mode 100644 projects/js-packages/charts/src/components/pie-chart/pie-chart.module.scss diff --git a/projects/js-packages/charts/changelog/add-charts-multi-series b/projects/js-packages/charts/changelog/add-charts-multi-series new file mode 100644 index 0000000000000..c5707223a9596 --- /dev/null +++ b/projects/js-packages/charts/changelog/add-charts-multi-series @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Adding support for mutliple data series for the line charts. diff --git a/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx b/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx index 984fddd5ac8f4..7c6f4976eaa06 100644 --- a/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx +++ b/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx @@ -24,6 +24,7 @@ const BarChart: FC< BarChartProps > = ( { height, margin = { top: 20, right: 20, bottom: 40, left: 40 }, withTooltips = false, + className, } ) => { const theme = useChartTheme(); const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = @@ -63,7 +64,7 @@ const BarChart: FC< BarChartProps > = ( { }, [ hideTooltip ] ); return ( -
+
{ data.map( d => { diff --git a/projects/js-packages/charts/src/components/line-chart/line-chart.module.scss b/projects/js-packages/charts/src/components/line-chart/line-chart.module.scss index 2cc6da1a812ee..73f79787bf881 100644 --- a/projects/js-packages/charts/src/components/line-chart/line-chart.module.scss +++ b/projects/js-packages/charts/src/components/line-chart/line-chart.module.scss @@ -2,14 +2,24 @@ position: relative; &__tooltip { + background: #fff; padding: 0.5rem; + } + + &__tooltip-date { + font-weight: bold; + padding-bottom: 10px; + } - &-row { - margin-bottom: 0.25rem; + &__tooltip-row { + display: flex; + align-items: center; + padding: 4px 0; + justify-content: space-between; + } - &:last-child { - margin-bottom: 0; - } - } + &__tooltip-label { + font-weight: 500; + padding-right: 1rem; } } diff --git a/projects/js-packages/charts/src/components/line-chart/line-chart.tsx b/projects/js-packages/charts/src/components/line-chart/line-chart.tsx index a4ee95e27fa40..155f2adee032a 100644 --- a/projects/js-packages/charts/src/components/line-chart/line-chart.tsx +++ b/projects/js-packages/charts/src/components/line-chart/line-chart.tsx @@ -10,28 +10,54 @@ import clsx from 'clsx'; import { FC } from 'react'; import { useChartTheme } from '../../providers/theme/theme-provider'; import styles from './line-chart.module.scss'; -import type { BaseChartProps, DataPointDate } from '../shared/types'; +import type { BaseChartProps, DataPointDate, SeriesData } from '../shared/types'; // TODO: revisit grid and axis options - accept as props for frid lines, axis, values: x, y, all, none -interface LineChartProps extends BaseChartProps< DataPointDate[] > {} +interface LineChartProps extends BaseChartProps< SeriesData[] > {} -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const renderTooltip: any = ( { tooltipData } ) => { - // TODO: fix any - const datum = tooltipData?.nearestDatum?.datum; - if ( ! datum ) { - return null; - } +type TooltipData = { + date: Date; + [ key: string ]: number | Date; +}; + +type TooltipDatum = { + key: string; + value: number; +}; + +const renderTooltip = ( { + tooltipData, +}: { + tooltipData?: { + nearestDatum?: { + datum: TooltipData; + key: string; + }; + datumByKey?: { [ key: string ]: { datum: TooltipData } }; + }; +} ) => { + const nearestDatum = tooltipData?.nearestDatum?.datum; + if ( ! nearestDatum ) return null; + + const tooltipPoints: TooltipDatum[] = Object.entries( tooltipData?.datumByKey || {} ) + .map( ( [ key, { datum } ] ) => ( { + key, + value: datum.value as number, + } ) ) + .sort( ( a, b ) => b.value - a.value ); return (
-
- Date: { datum.date.toLocaleDateString() } -
-
- Value: { datum.value } +
+ { nearestDatum.date.toLocaleDateString() }
+ { tooltipPoints.map( point => ( +
+ { point.key }: + { point.value } +
+ ) ) }
); }; @@ -44,35 +70,38 @@ const formatDateTick = ( value: number ) => { } ); }; -// TODO: add support for multiple data sets - const LineChart: FC< LineChartProps > = ( { data, width, height, margin = { top: 20, right: 20, bottom: 40, left: 40 }, + className, + withTooltips = true, } ) => { const providerTheme = useChartTheme(); + + if ( ! data.length ) { + return ( +
Empty...
+ ); + } + const accessors = { xAccessor: ( d: DataPointDate ) => d.date, yAccessor: ( d: DataPointDate ) => d.value, }; - // Use theme to construct XYChart theme - const chartTheme = { + const theme = buildChartTheme( { backgroundColor: providerTheme.backgroundColor, colors: providerTheme.colors, gridStyles: providerTheme.gridStyles, tickLength: providerTheme?.tickLength || 0, gridColor: providerTheme?.gridColor || '', gridColorDark: providerTheme?.gridColorDark || '', - }; - - const theme = buildChartTheme( chartTheme ); + } ); - // return ( -
+
= ( { yScale={ { type: 'linear', nice: true } } > - - - - + { data.map( ( seriesData, index ) => ( + + ) ) } + + { withTooltips && ( + + ) }
); diff --git a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx index d5c1a7fb6e2fb..b09bc9594c4fd 100644 --- a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx @@ -19,10 +19,39 @@ export default { const Template = args => ; +// Default story with multiple series export const Default = Template.bind( {} ); Default.args = { width: 500, height: 300, margin: { top: 20, right: 20, bottom: 30, left: 40 }, - data: sampleData.mars, + data: sampleData, +}; + +// Story with single data series +export const SingleSeries = Template.bind( {} ); +SingleSeries.args = { + width: 500, + height: 300, + margin: { top: 20, right: 20, bottom: 30, left: 40 }, + data: [ sampleData[ 0 ] ], // Only London temperature data +}; + +// Story without tooltip +export const WithoutTooltip = Template.bind( {} ); +WithoutTooltip.args = { + width: 500, + height: 300, + margin: { top: 20, right: 20, bottom: 30, left: 40 }, + data: sampleData, + withTooltips: false, +}; + +// Story with custom dimensions +export const CustomDimensions = Template.bind( {} ); +CustomDimensions.args = { + width: 800, + height: 400, + margin: { top: 20, right: 20, bottom: 30, left: 40 }, + data: sampleData, }; diff --git a/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts b/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts index 53d7f8d97d72e..1231466c476d4 100644 --- a/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts +++ b/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts @@ -1,173 +1,346 @@ -// Data from UK Met Office (London/Heathrow), Australian Bureau of Meteorology (Canberra), -// and NASA Mars Curiosity Rover (Gale Crater) -const temperatureData = { - london: [ - { date: new Date( '2023-01-01' ), value: 8.2 }, - { date: new Date( '2023-01-08' ), value: 7.9 }, - { date: new Date( '2023-01-15' ), value: 5.1 }, - { date: new Date( '2023-01-22' ), value: 4.8 }, - { date: new Date( '2023-01-29' ), value: 6.3 }, - { date: new Date( '2023-02-05' ), value: 7.2 }, - { date: new Date( '2023-02-12' ), value: 9.4 }, - { date: new Date( '2023-02-19' ), value: 8.7 }, - { date: new Date( '2023-02-26' ), value: 7.1 }, - { date: new Date( '2023-03-05' ), value: 8.3 }, - { date: new Date( '2023-03-12' ), value: 9.5 }, - { date: new Date( '2023-03-19' ), value: 11.2 }, - { date: new Date( '2023-03-26' ), value: 12.8 }, - { date: new Date( '2023-04-02' ), value: 13.4 }, - { date: new Date( '2023-04-09' ), value: 14.1 }, - { date: new Date( '2023-04-16' ), value: 15.3 }, - { date: new Date( '2023-04-23' ), value: 14.8 }, - { date: new Date( '2023-04-30' ), value: 15.7 }, - { date: new Date( '2023-05-07' ), value: 16.9 }, - { date: new Date( '2023-05-14' ), value: 17.2 }, - { date: new Date( '2023-05-21' ), value: 18.4 }, - { date: new Date( '2023-05-28' ), value: 19.1 }, - { date: new Date( '2023-06-04' ), value: 20.3 }, - { date: new Date( '2023-06-11' ), value: 21.5 }, - { date: new Date( '2023-06-18' ), value: 22.8 }, - { date: new Date( '2023-06-25' ), value: 21.9 }, - { date: new Date( '2023-07-02' ), value: 23.1 }, - { date: new Date( '2023-07-09' ), value: 22.7 }, - { date: new Date( '2023-07-16' ), value: 24.2 }, - { date: new Date( '2023-07-23' ), value: 23.8 }, - { date: new Date( '2023-07-30' ), value: 22.9 }, - { date: new Date( '2023-08-06' ), value: 23.4 }, - { date: new Date( '2023-08-13' ), value: 22.8 }, - { date: new Date( '2023-08-20' ), value: 21.9 }, - { date: new Date( '2023-08-27' ), value: 20.7 }, - { date: new Date( '2023-09-03' ), value: 19.8 }, - { date: new Date( '2023-09-10' ), value: 18.9 }, - { date: new Date( '2023-09-17' ), value: 17.6 }, - { date: new Date( '2023-09-24' ), value: 16.8 }, - { date: new Date( '2023-10-01' ), value: 15.9 }, - { date: new Date( '2023-10-08' ), value: 14.7 }, - { date: new Date( '2023-10-15' ), value: 13.8 }, - { date: new Date( '2023-10-22' ), value: 12.9 }, - { date: new Date( '2023-10-29' ), value: 11.7 }, - { date: new Date( '2023-11-05' ), value: 10.8 }, - { date: new Date( '2023-11-12' ), value: 9.9 }, - { date: new Date( '2023-11-19' ), value: 8.7 }, - { date: new Date( '2023-11-26' ), value: 7.8 }, - { date: new Date( '2023-12-03' ), value: 6.9 }, - { date: new Date( '2023-12-10' ), value: 5.8 }, - { date: new Date( '2023-12-17' ), value: 4.9 }, - { date: new Date( '2023-12-24' ), value: 5.7 }, - { date: new Date( '2023-12-31' ), value: 6.2 }, - ], +import type { SeriesData } from '../../shared/types'; - canberra: [ - { date: new Date( '2023-01-01' ), value: 28.5 }, - { date: new Date( '2023-01-08' ), value: 29.2 }, - { date: new Date( '2023-01-15' ), value: 30.1 }, - { date: new Date( '2023-01-22' ), value: 29.8 }, - { date: new Date( '2023-01-29' ), value: 28.9 }, - { date: new Date( '2023-02-05' ), value: 27.8 }, - { date: new Date( '2023-02-12' ), value: 26.9 }, - { date: new Date( '2023-02-19' ), value: 25.7 }, - { date: new Date( '2023-02-26' ), value: 24.8 }, - { date: new Date( '2023-03-05' ), value: 23.9 }, - { date: new Date( '2023-03-12' ), value: 22.8 }, - { date: new Date( '2023-03-19' ), value: 21.7 }, - { date: new Date( '2023-03-26' ), value: 20.8 }, - { date: new Date( '2023-04-02' ), value: 19.6 }, - { date: new Date( '2023-04-09' ), value: 18.4 }, - { date: new Date( '2023-04-16' ), value: 17.2 }, - { date: new Date( '2023-04-23' ), value: 16.1 }, - { date: new Date( '2023-04-30' ), value: 15.3 }, - { date: new Date( '2023-05-07' ), value: 14.2 }, - { date: new Date( '2023-05-14' ), value: 13.1 }, - { date: new Date( '2023-05-21' ), value: 12.3 }, - { date: new Date( '2023-05-28' ), value: 11.4 }, - { date: new Date( '2023-06-04' ), value: 10.2 }, - { date: new Date( '2023-06-11' ), value: 9.1 }, - { date: new Date( '2023-06-18' ), value: 8.3 }, - { date: new Date( '2023-06-25' ), value: 7.8 }, - { date: new Date( '2023-07-02' ), value: 7.1 }, - { date: new Date( '2023-07-09' ), value: 6.9 }, - { date: new Date( '2023-07-16' ), value: 7.2 }, - { date: new Date( '2023-07-23' ), value: 8.1 }, - { date: new Date( '2023-07-30' ), value: 9.3 }, - { date: new Date( '2023-08-06' ), value: 10.4 }, - { date: new Date( '2023-08-13' ), value: 11.6 }, - { date: new Date( '2023-08-20' ), value: 12.8 }, - { date: new Date( '2023-08-27' ), value: 13.9 }, - { date: new Date( '2023-09-03' ), value: 15.2 }, - { date: new Date( '2023-09-10' ), value: 16.4 }, - { date: new Date( '2023-09-17' ), value: 17.6 }, - { date: new Date( '2023-09-24' ), value: 18.9 }, - { date: new Date( '2023-10-01' ), value: 20.1 }, - { date: new Date( '2023-10-08' ), value: 21.3 }, - { date: new Date( '2023-10-15' ), value: 22.5 }, - { date: new Date( '2023-10-22' ), value: 23.7 }, - { date: new Date( '2023-10-29' ), value: 24.8 }, - { date: new Date( '2023-11-05' ), value: 25.9 }, - { date: new Date( '2023-11-12' ), value: 26.7 }, - { date: new Date( '2023-11-19' ), value: 27.8 }, - { date: new Date( '2023-11-26' ), value: 28.6 }, - { date: new Date( '2023-12-03' ), value: 29.4 }, - { date: new Date( '2023-12-10' ), value: 30.2 }, - { date: new Date( '2023-12-17' ), value: 29.8 }, - { date: new Date( '2023-12-24' ), value: 28.9 }, - { date: new Date( '2023-12-31' ), value: 29.3 }, - ], - - mars: [ - { date: new Date( '2023-01-01' ), value: -63 }, - { date: new Date( '2023-01-08' ), value: -64 }, - { date: new Date( '2023-01-15' ), value: -65 }, - { date: new Date( '2023-01-22' ), value: -63 }, - { date: new Date( '2023-01-29' ), value: -62 }, - { date: new Date( '2023-02-05' ), value: -60 }, - { date: new Date( '2023-02-12' ), value: -58 }, - { date: new Date( '2023-02-19' ), value: -55 }, - { date: new Date( '2023-02-26' ), value: -52 }, - { date: new Date( '2023-03-05' ), value: -48 }, - { date: new Date( '2023-03-12' ), value: -45 }, - { date: new Date( '2023-03-19' ), value: -42 }, - { date: new Date( '2023-03-26' ), value: -38 }, - { date: new Date( '2023-04-02' ), value: -35 }, - { date: new Date( '2023-04-09' ), value: -32 }, - { date: new Date( '2023-04-16' ), value: -28 }, - { date: new Date( '2023-04-23' ), value: -25 }, - { date: new Date( '2023-04-30' ), value: -22 }, - { date: new Date( '2023-05-07' ), value: -18 }, - { date: new Date( '2023-05-14' ), value: -15 }, - { date: new Date( '2023-05-21' ), value: -12 }, - { date: new Date( '2023-05-28' ), value: -8 }, - { date: new Date( '2023-06-04' ), value: -5 }, - { date: new Date( '2023-06-11' ), value: -2 }, - { date: new Date( '2023-06-18' ), value: 0 }, - { date: new Date( '2023-06-25' ), value: 2 }, - { date: new Date( '2023-07-02' ), value: 5 }, - { date: new Date( '2023-07-09' ), value: 8 }, - { date: new Date( '2023-07-16' ), value: 10 }, - { date: new Date( '2023-07-23' ), value: 12 }, - { date: new Date( '2023-07-30' ), value: 15 }, - { date: new Date( '2023-08-06' ), value: 17 }, - { date: new Date( '2023-08-13' ), value: 20 }, - { date: new Date( '2023-08-20' ), value: 22 }, - { date: new Date( '2023-08-27' ), value: 20 }, - { date: new Date( '2023-09-03' ), value: 18 }, - { date: new Date( '2023-09-10' ), value: 15 }, - { date: new Date( '2023-09-17' ), value: 12 }, - { date: new Date( '2023-09-24' ), value: 8 }, - { date: new Date( '2023-10-01' ), value: 5 }, - { date: new Date( '2023-10-08' ), value: 2 }, - { date: new Date( '2023-10-15' ), value: -2 }, - { date: new Date( '2023-10-22' ), value: -5 }, - { date: new Date( '2023-10-29' ), value: -8 }, - { date: new Date( '2023-11-05' ), value: -12 }, - { date: new Date( '2023-11-12' ), value: -15 }, - { date: new Date( '2023-11-19' ), value: -18 }, - { date: new Date( '2023-11-26' ), value: -22 }, - { date: new Date( '2023-12-03' ), value: -25 }, - { date: new Date( '2023-12-10' ), value: -28 }, - { date: new Date( '2023-12-17' ), value: -32 }, - { date: new Date( '2023-12-24' ), value: -35 }, - { date: new Date( '2023-12-31' ), value: -38 }, - ], -}; +// Sample data +const temperatureData: SeriesData[] = [ + { + label: 'London', + data: [ + // 2022 data + { date: new Date( '2022-01-01' ), value: 7.8 }, + { date: new Date( '2022-01-08' ), value: 7.2 }, + { date: new Date( '2022-01-15' ), value: 6.9 }, + { date: new Date( '2022-01-22' ), value: 6.5 }, + { date: new Date( '2022-01-29' ), value: 7.1 }, + { date: new Date( '2022-02-05' ), value: 8.3 }, + { date: new Date( '2022-02-12' ), value: 8.9 }, + { date: new Date( '2022-02-19' ), value: 9.2 }, + { date: new Date( '2022-02-26' ), value: 8.7 }, + { date: new Date( '2022-03-05' ), value: 9.4 }, + { date: new Date( '2022-03-12' ), value: 10.2 }, + { date: new Date( '2022-03-19' ), value: 11.5 }, + { date: new Date( '2022-03-26' ), value: 12.3 }, + { date: new Date( '2022-04-02' ), value: 13.1 }, + { date: new Date( '2022-04-09' ), value: 13.8 }, + { date: new Date( '2022-04-16' ), value: 14.6 }, + { date: new Date( '2022-04-23' ), value: 15.2 }, + { date: new Date( '2022-04-30' ), value: 15.9 }, + { date: new Date( '2022-05-07' ), value: 16.7 }, + { date: new Date( '2022-05-14' ), value: 17.4 }, + { date: new Date( '2022-05-21' ), value: 18.2 }, + { date: new Date( '2022-05-28' ), value: 18.9 }, + { date: new Date( '2022-06-04' ), value: 19.7 }, + { date: new Date( '2022-06-11' ), value: 20.5 }, + { date: new Date( '2022-06-18' ), value: 21.3 }, + { date: new Date( '2022-06-25' ), value: 22.1 }, + { date: new Date( '2022-07-02' ), value: 22.8 }, + { date: new Date( '2022-07-09' ), value: 23.6 }, + { date: new Date( '2022-07-16' ), value: 24.4 }, + { date: new Date( '2022-07-23' ), value: 25.2 }, + { date: new Date( '2022-07-30' ), value: 24.8 }, + { date: new Date( '2022-08-06' ), value: 24.1 }, + { date: new Date( '2022-08-13' ), value: 23.5 }, + { date: new Date( '2022-08-20' ), value: 22.8 }, + { date: new Date( '2022-08-27' ), value: 21.9 }, + { date: new Date( '2022-09-03' ), value: 20.7 }, + { date: new Date( '2022-09-10' ), value: 19.5 }, + { date: new Date( '2022-09-17' ), value: 18.3 }, + { date: new Date( '2022-09-24' ), value: 17.1 }, + { date: new Date( '2022-10-01' ), value: 16.2 }, + { date: new Date( '2022-10-08' ), value: 15.1 }, + { date: new Date( '2022-10-15' ), value: 14.2 }, + { date: new Date( '2022-10-22' ), value: 13.1 }, + { date: new Date( '2022-10-29' ), value: 12.2 }, + { date: new Date( '2022-11-05' ), value: 11.1 }, + { date: new Date( '2022-11-12' ), value: 10.2 }, + { date: new Date( '2022-11-19' ), value: 9.1 }, + { date: new Date( '2022-11-26' ), value: 8.2 }, + { date: new Date( '2022-12-03' ), value: 7.1 }, + { date: new Date( '2022-12-10' ), value: 6.2 }, + { date: new Date( '2022-12-17' ), value: 5.5 }, + { date: new Date( '2022-12-24' ), value: 5.2 }, + { date: new Date( '2022-12-31' ), value: 6.8 }, + // 2023 data + { date: new Date( '2023-01-01' ), value: 8.2 }, + { date: new Date( '2023-01-08' ), value: 7.9 }, + { date: new Date( '2023-01-15' ), value: 5.1 }, + { date: new Date( '2023-01-22' ), value: 4.8 }, + { date: new Date( '2023-01-29' ), value: 6.3 }, + { date: new Date( '2023-02-05' ), value: 7.2 }, + { date: new Date( '2023-02-12' ), value: 9.4 }, + { date: new Date( '2023-02-19' ), value: 8.7 }, + { date: new Date( '2023-02-26' ), value: 7.1 }, + { date: new Date( '2023-03-05' ), value: 8.3 }, + { date: new Date( '2023-03-12' ), value: 9.5 }, + { date: new Date( '2023-03-19' ), value: 11.2 }, + { date: new Date( '2023-03-26' ), value: 12.8 }, + { date: new Date( '2023-04-02' ), value: 13.4 }, + { date: new Date( '2023-04-09' ), value: 14.1 }, + { date: new Date( '2023-04-16' ), value: 15.3 }, + { date: new Date( '2023-04-23' ), value: 14.8 }, + { date: new Date( '2023-04-30' ), value: 15.7 }, + { date: new Date( '2023-05-07' ), value: 16.9 }, + { date: new Date( '2023-05-14' ), value: 17.2 }, + { date: new Date( '2023-05-21' ), value: 18.4 }, + { date: new Date( '2023-05-28' ), value: 19.1 }, + { date: new Date( '2023-06-04' ), value: 20.3 }, + { date: new Date( '2023-06-11' ), value: 21.5 }, + { date: new Date( '2023-06-18' ), value: 22.8 }, + { date: new Date( '2023-06-25' ), value: 21.9 }, + { date: new Date( '2023-07-02' ), value: 23.1 }, + { date: new Date( '2023-07-09' ), value: 22.7 }, + { date: new Date( '2023-07-16' ), value: 24.2 }, + { date: new Date( '2023-07-23' ), value: 23.8 }, + { date: new Date( '2023-07-30' ), value: 22.9 }, + { date: new Date( '2023-08-06' ), value: 23.4 }, + { date: new Date( '2023-08-13' ), value: 22.8 }, + { date: new Date( '2023-08-20' ), value: 21.9 }, + { date: new Date( '2023-08-27' ), value: 20.7 }, + { date: new Date( '2023-09-03' ), value: 19.8 }, + { date: new Date( '2023-09-10' ), value: 18.9 }, + { date: new Date( '2023-09-17' ), value: 17.6 }, + { date: new Date( '2023-09-24' ), value: 16.8 }, + { date: new Date( '2023-10-01' ), value: 15.9 }, + { date: new Date( '2023-10-08' ), value: 14.7 }, + { date: new Date( '2023-10-15' ), value: 13.8 }, + { date: new Date( '2023-10-22' ), value: 12.9 }, + { date: new Date( '2023-10-29' ), value: 11.7 }, + { date: new Date( '2023-11-05' ), value: 10.8 }, + { date: new Date( '2023-11-12' ), value: 9.9 }, + { date: new Date( '2023-11-19' ), value: 8.7 }, + { date: new Date( '2023-11-26' ), value: 7.8 }, + { date: new Date( '2023-12-03' ), value: 6.9 }, + { date: new Date( '2023-12-10' ), value: 5.8 }, + { date: new Date( '2023-12-17' ), value: 4.9 }, + { date: new Date( '2023-12-24' ), value: 5.7 }, + { date: new Date( '2023-12-31' ), value: 6.2 }, + ], + }, + { + label: 'Canberra', + data: [ + // 2022 data + { date: new Date( '2022-01-01' ), value: 27.9 }, + { date: new Date( '2022-01-08' ), value: 28.4 }, + { date: new Date( '2022-01-15' ), value: 29.2 }, + { date: new Date( '2022-01-22' ), value: 28.9 }, + { date: new Date( '2022-01-29' ), value: 28.1 }, + { date: new Date( '2022-02-05' ), value: 27.3 }, + { date: new Date( '2022-02-12' ), value: 26.5 }, + { date: new Date( '2022-02-19' ), value: 25.4 }, + { date: new Date( '2022-02-26' ), value: 24.2 }, + { date: new Date( '2022-03-05' ), value: 23.1 }, + { date: new Date( '2022-03-12' ), value: 22.3 }, + { date: new Date( '2022-03-19' ), value: 21.2 }, + { date: new Date( '2022-03-26' ), value: 20.1 }, + { date: new Date( '2022-04-02' ), value: 19.2 }, + { date: new Date( '2022-04-09' ), value: 18.1 }, + { date: new Date( '2022-04-16' ), value: 16.9 }, + { date: new Date( '2022-04-23' ), value: 15.8 }, + { date: new Date( '2022-04-30' ), value: 14.9 }, + { date: new Date( '2022-05-07' ), value: 13.8 }, + { date: new Date( '2022-05-14' ), value: 12.9 }, + { date: new Date( '2022-05-21' ), value: 11.8 }, + { date: new Date( '2022-05-28' ), value: 10.9 }, + { date: new Date( '2022-06-04' ), value: 9.8 }, + { date: new Date( '2022-06-11' ), value: 8.9 }, + { date: new Date( '2022-06-18' ), value: 8.1 }, + { date: new Date( '2022-06-25' ), value: 7.5 }, + { date: new Date( '2022-07-02' ), value: 6.9 }, + { date: new Date( '2022-07-09' ), value: 6.7 }, + { date: new Date( '2022-07-16' ), value: 7.1 }, + { date: new Date( '2022-07-23' ), value: 7.9 }, + { date: new Date( '2022-07-30' ), value: 8.8 }, + { date: new Date( '2022-08-06' ), value: 9.9 }, + { date: new Date( '2022-08-13' ), value: 11.2 }, + { date: new Date( '2022-08-20' ), value: 12.4 }, + { date: new Date( '2022-08-27' ), value: 13.6 }, + { date: new Date( '2022-09-03' ), value: 14.8 }, + { date: new Date( '2022-09-10' ), value: 16.1 }, + { date: new Date( '2022-09-17' ), value: 17.3 }, + { date: new Date( '2022-09-24' ), value: 18.5 }, + { date: new Date( '2022-10-01' ), value: 19.8 }, + { date: new Date( '2022-10-08' ), value: 21.1 }, + { date: new Date( '2022-10-15' ), value: 22.3 }, + { date: new Date( '2022-10-22' ), value: 23.5 }, + { date: new Date( '2022-10-29' ), value: 24.6 }, + { date: new Date( '2022-11-05' ), value: 25.7 }, + { date: new Date( '2022-11-12' ), value: 26.5 }, + { date: new Date( '2022-11-19' ), value: 27.4 }, + { date: new Date( '2022-11-26' ), value: 28.2 }, + { date: new Date( '2022-12-03' ), value: 28.9 }, + { date: new Date( '2022-12-10' ), value: 29.5 }, + { date: new Date( '2022-12-17' ), value: 29.1 }, + { date: new Date( '2022-12-24' ), value: 28.2 }, + { date: new Date( '2022-12-31' ), value: 28.7 }, + // 2023 data + { date: new Date( '2023-01-01' ), value: 28.5 }, + { date: new Date( '2023-01-08' ), value: 29.2 }, + { date: new Date( '2023-01-15' ), value: 30.1 }, + { date: new Date( '2023-01-22' ), value: 29.8 }, + { date: new Date( '2023-01-29' ), value: 28.9 }, + { date: new Date( '2023-02-05' ), value: 27.8 }, + { date: new Date( '2023-02-12' ), value: 26.9 }, + { date: new Date( '2023-02-19' ), value: 25.7 }, + { date: new Date( '2023-02-26' ), value: 24.8 }, + { date: new Date( '2023-03-05' ), value: 23.9 }, + { date: new Date( '2023-03-12' ), value: 22.8 }, + { date: new Date( '2023-03-19' ), value: 21.7 }, + { date: new Date( '2023-03-26' ), value: 20.8 }, + { date: new Date( '2023-04-02' ), value: 19.6 }, + { date: new Date( '2023-04-09' ), value: 18.4 }, + { date: new Date( '2023-04-16' ), value: 17.2 }, + { date: new Date( '2023-04-23' ), value: 16.1 }, + { date: new Date( '2023-04-30' ), value: 15.3 }, + { date: new Date( '2023-05-07' ), value: 14.2 }, + { date: new Date( '2023-05-14' ), value: 13.1 }, + { date: new Date( '2023-05-21' ), value: 12.3 }, + { date: new Date( '2023-05-28' ), value: 11.4 }, + { date: new Date( '2023-06-04' ), value: 10.2 }, + { date: new Date( '2023-06-11' ), value: 9.1 }, + { date: new Date( '2023-06-18' ), value: 8.3 }, + { date: new Date( '2023-06-25' ), value: 7.8 }, + { date: new Date( '2023-07-02' ), value: 7.1 }, + { date: new Date( '2023-07-09' ), value: 6.9 }, + { date: new Date( '2023-07-16' ), value: 7.2 }, + { date: new Date( '2023-07-23' ), value: 8.1 }, + { date: new Date( '2023-07-30' ), value: 9.3 }, + { date: new Date( '2023-08-06' ), value: 10.4 }, + { date: new Date( '2023-08-13' ), value: 11.6 }, + { date: new Date( '2023-08-20' ), value: 12.8 }, + { date: new Date( '2023-08-27' ), value: 13.9 }, + { date: new Date( '2023-09-03' ), value: 15.2 }, + { date: new Date( '2023-09-10' ), value: 16.4 }, + { date: new Date( '2023-09-17' ), value: 17.6 }, + { date: new Date( '2023-09-24' ), value: 18.9 }, + { date: new Date( '2023-10-01' ), value: 20.1 }, + { date: new Date( '2023-10-08' ), value: 21.3 }, + { date: new Date( '2023-10-15' ), value: 22.5 }, + { date: new Date( '2023-10-22' ), value: 23.7 }, + { date: new Date( '2023-10-29' ), value: 24.8 }, + { date: new Date( '2023-11-05' ), value: 25.9 }, + { date: new Date( '2023-11-12' ), value: 26.7 }, + { date: new Date( '2023-11-19' ), value: 27.8 }, + { date: new Date( '2023-11-26' ), value: 28.6 }, + { date: new Date( '2023-12-03' ), value: 29.4 }, + { date: new Date( '2023-12-10' ), value: 30.2 }, + { date: new Date( '2023-12-17' ), value: 29.8 }, + { date: new Date( '2023-12-24' ), value: 28.9 }, + { date: new Date( '2023-12-31' ), value: 29.3 }, + ], + }, + { + label: 'Mars', + data: [ + // 2022 data + { date: new Date( '2022-01-01' ), value: -62 }, + { date: new Date( '2022-01-08' ), value: -63 }, + { date: new Date( '2022-01-15' ), value: -64 }, + { date: new Date( '2022-01-22' ), value: -62 }, + { date: new Date( '2022-01-29' ), value: -61 }, + { date: new Date( '2022-02-05' ), value: -59 }, + { date: new Date( '2022-02-12' ), value: -56 }, + { date: new Date( '2022-02-19' ), value: -53 }, + { date: new Date( '2022-02-26' ), value: -50 }, + { date: new Date( '2022-03-05' ), value: -47 }, + { date: new Date( '2022-03-12' ), value: -44 }, + { date: new Date( '2022-03-19' ), value: -41 }, + { date: new Date( '2022-03-26' ), value: -37 }, + { date: new Date( '2022-04-02' ), value: -34 }, + { date: new Date( '2022-04-09' ), value: -31 }, + { date: new Date( '2022-04-16' ), value: -27 }, + { date: new Date( '2022-04-23' ), value: -24 }, + { date: new Date( '2022-04-30' ), value: -21 }, + { date: new Date( '2022-05-07' ), value: -17 }, + { date: new Date( '2022-05-14' ), value: -14 }, + { date: new Date( '2022-05-21' ), value: -11 }, + { date: new Date( '2022-05-28' ), value: -7 }, + { date: new Date( '2022-06-04' ), value: -4 }, + { date: new Date( '2022-06-11' ), value: -1 }, + { date: new Date( '2022-06-18' ), value: 1 }, + { date: new Date( '2022-06-25' ), value: 3 }, + { date: new Date( '2022-07-02' ), value: 6 }, + { date: new Date( '2022-07-09' ), value: 9 }, + { date: new Date( '2022-07-16' ), value: 11 }, + { date: new Date( '2022-07-23' ), value: 13 }, + { date: new Date( '2022-07-30' ), value: 16 }, + { date: new Date( '2022-08-06' ), value: 18 }, + { date: new Date( '2022-08-13' ), value: 21 }, + { date: new Date( '2022-08-20' ), value: 23 }, + { date: new Date( '2022-08-27' ), value: 21 }, + { date: new Date( '2022-09-03' ), value: 19 }, + { date: new Date( '2022-09-10' ), value: 16 }, + { date: new Date( '2022-09-17' ), value: 13 }, + { date: new Date( '2022-09-24' ), value: 9 }, + { date: new Date( '2022-10-01' ), value: 6 }, + { date: new Date( '2022-10-08' ), value: 3 }, + { date: new Date( '2022-10-15' ), value: -1 }, + { date: new Date( '2022-10-22' ), value: -4 }, + { date: new Date( '2022-10-29' ), value: -7 }, + { date: new Date( '2022-11-05' ), value: -11 }, + { date: new Date( '2022-11-12' ), value: -14 }, + { date: new Date( '2022-11-19' ), value: -17 }, + { date: new Date( '2022-11-26' ), value: -21 }, + { date: new Date( '2022-12-03' ), value: -24 }, + { date: new Date( '2022-12-10' ), value: -27 }, + { date: new Date( '2022-12-17' ), value: -31 }, + { date: new Date( '2022-12-24' ), value: -36 }, + { date: new Date( '2022-12-31' ), value: -37 }, + // 2023 data + { date: new Date( '2023-01-01' ), value: -63 }, + { date: new Date( '2023-01-08' ), value: -64 }, + { date: new Date( '2023-01-15' ), value: -65 }, + { date: new Date( '2023-01-22' ), value: -63 }, + { date: new Date( '2023-01-29' ), value: -62 }, + { date: new Date( '2023-02-05' ), value: -60 }, + { date: new Date( '2023-02-12' ), value: -58 }, + { date: new Date( '2023-02-19' ), value: -55 }, + { date: new Date( '2023-02-26' ), value: -52 }, + { date: new Date( '2023-03-05' ), value: -48 }, + { date: new Date( '2023-03-12' ), value: -45 }, + { date: new Date( '2023-03-19' ), value: -42 }, + { date: new Date( '2023-03-26' ), value: -38 }, + { date: new Date( '2023-04-02' ), value: -35 }, + { date: new Date( '2023-04-09' ), value: -32 }, + { date: new Date( '2023-04-16' ), value: -28 }, + { date: new Date( '2023-04-23' ), value: -25 }, + { date: new Date( '2023-04-30' ), value: -22 }, + { date: new Date( '2023-05-07' ), value: -18 }, + { date: new Date( '2023-05-14' ), value: -15 }, + { date: new Date( '2023-05-21' ), value: -12 }, + { date: new Date( '2023-05-28' ), value: -8 }, + { date: new Date( '2023-06-04' ), value: -5 }, + { date: new Date( '2023-06-11' ), value: -2 }, + { date: new Date( '2023-06-18' ), value: 0 }, + { date: new Date( '2023-06-25' ), value: 2 }, + { date: new Date( '2023-07-02' ), value: 5 }, + { date: new Date( '2023-07-09' ), value: 8 }, + { date: new Date( '2023-07-16' ), value: 10 }, + { date: new Date( '2023-07-23' ), value: 12 }, + { date: new Date( '2023-07-30' ), value: 15 }, + { date: new Date( '2023-08-06' ), value: 17 }, + { date: new Date( '2023-08-13' ), value: 20 }, + { date: new Date( '2023-08-20' ), value: 22 }, + { date: new Date( '2023-08-27' ), value: 20 }, + { date: new Date( '2023-09-03' ), value: 18 }, + { date: new Date( '2023-09-10' ), value: 15 }, + { date: new Date( '2023-09-17' ), value: 12 }, + { date: new Date( '2023-09-24' ), value: 8 }, + { date: new Date( '2023-10-01' ), value: 5 }, + { date: new Date( '2023-10-08' ), value: 2 }, + { date: new Date( '2023-10-15' ), value: -2 }, + { date: new Date( '2023-10-22' ), value: -5 }, + { date: new Date( '2023-10-29' ), value: -8 }, + { date: new Date( '2023-11-05' ), value: -12 }, + { date: new Date( '2023-11-12' ), value: -15 }, + { date: new Date( '2023-11-19' ), value: -18 }, + { date: new Date( '2023-11-26' ), value: -22 }, + { date: new Date( '2023-12-03' ), value: -25 }, + { date: new Date( '2023-12-10' ), value: -28 }, + { date: new Date( '2023-12-17' ), value: -32 }, + { date: new Date( '2023-12-24' ), value: -35 }, + { date: new Date( '2023-12-31' ), value: -38 }, + ], + }, +]; export default temperatureData; diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.module.scss b/projects/js-packages/charts/src/components/pie-chart/pie-chart.module.scss new file mode 100644 index 0000000000000..cbb24ea286735 --- /dev/null +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.module.scss @@ -0,0 +1,3 @@ +.pie-chart { + position: relative; +} diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx index bffe55ea4de25..764910bfa82f7 100644 --- a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx @@ -1,9 +1,11 @@ import { Group } from '@visx/group'; import { Pie } from '@visx/shape'; +import clsx from 'clsx'; import { SVGProps } from 'react'; import useChartMouseHandler from '../../hooks/use-chart-mouse-handler'; import { useChartTheme, defaultTheme } from '../../providers/theme'; -import { Tooltip } from '../tooltip'; +import { BaseTooltip } from '../tooltip'; +import styles from './pie-chart.module.scss'; import type { BaseChartProps, DataPoint } from '../shared/types'; // TODO: add animation @@ -27,6 +29,7 @@ const PieChart = ( { height, withTooltips = false, innerRadius = 0, + className, }: PieChartProps ) => { const providerTheme = useChartTheme(); const { onMouseMove, onMouseLeave, tooltipOpen, tooltipData, tooltipLeft, tooltipTop } = @@ -46,7 +49,7 @@ const PieChart = ( { }; return ( -
+
{ withTooltips && tooltipOpen && tooltipData && ( - = ( { width, label, note, + className, withTooltips = false, clockwise = true, thickness = 0.4, @@ -98,7 +99,9 @@ const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( { ); return ( -
+
{ /* Main chart group that contains both the pie and text elements */ } diff --git a/projects/js-packages/charts/src/components/pie-semi-circle-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/pie-semi-circle-chart/stories/index.stories.tsx index 886e1c2f21119..6f2eee9df73ae 100644 --- a/projects/js-packages/charts/src/components/pie-semi-circle-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/pie-semi-circle-chart/stories/index.stories.tsx @@ -73,5 +73,5 @@ WithTooltips.args = { data, label: 'OS', note: 'Windows +10%', - WithTooltips: true, + withTooltips: true, }; diff --git a/projects/js-packages/charts/src/components/shared/types.d.ts b/projects/js-packages/charts/src/components/shared/types.d.ts index 6b4bd04bc76c6..4b0836d7b4770 100644 --- a/projects/js-packages/charts/src/components/shared/types.d.ts +++ b/projects/js-packages/charts/src/components/shared/types.d.ts @@ -10,6 +10,16 @@ export type DataPointDate = { value: number; }; +export type SeriesData = { + label: string; + data: DataPointDate[]; +}; + +export type MultipleDataPointsDate = { + label: string; + data: DataPointDate[]; +}; + export type DataPointPercentage = { /** * Label for the data point @@ -61,6 +71,10 @@ export type BaseChartProps< T = DataPoint | DataPointDate > = { * Array of data points to display in the chart */ data: T extends DataPoint | DataPointDate ? T[] : T; + /** + * Additional CSS class name for the chart container + */ + className?: string; /** * Width of the chart in pixels */ From e76d7196ee80123c42ab63a60431e2bacd6a1758 Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:25:52 +1300 Subject: [PATCH 12/63] Charts: Fixing theme storybook story (#40640) * Charts: Fixing theme demo after introducing multiple series * changelog --- .../changelog/fix-charts-line-chart-themes | 4 +++ .../providers/theme/stories/index.stories.tsx | 31 ++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 projects/js-packages/charts/changelog/fix-charts-line-chart-themes diff --git a/projects/js-packages/charts/changelog/fix-charts-line-chart-themes b/projects/js-packages/charts/changelog/fix-charts-line-chart-themes new file mode 100644 index 0000000000000..5dc5e61fc4c0a --- /dev/null +++ b/projects/js-packages/charts/changelog/fix-charts-line-chart-themes @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixing a bug in Chart storybook data. diff --git a/projects/js-packages/charts/src/providers/theme/stories/index.stories.tsx b/projects/js-packages/charts/src/providers/theme/stories/index.stories.tsx index 69e1cdd50887f..27a5f350232fd 100644 --- a/projects/js-packages/charts/src/providers/theme/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/providers/theme/stories/index.stories.tsx @@ -21,6 +21,29 @@ const sampleData = [ { date: new Date( '2024-01-05' ), value: 30, label: 'Jan 5' }, ]; +const lineSampleData = [ + { + label: 'Line 1', + data: [ + { date: new Date( '2024-01-01' ), value: 10, label: 'Jan 1' }, + { date: new Date( '2024-01-02' ), value: 20, label: 'Jan 2' }, + { date: new Date( '2024-01-03' ), value: 15, label: 'Jan 3' }, + { date: new Date( '2024-01-04' ), value: 25, label: 'Jan 4' }, + { date: new Date( '2024-01-05' ), value: 30, label: 'Jan 5' }, + ], + }, + { + label: 'Line 2', + data: [ + { date: new Date( '2024-01-01' ), value: 1, label: 'Jan 1' }, + { date: new Date( '2024-01-02' ), value: 2, label: 'Jan 2' }, + { date: new Date( '2024-01-03' ), value: 1.5, label: 'Jan 3' }, + { date: new Date( '2024-01-04' ), value: 2.5, label: 'Jan 4' }, + { date: new Date( '2024-01-05' ), value: 3, label: 'Jan 5' }, + ], + }, +]; + const pieData = [ { label: 'Windows', @@ -54,7 +77,7 @@ export const Default: Story = { render: () => ( - + ( - + ( - + - + Date: Tue, 17 Dec 2024 11:49:02 +0400 Subject: [PATCH 13/63] =?UTF-8?q?[WooCommerce=20Analytics]=C2=A0Add=20Comm?= =?UTF-8?q?on=20props,=20new=20events=20and=20bug=20fixing=20=20(#40562)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Avoid to track Ajax and Login pages * Support session_started event * Add Common events * Fix error triggering checkout_view when visiting classic cart * Fix error not triggering cart_view when visiting a page with cart block * Fix "null" as string error in user_id * Fix consistency problems with totals and counts * Fix update cart quantity * Fix update cart in blocks * Add totals, shipping totals & taxes * Fix Checkout Product View data --- .../changelog/add-common-props | 4 + .../src/class-checkout-flow.php | 22 ++- .../src/class-universal.php | 161 +++++++----------- .../src/class-woo-analytics-trait.php | 86 +++++++++- .../src/class-woocommerce-analytics.php | 3 +- 5 files changed, 168 insertions(+), 108 deletions(-) create mode 100644 projects/packages/woocommerce-analytics/changelog/add-common-props diff --git a/projects/packages/woocommerce-analytics/changelog/add-common-props b/projects/packages/woocommerce-analytics/changelog/add-common-props new file mode 100644 index 0000000000000..c1f0acb1c1d3f --- /dev/null +++ b/projects/packages/woocommerce-analytics/changelog/add-common-props @@ -0,0 +1,4 @@ +Significance: minor +Type: changed + +Add common props, more events and bug fixing. diff --git a/projects/packages/woocommerce-analytics/src/class-checkout-flow.php b/projects/packages/woocommerce-analytics/src/class-checkout-flow.php index 9d7422f530d17..efdb2cb5ce7bb 100644 --- a/projects/packages/woocommerce-analytics/src/class-checkout-flow.php +++ b/projects/packages/woocommerce-analytics/src/class-checkout-flow.php @@ -96,7 +96,6 @@ public function capture_order_confirmation_view() { } $delayed_account_creation = ucfirst( get_option( 'woocommerce_enable_delayed_account_creation', 'Yes' ) ); - $this->record_event( 'woocommerceanalytics_order_confirmation_view', array( @@ -106,9 +105,13 @@ public function capture_order_confirmation_view() { 'guest_checkout' => $order->get_customer_id() ? 'No' : 'Yes', 'delayed_account_creation' => $delayed_account_creation, 'oi' => $order->get_id(), - 'order_value' => $order->get_total(), - 'payment_option' => $order->get_payment_method(), + 'order_value' => $order->get_subtotal(), + 'order_total' => $order->get_total(), 'products_count' => $order->get_item_count(), + 'total_discount' => $order->get_discount_total(), + 'total_shipping' => $order->get_shipping_total(), + 'total_tax' => $order->get_total_tax(), + 'payment_option' => $order->get_payment_method(), 'products' => $this->format_items_to_json( $order->get_items() ), 'order_note' => $order->get_customer_note(), 'shipping_option' => $order->get_shipping_method(), @@ -123,7 +126,17 @@ public function capture_order_confirmation_view() { * Track the cart page view */ public function capture_cart_view() { - if ( ! is_cart() ) { + global $post; + $cart_page_id = wc_get_page_id( 'cart' ); + + $is_cart = $cart_page_id && is_page( $cart_page_id ) + || wc_post_content_has_shortcode( 'woocommerce_cart' ) + || has_block( 'woocommerce/cart', $post ) + || apply_filters( 'woocommerce_is_cart', false ) + || Constants::is_defined( 'WOOCOMMERCE_CART' ) + || is_cart(); + + if ( ! $is_cart ) { return; } @@ -146,7 +159,6 @@ public function capture_checkout_view() { $is_checkout = $checkout_page_id && is_page( $checkout_page_id ) || wc_post_content_has_shortcode( 'woocommerce_checkout' ) || has_block( 'woocommerce/checkout', $post ) - || has_block( 'woocommerce/classic-shortcode', $post ) || apply_filters( 'woocommerce_is_checkout', false ) || Constants::is_defined( 'WOOCOMMERCE_CHECKOUT' ); diff --git a/projects/packages/woocommerce-analytics/src/class-universal.php b/projects/packages/woocommerce-analytics/src/class-universal.php index 5a8fb6587b309..713657256420e 100644 --- a/projects/packages/woocommerce-analytics/src/class-universal.php +++ b/projects/packages/woocommerce-analytics/src/class-universal.php @@ -8,7 +8,6 @@ namespace Automattic\Woocommerce_Analytics; use WC_Order; -use WC_Payment_Gateway; use WC_Product; /** @@ -28,16 +27,20 @@ public function init_hooks() { $this->additional_blocks_on_cart_page = $this->get_additional_blocks_on_page( 'cart' ); $this->additional_blocks_on_checkout_page = $this->get_additional_blocks_on_page( 'checkout' ); - // add to carts from non-product pages or lists -- search, store etc. + // delayed events stored in session (can be add_to_carts, product_views...) add_action( 'wp_head', array( $this, 'loop_session_events' ), 2 ); + // Initialize session + add_action( 'send_headers', array( $this, 'initialize_woocommerceanalytics_session' ) ); + // Capture cart events. add_action( 'woocommerce_add_to_cart', array( $this, 'capture_add_to_cart' ), 10, 6 ); - + add_action( 'woocommerce_cart_item_removed', array( $this, 'capture_remove_from_cart' ), 10, 2 ); add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart' ) ); add_action( 'woocommerce_after_mini_cart', array( $this, 'remove_from_cart' ) ); add_action( 'wcct_before_cart_widget', array( $this, 'remove_from_cart' ) ); add_filter( 'woocommerce_cart_item_remove_link', array( $this, 'remove_from_cart_attributes' ), 10, 2 ); + add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart_via_quantity' ), 10, 1 ); // Checkout. // Send events after checkout template (shortcode). @@ -47,7 +50,6 @@ public function init_hooks() { // order confirmed. add_action( 'woocommerce_thankyou', array( $this, 'order_process' ), 10, 1 ); - add_action( 'woocommerce_after_cart', array( $this, 'remove_from_cart_via_quantity' ), 10, 1 ); add_filter( 'woocommerce_checkout_posted_data', array( $this, 'save_checkout_post_data' ), 10, 1 ); @@ -56,6 +58,18 @@ public function init_hooks() { add_action( 'woocommerce_created_customer', array( $this, 'capture_post_checkout_created_customer' ), 10, 2 ); } + /** + * Set a UUID for the current session if is not yet loaded and record the session started event + */ + public function initialize_woocommerceanalytics_session() { + if ( ! isset( $_COOKIE['woocommerceanalytics_session_id'] ) ) { + $session_id = wp_generate_uuid4(); + $this->session_id = $session_id; + setcookie( 'woocommerceanalytics_session_id', $session_id, 0, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true ); + $this->record_event( 'woocommerceanalytics_session_started' ); + } + } + /** * On product lists or other non-product pages, add an event listener to "Add to Cart" button click */ @@ -102,12 +116,26 @@ public function remove_from_cart() { '_en': 'woocommerceanalytics_remove_from_cart', 'pi': productDetails.id, 'pq': productDetails.quantity, " . - $common_props . ' + $common_props . ' } ); } );' ); } + /** + * Capture remove from cart events in mini-cart and cart blocls + * + * @param string $cart_item_key The cart item removed. + * @param \WC_Cart $cart The cart. + * + * @return void + */ + public function capture_remove_from_cart( $cart_item_key, $cart ) { + $item = $cart->removed_cart_contents[ $cart_item_key ] ?? null; + $this->record_event( 'woocommerceanalytics_remove_from_cart' ); + $this->capture_event_in_session_data( (int) $item['product_id'], (int) $item['quantity'], 'woocommerceanalytics_remove_from_cart' ); + } + /** * Adds the product ID to the remove product link (for use by remove_from_cart above) if not present * @@ -182,33 +210,19 @@ public function get_shipping_option_for_item( $cart_item_key ) { */ public function checkout_process() { global $post; - $checkout_page_id = wc_get_page_id( 'checkout' ); - $cart = WC()->cart->get_cart(); - - $enabled_payment_options = array_filter( - WC()->payment_gateways->get_available_payment_gateways(), - function ( $payment_gateway ) { - if ( ! $payment_gateway instanceof WC_Payment_Gateway ) { - return false; - } - - return $payment_gateway->is_available(); - } - ); - - $enabled_payment_options = array_keys( $enabled_payment_options ); - + $checkout_page_id = wc_get_page_id( 'checkout' ); + $cart = WC()->cart->get_cart(); $is_in_checkout_page = $checkout_page_id === $post->ID ? 'Yes' : 'No'; $session = WC()->session; if ( is_object( $session ) ) { - $session->set( 'checkout_page_used', true ); + $session->set( 'checkout_page_used', 'Yes' === $is_in_checkout_page ); $session->save_data(); } foreach ( $cart as $cart_item_key => $cart_item ) { /** - * This filter is already documented in woocommerce/templates/cart/cart.php - */ + * This filter is already documented in woocommerce/templates/cart/cart.php + */ $product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key ); if ( ! $product || ! $product instanceof WC_Product ) { @@ -228,66 +242,7 @@ function ( $payment_gateway ) { } $data['pq'] = $cart_item['quantity']; - - $properties = $this->process_event_properties( - 'woocommerceanalytics_product_checkout', - $data, - $product->get_id() - ); - - wc_enqueue_js( - " - var cartItem_{$cart_item_key}_logged = false; - var properties = {$properties}; - // Check if jQuery is available - if ( typeof jQuery !== 'undefined' ) { - // This is only triggered on the checkout shortcode. - jQuery( document.body ).on( 'init_checkout', function () { - if ( true === cartItem_{$cart_item_key}_logged ) { - return; - } - if ( typeof wp !== 'undefined' && typeof wp.hooks !== 'undefined' && typeof wp.hooks.addAction === 'function' ) { - wp.hooks.addAction( 'wcpay.payment-request.availability', 'wcpay', function ( args ) { - properties.express_checkout = args.paymentRequestType; - } ); - } - properties.checkout_page_contains_checkout_block = '0'; - properties.checkout_page_contains_checkout_shortcode = '1'; - - _wca.push( properties ); - cartItem_{$cart_item_key}_logged = true; - - } ); - } - - if ( - typeof wp !== 'undefined' && - typeof wp.data !== 'undefined' && - typeof wp.data.subscribe !== 'undefined' - ) { - wp.data.subscribe( function () { - if ( true === cartItem_{$cart_item_key}_logged ) { - return; - } - - const checkoutDataStore = wp.data.select( 'wc/store/checkout' ); - // Ensures we're not in Cart, but in Checkout page. - if ( - typeof checkoutDataStore !== 'undefined' && - checkoutDataStore.getOrderId() !== 0 - ) { - properties.express_checkout = Object.keys( wc.wcBlocksRegistry.getExpressPaymentMethods() ); - properties.checkout_page_contains_checkout_block = '1'; - properties.checkout_page_contains_checkout_shortcode = '0'; - - _wca.push( properties ); - cartItem_{$cart_item_key}_logged = true; - } - } ); - } - " - ); - + $this->record_event( 'woocommerceanalytics_product_checkout', $data, $product->get_id() ); } } @@ -333,8 +288,8 @@ public function order_process( $order_id ) { } } - $checkout_page_contains_checkout_block = '0'; - $checkout_page_contains_checkout_shortcode = '0'; + $checkout_page_contains_checkout_block = '0'; + $checkout_page_contains_checkout_shortcode = '0'; $order_source = $order->get_created_via(); if ( 'store-api' === $order_source ) { @@ -370,9 +325,13 @@ public function order_process( $order_id ) { 'guest_checkout' => $guest_checkout, 'delayed_account_creation' => $delayed_account_creation, 'express_checkout' => $express_checkout, - 'products_count' => $order_items_count, 'coupon_used' => $order_coupons_count, - 'order_value' => $order->get_total(), + 'products_count' => $order_items_count, + 'order_value' => $order->get_subtotal(), + 'order_total' => $order->get_total(), + 'total_discount' => $order->get_discount_total(), + 'total_taxes' => $order->get_total_tax(), + 'total_shipping' => $order->get_shipping_total(), 'from_checkout' => $checkout_page_used, 'checkout_page_contains_checkout_block' => $checkout_page_contains_checkout_block, 'checkout_page_contains_checkout_shortcode' => $checkout_page_contains_checkout_shortcode, @@ -393,20 +352,30 @@ public function remove_from_cart_via_quantity() { wc_enqueue_js( " - jQuery( 'button[name=update_cart]' ).on( 'click', function() { - var cartItems = jQuery( '.cart_item' ); - cartItems.each( function( item ) { - var qty = jQuery( this ).find( 'input.qty' ); - if ( qty && qty.val() === '0' ) { - var productID = jQuery( this ).find( '.product-remove a' ).data( 'product_id' ); + function trigger_cart_remove() { + let cartItems = document.querySelectorAll( '.cart_item' ); + [...cartItems].forEach( function( item ) { + let qtyInput = item.querySelector('input.qty'); + if ( qtyInput && qtyInput.value === '0' ) { + let productRemoveLink = item.querySelector('.product-remove a'); + let productID = productRemoveLink ? productRemoveLink.dataset.product_id : null; _wca.push( { '_en': 'woocommerceanalytics_remove_from_cart', 'pi': productID, " . - $common_props . ' + $common_props . " } ); } } ); - } );' + } + + document.querySelector( 'button[name=update_cart]' ).addEventListener( 'click', trigger_cart_remove ); + + // The duplicated listener is needed because updated_wc_div replaces all the DOM and then the initial listener stops working. + document.body.onupdated_wc_div = function () { + document.querySelector( 'button[name=update_cart]' ).addEventListener( 'click', trigger_cart_remove ); + }; + + " ); } diff --git a/projects/packages/woocommerce-analytics/src/class-woo-analytics-trait.php b/projects/packages/woocommerce-analytics/src/class-woo-analytics-trait.php index 1d3a6658001e1..b7bcf0b3446f4 100644 --- a/projects/packages/woocommerce-analytics/src/class-woo-analytics-trait.php +++ b/projects/packages/woocommerce-analytics/src/class-woo-analytics-trait.php @@ -52,6 +52,13 @@ trait Woo_Analytics_Trait { */ protected $additional_blocks_on_checkout_page; + /** + * Session ID. + * + * @var string + */ + protected $session_id; + /** * Format Cart Items or Order Items to an array * @@ -112,15 +119,12 @@ function ( $payment_gateway ) { ); $enabled_payment_options = array_keys( $enabled_payment_options ); - $cart_total = wc_prices_include_tax() ? $cart->get_cart_contents_total() + $cart->get_cart_contents_tax() : $cart->get_cart_contents_total(); $shared_data = array( 'products' => $this->format_items_to_json( $cart->get_cart() ), 'create_account' => $create_account, 'guest_checkout' => $guest_checkout, 'delayed_account_creation' => $delayed_account_creation, 'express_checkout' => 'null', // TODO: not solved yet. - 'products_count' => $cart->get_cart_contents_count(), - 'order_value' => $cart_total, 'shipping_options_count' => 'null', // TODO: not solved yet. 'coupon_used' => $coupon_used, 'payment_options' => $enabled_payment_options, @@ -253,17 +257,27 @@ public function find_cart_checkout_content_sources() { */ public function get_common_properties() { $site_info = array( + 'session_id' => sanitize_text_field( wp_unslash( $_COOKIE['woocommerceanalytics_session_id'] ?? $this->session_id ) ), 'blog_id' => Jetpack_Connection::get_site_id(), 'store_id' => defined( '\\WC_Install::STORE_ID_OPTION' ) ? get_option( \WC_Install::STORE_ID_OPTION ) : false, 'ui' => $this->get_user_id(), 'url' => home_url(), 'woo_version' => WC()->version, + 'wp_version' => get_bloginfo( 'version' ), 'store_admin' => in_array( array( 'administrator', 'shop_manager' ), wp_get_current_user()->roles, true ) ? 1 : 0, 'device' => wp_is_mobile() ? 'mobile' : 'desktop', 'template_used' => $this->cart_checkout_templates_in_use ? '1' : '0', 'additional_blocks_on_cart_page' => $this->additional_blocks_on_cart_page, 'additional_blocks_on_checkout_page' => $this->additional_blocks_on_checkout_page, 'store_currency' => get_woocommerce_currency(), + 'timezone' => wp_timezone_string(), + 'is_guest' => $this->get_user_id() === null, + 'order_value' => $this->get_cart_subtotal(), + 'order_total' => $this->get_cart_total(), + 'total_tax' => $this->get_cart_taxes(), + 'total_discount' => $this->get_total_discounts(), + 'total_shipping' => $this->get_cart_shipping_total(), + 'products_count' => $this->get_cart_items_count(), ); $cart_checkout_info = $this->get_cart_checkout_info(); return array_merge( $site_info, $cart_checkout_info ); @@ -404,7 +418,7 @@ public function get_user_id() { $userid = get_current_user_id(); return $blogid . ':' . $userid; } - return 'null'; + return null; } /** @@ -489,7 +503,7 @@ public function get_cart_page_block_usage() { $content = $this->cart_content_source; $block_presence = str_contains( $content, '<)Pq6JxCzS5C6GBU8zhK&9Km|W9b07_gn(0 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 literal 0 HcmV?d00001 diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php index cabfcca1dfe58..6f366cd924c11 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php @@ -195,12 +195,8 @@ function wpcom_admin_get_user_option_jetpack( $value ) { * Hides the "View" switcher on WP Admin screens enforced by the "Remove duplicate views" experiment. */ function wpcom_duplicate_views_hide_view_switcher() { - if ( ! function_exists( '\Automattic\Jetpack\Masterbar\get_admin_menu_class' ) || ! function_exists( '\Automattic\Jetpack\Masterbar\should_customize_nav' ) ) { - return; - } - - $admin_menu_class = apply_filters( 'jetpack_admin_menu_class', \Automattic\Jetpack\Masterbar\get_admin_menu_class() ); - if ( \Automattic\Jetpack\Masterbar\should_customize_nav( $admin_menu_class ) ) { + $admin_menu_class = wpcom_get_custom_admin_menu_class(); + if ( $admin_menu_class ) { $admin_menu = $admin_menu_class::get_instance(); $current_screen = wpcom_admin_get_current_screen(); @@ -435,3 +431,100 @@ function wpcom_is_duplicate_views_experiment_enabled() { return $is_enabled; } } + +/** + * Displays a notice when a user visits the enforced WP Admin view of a removed Calypso screen for + * the first time. + */ +function wpcom_show_removed_calypso_screen_notice() { + $admin_menu_class = wpcom_get_custom_admin_menu_class(); + if ( ! $admin_menu_class ) { + return; + } + + $current_screen = wpcom_admin_get_current_screen(); + + if ( ! in_array( $current_screen, WPCOM_DUPLICATED_VIEW, true ) ) { + return; + } + + $dismissed_notices = get_user_option( 'wpcom_removed_calypso_screen_dismissed_notices' ); + if ( is_array( $dismissed_notices ) && in_array( $current_screen, $dismissed_notices, true ) ) { + return; + } + + if ( ! wpcom_is_duplicate_views_experiment_enabled() ) { + return; + } + + remove_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' ); + $uses_wp_admin_interface = get_option( 'wpcom_admin_interface' ) === 'wp-admin'; + add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option', 10 ); + if ( $uses_wp_admin_interface ) { + return; + } + + remove_filter( 'get_user_option_jetpack_admin_menu_preferred_views', 'wpcom_admin_get_user_option_jetpack' ); + $preferred_views = get_user_option( 'jetpack_admin_menu_preferred_views' ); + add_filter( 'get_user_option_jetpack_admin_menu_preferred_views', 'wpcom_admin_get_user_option_jetpack' ); + if ( ! empty( $preferred_views ) && isset( $preferred_views[ $current_screen ] ) && $preferred_views[ $current_screen ] === 'classic' ) { + return; + } + + $handle = jetpack_mu_wpcom_enqueue_assets( 'removed-calypso-screen-notice', array( 'js', 'css' ) ); + wp_set_script_translations( $handle, 'jetpack-mu-wpcom', Jetpack_Mu_Wpcom::PKG_DIR . 'languages' ); + + global $title; + $config = wp_json_encode( + array( + 'imageUrl' => plugins_url( 'screens/' . sanitize_title( $current_screen ) . '.webp', __FILE__ ), + 'title' => $title, + 'screen' => $current_screen, + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'dismissNonce' => wp_create_nonce( 'wpcom_dismiss_removed_calypso_screen_notice' ), + ) + ); + + wp_add_inline_script( + $handle, + "window.removedCalypsoScreenNoticeConfig = $config;", + 'before' + ); +} +add_action( 'admin_enqueue_scripts', 'wpcom_show_removed_calypso_screen_notice' ); + +/** + * Gets the name of the class used to customize the admin menu when Nav Unification is enabled. + * + * @return false|string The class name of the customized admin menu if any, false otherwise. + */ +function wpcom_get_custom_admin_menu_class() { + if ( ! function_exists( '\Automattic\Jetpack\Masterbar\get_admin_menu_class' ) || ! function_exists( '\Automattic\Jetpack\Masterbar\should_customize_nav' ) ) { + return false; + } + + $admin_menu_class = apply_filters( 'jetpack_admin_menu_class', \Automattic\Jetpack\Masterbar\get_admin_menu_class() ); + if ( ! \Automattic\Jetpack\Masterbar\should_customize_nav( $admin_menu_class ) ) { + return false; + } + + return $admin_menu_class; +} + +/** + * Handles the AJAX request to dismiss a notice of a removed Calypsos screen. + */ +function wpcom_dismiss_removed_calypso_screen_notice() { + check_ajax_referer( 'wpcom_dismiss_removed_calypso_screen_notice' ); + if ( isset( $_REQUEST['screen'] ) ) { + $screen = sanitize_text_field( wp_unslash( $_REQUEST['screen'] ) ); + $dismissed_notices = get_user_option( 'wpcom_removed_calypso_screen_dismissed_notices' ); + if ( ! is_array( $dismissed_notices ) ) { + $dismissed_notices = array(); + } + $dismissed_notices[] = $screen; + update_user_option( get_current_user_id(), 'wpcom_removed_calypso_screen_dismissed_notices', $dismissed_notices, true ); + } + wp_die(); +} +add_action( 'wp_ajax_wpcom_dismiss_removed_calypso_screen_notice', 'wpcom_dismiss_removed_calypso_screen_notice' ); diff --git a/projects/packages/jetpack-mu-wpcom/webpack.config.js b/projects/packages/jetpack-mu-wpcom/webpack.config.js index 3386412b45d23..b4ebf4c88b1d2 100644 --- a/projects/packages/jetpack-mu-wpcom/webpack.config.js +++ b/projects/packages/jetpack-mu-wpcom/webpack.config.js @@ -51,6 +51,8 @@ module.exports = [ './src/features/wpcom-profile-settings/profile-settings-link-to-wpcom.ts', 'wpcom-sidebar-notice': './src/features/wpcom-sidebar-notice/wpcom-sidebar-notice.js', 'starter-page-templates': './src/features/starter-page-templates/index.tsx', + 'removed-calypso-screen-notice': + './src/features/wpcom-admin-interface/removed-calypso-screen-notice.tsx', }, mode: jetpackWebpackConfig.mode, devtool: jetpackWebpackConfig.devtool, diff --git a/projects/packages/masterbar/changelog/update-remove-duplicate-views-notices b/projects/packages/masterbar/changelog/update-remove-duplicate-views-notices new file mode 100644 index 0000000000000..a1247b2d6cf66 --- /dev/null +++ b/projects/packages/masterbar/changelog/update-remove-duplicate-views-notices @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Persist only preferred views from option, skip experiment hooks + + diff --git a/projects/packages/masterbar/src/admin-menu/class-base-admin-menu.php b/projects/packages/masterbar/src/admin-menu/class-base-admin-menu.php index 3a5dea1f05b4b..65400fff1d1bc 100644 --- a/projects/packages/masterbar/src/admin-menu/class-base-admin-menu.php +++ b/projects/packages/masterbar/src/admin-menu/class-base-admin-menu.php @@ -602,7 +602,12 @@ public function dashboard_switcher_scripts() { * @param string $view Preferred view. */ public function set_preferred_view( $screen, $view ) { - $preferred_views = $this->get_preferred_views(); + remove_filter( 'get_user_option_jetpack_admin_menu_preferred_views', 'wpcom_admin_get_user_option_jetpack' ); + $preferred_views = $this->get_preferred_views(); + if ( function_exists( 'wpcom_admin_get_user_option_jetpack' ) ) { + add_filter( 'get_user_option_jetpack_admin_menu_preferred_views', 'wpcom_admin_get_user_option_jetpack' ); + } + $screen = str_replace( '?post_type=post', '', $screen ); $preferred_views[ $screen ] = $view; update_user_option( get_current_user_id(), 'jetpack_admin_menu_preferred_views', $preferred_views ); From f02d391fe6b479d8f19cb657fffbbf14c7d6b2c7 Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Wed, 18 Dec 2024 16:41:31 +0700 Subject: [PATCH 22/63] wpcom-block-editor-nux: avoid using useLocation which throws exception outside Site Editor in GB 19.9.0 (#40656) --- .../changelog/fix-uselocation-exception | 4 +++ .../src/common/hooks/use-canvas-mode.ts | 31 +++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/fix-uselocation-exception diff --git a/projects/packages/jetpack-mu-wpcom/changelog/fix-uselocation-exception b/projects/packages/jetpack-mu-wpcom/changelog/fix-uselocation-exception new file mode 100644 index 0000000000000..fc95eb7085776 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/fix-uselocation-exception @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +wpcom-block-editor-nux: avoid using useLocation which now throws exception outside Site Editor in Gutenberg 19.9.0 diff --git a/projects/packages/jetpack-mu-wpcom/src/common/hooks/use-canvas-mode.ts b/projects/packages/jetpack-mu-wpcom/src/common/hooks/use-canvas-mode.ts index fba0a4cbdf2c7..d4c1332de6386 100644 --- a/projects/packages/jetpack-mu-wpcom/src/common/hooks/use-canvas-mode.ts +++ b/projects/packages/jetpack-mu-wpcom/src/common/hooks/use-canvas-mode.ts @@ -1,20 +1,25 @@ -import { useSelect } from '@wordpress/data'; -import useLocation from './use-location'; +import { subscribe, useSelect } from '@wordpress/data'; +import { useEffect, useState } from 'react'; const useCanvasMode = () => { - const location = useLocation(); + const [ canvasMode, setCanvasMode ] = useState< string | null >( null ); + const isSiteEditor = useSelect( select => !! select( 'core/edit-site' ), [] ); - return useSelect( - select => { - // The canvas mode is limited to the site editor. - if ( ! select( 'core/edit-site' ) ) { - return null; - } + useEffect( () => { + // The canvas mode is limited to the site editor. + if ( ! isSiteEditor ) { + return; + } - return new URLSearchParams( location?.search ).get( 'canvas' ) || 'view'; - }, - [ location?.search ] - ); + const unsubscribe = subscribe( () => { + const mode = new URLSearchParams( window.location?.search ).get( 'canvas' ) || 'view'; + setCanvasMode( mode ); + } ); + + return () => unsubscribe(); + }, [ isSiteEditor ] ); + + return canvasMode; }; export default useCanvasMode; From f52f223ae2f9115e4dbbfb58b6b513da83953c75 Mon Sep 17 00:00:00 2001 From: Bogdan Ungureanu Date: Wed, 18 Dec 2024 12:26:10 +0200 Subject: [PATCH 23/63] Remove duplicate views: Fix the Stats and Promote with blaze pages (#40630) * Fix the Stats and Promote with blaze pages * Update fix-wp-admin-post-fixes Update the comment --- .../jetpack-mu-wpcom/changelog/fix-wp-admin-post-fixes | 5 +++++ .../features/wpcom-admin-interface/wpcom-admin-interface.php | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/fix-wp-admin-post-fixes diff --git a/projects/packages/jetpack-mu-wpcom/changelog/fix-wp-admin-post-fixes b/projects/packages/jetpack-mu-wpcom/changelog/fix-wp-admin-post-fixes new file mode 100644 index 0000000000000..bbf2e7375d50c --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/fix-wp-admin-post-fixes @@ -0,0 +1,5 @@ +Significance: patch +Type: fixed +Comment: Fix the stats and promote with blaze quicklinks in edit.php for Simple sites that are part of the remove duplicate views experiment + + diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php index 6f366cd924c11..fb7af7b332ac0 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php @@ -118,6 +118,8 @@ function ( $location ) { const WPCOM_DUPLICATED_VIEW = array( 'edit.php', + 'admin.php?page=stats', + 'tools.php?page=advertising', 'edit.php?post_type=jetpack-portfolio', 'edit.php?post_type=jetpack-testimonial', 'edit-tags.php?taxonomy=category', From 64ab237e7488de4ca2656951bc66314a05351a9a Mon Sep 17 00:00:00 2001 From: Karen Attfield Date: Wed, 18 Dec 2024 15:28:33 +0000 Subject: [PATCH 24/63] Photon: Ensure VIP site are correctly identified within isVIP. (#39941) Co-authored-by: Sergey Mitroshin --- .../jetpack/changelog/fix-photon-on-vip | 4 ++++ .../jetpack/class.jetpack-gutenberg.php | 4 ++++ .../blocks/tiled-gallery/tiled-gallery.php | 3 ++- .../blocks/tiled-gallery/utils/index.js | 21 +++++++++++++++++-- 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/fix-photon-on-vip diff --git a/projects/plugins/jetpack/changelog/fix-photon-on-vip b/projects/plugins/jetpack/changelog/fix-photon-on-vip new file mode 100644 index 0000000000000..5d205154e23f6 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-photon-on-vip @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Photon: Ensure VIP sites are correctly identified, to prevent unnecessary early Photonization of URLs. diff --git a/projects/plugins/jetpack/class.jetpack-gutenberg.php b/projects/plugins/jetpack/class.jetpack-gutenberg.php index ee8d7ddb4e3cc..aede14da9b1e1 100644 --- a/projects/plugins/jetpack/class.jetpack-gutenberg.php +++ b/projects/plugins/jetpack/class.jetpack-gutenberg.php @@ -715,6 +715,7 @@ public static function enqueue_block_editor_assets() { $modules = $module_list_endpoint->get_modules(); } + $jetpack_plan = Jetpack_Plan::get(); $initial_state = array( 'available_blocks' => self::get_availability(), 'blocks_variation' => $blocks_variation, @@ -731,6 +732,9 @@ public static function enqueue_block_editor_assets() { // this is the equivalent of JP initial state siteData.showMyJetpack (class-jetpack-redux-state-helper) // used to determine if we can link to My Jetpack from the block editor 'is_my_jetpack_available' => My_Jetpack_Initializer::should_initialize(), + 'jetpack_plan' => array( + 'data' => $jetpack_plan['product_slug'], + ), /** * Enable the RePublicize UI in the block editor context. * diff --git a/projects/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php b/projects/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php index 3be4604368cef..33b925fc2f0f5 100644 --- a/projects/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php +++ b/projects/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php @@ -58,7 +58,8 @@ public static function render( $attr, $content ) { Jetpack_Gutenberg::load_assets_as_required( __DIR__ ); $is_squareish_layout = self::is_squareish_layout( $attr ); - + // For backward compatibility (ensuring Tiled Galleries using now deprecated versions of the block are not affected). + // See isVIP() in utils/index.js. $jetpack_plan = Jetpack_Plan::get(); wp_localize_script( 'jetpack-gallery-settings', 'jetpack_plan', array( 'data' => $jetpack_plan['product_slug'] ) ); diff --git a/projects/plugins/jetpack/extensions/blocks/tiled-gallery/utils/index.js b/projects/plugins/jetpack/extensions/blocks/tiled-gallery/utils/index.js index 9a4e4f2ac3c6e..3de6ec8bdd0df 100644 --- a/projects/plugins/jetpack/extensions/blocks/tiled-gallery/utils/index.js +++ b/projects/plugins/jetpack/extensions/blocks/tiled-gallery/utils/index.js @@ -4,11 +4,23 @@ import { isSimpleSite, } from '@automattic/jetpack-shared-extension-utils'; import { isBlobURL } from '@wordpress/blob'; +import { select } from '@wordpress/data'; import { range } from 'lodash'; import photon from 'photon'; import isOfflineMode from '../../../shared/is-offline-mode'; import { PHOTON_MAX_RESIZE } from '../constants'; +let jetpackPlanFromState; + +window.addEventListener( 'load', function () { + const hasImageCompare = select( 'core/block-editor' ) + .getBlocks() + .some( block => block.name === 'jetpack/image-compare' ); + if ( hasImageCompare && ! jetpackPlanFromState ) { + jetpackPlanFromState = window?.Jetpack_Editor_Initial_State?.jetpack?.jetpack_plan; + } +} ); + export function isSquareishLayout( layout ) { return [ 'circle', 'square' ].includes( layout ); } @@ -112,9 +124,14 @@ export function photonizedImgProps( img, galleryAtts = {} ) { } function isVIP() { /*global jetpack_plan*/ - if ( typeof jetpack_plan !== 'undefined' && jetpack_plan.data === 'vip' ) { - return true; + // Use `jetpackPlanFromState` if available, otherwise fall back to `jetpack_plan` defined within the render function in tiled-gallery.php. + let jetpackPlan; + if ( typeof jetpackPlanFromState !== 'undefined' ) { + jetpackPlan = jetpackPlanFromState; + } else if ( typeof jetpack_plan !== 'undefined' ) { + jetpackPlan = jetpack_plan; } + return jetpackPlan && jetpackPlan?.data === 'vip'; } /** From 829b8dc6b330ada0ceec66f9236d31721375542b Mon Sep 17 00:00:00 2001 From: Karen Attfield Date: Wed, 18 Dec 2024 15:28:51 +0000 Subject: [PATCH 25/63] Restaurant Menu CPT: Replace most jQuery with Javascript (#40645) --- .../changelog/update-nova-cpt-jquery-to-js | 4 + .../custom-post-types/js/many-items.js | 207 ++++++++++-------- .../custom-post-types/js/menu-checkboxes.js | 67 ++++-- .../modules/custom-post-types/nova.php | 4 +- 4 files changed, 173 insertions(+), 109 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/update-nova-cpt-jquery-to-js diff --git a/projects/plugins/jetpack/changelog/update-nova-cpt-jquery-to-js b/projects/plugins/jetpack/changelog/update-nova-cpt-jquery-to-js new file mode 100644 index 0000000000000..a3ef11151211c --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-nova-cpt-jquery-to-js @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Restaurant Menu CPT: Convert much of the jQuery usage to JavaScript diff --git a/projects/plugins/jetpack/modules/custom-post-types/js/many-items.js b/projects/plugins/jetpack/modules/custom-post-types/js/many-items.js index 09c7ad911f091..4a3903dc04a05 100644 --- a/projects/plugins/jetpack/modules/custom-post-types/js/many-items.js +++ b/projects/plugins/jetpack/modules/custom-post-types/js/many-items.js @@ -1,111 +1,144 @@ -( function ( $ ) { - var menuSelector, nonceInput, methods; - - methods = { - init: function ( /*options*/ ) { - var $this = this, - tbody, - row; - - this.on( 'keypress.manyItemsTable', function ( event ) { - if ( 13 !== event.which ) { - return; - } +( function () { + let menuSelector, nonceInput; + const initializedTables = new Set(); + + const methods = { + init: function ( table ) { + let tbody = table.lastElementChild; + while ( tbody && tbody.tagName !== 'TBODY' ) { + tbody = tbody.previousElementSibling; + } + const row = tbody.querySelector( 'tr:first-child' ).cloneNode( true ); + + table.dataset.form = table.closest( 'form' ); + table.dataset.tbody = tbody; + table.dataset.row = row; + table.dataset.currentRow = row; + + menuSelector = document.getElementById( 'nova-menu-tax' ); + nonceInput = document.getElementById( '_wpnonce' ); + + table.addEventListener( 'keypress', function ( event ) { + if ( event.which !== 13 ) return; event.preventDefault(); - if ( 'function' === typeof FormData ) { - methods.submitRow.apply( $this ); + if ( typeof FormData === 'function' ) { + methods.submitRow.call( table ); } - methods.addRow.apply( $this ); - } ).on( 'focus.manyItemsTable', ':input', function ( /*event*/ ) { - $this.data( 'currentRow', $( this ).parents( 'tr:first' ) ); + methods.addRow.call( table ); } ); - tbody = this.find( 'tbody:last' ); - row = tbody.find( 'tr:first' ).clone(); - - this.data( 'form', this.parents( 'form:first' ) ); - this.data( 'tbody', tbody ); - this.data( 'row', row ); - this.data( 'currentRow', row ); - - menuSelector = $( '#nova-menu-tax' ); - nonceInput = $( '#_wpnonce' ); + table.addEventListener( 'focusin', function ( event ) { + if ( event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA' ) { + table.dataset.currentRow = event.target.closest( 'tr' ); + } + } ); - return this; + initializedTables.add( table ); + return table; }, - destroy: function () { - this.off( '.manyItemsTable' ); - - return this; + destroy: function ( table ) { + if ( this.observer ) { + this.observer.disconnect(); + } + table.removeEventListener( 'keypress', methods.keypressHandler ); + table.removeEventListener( 'focusin', methods.focusinHandler ); + initializedTables.delete( table ); + return table; }, - submitRow: function () { - var submittedRow, currentInputs, allInputs, partialFormData; + submitRow: function ( table ) { + const submittedRow = table.dataset.currentRow; + const currentInputs = submittedRow.querySelectorAll( 'input, textarea, select' ); + const form = document.querySelector( table.dataset.form ); + const allInputs = Array.from( form.querySelectorAll( 'input, textarea, select' ) ); - submittedRow = this.data( 'currentRow' ); - currentInputs = submittedRow.find( ':input' ); - allInputs = this.data( 'form' ) - .find( ':input' ) - .not( currentInputs ) - .attr( 'disabled', true ) - .end(); + currentInputs.forEach( input => ( input.disabled = true ) ); + allInputs + .filter( input => ! currentInputs.includes( input ) ) + .forEach( input => ( input.disabled = true ) ); - partialFormData = new FormData( this.data( 'form' ).get( 0 ) ); + const partialFormData = new FormData( form ); partialFormData.append( 'ajax', '1' ); - partialFormData.append( 'nova_menu_tax', menuSelector.val() ); - partialFormData.append( '_wpnonce', nonceInput.val() ); - - allInputs.attr( 'disabled', false ); - - $.ajax( { - url: '', - type: 'POST', - data: partialFormData, - processData: false, - contentType: false, - } ).complete( function ( xhr ) { - submittedRow.html( xhr.responseText ); - } ); + partialFormData.append( 'nova_menu_tax', menuSelector.value ); + partialFormData.append( '_wpnonce', nonceInput.value ); - currentInputs.attr( 'disabled', true ); + fetch( '', { + method: 'POST', + body: partialFormData, + } ) + .then( response => response.text() ) + .then( responseText => { + submittedRow.innerHTML = responseText; + } ); - return this; + allInputs.forEach( input => ( input.disabled = false ) ); + + return table; }, - addRow: function () { - var row = this.data( 'row' ).clone(); - row.appendTo( this.data( 'tbody' ) ); - row.find( ':input:first' ).focus(); + addRow: function ( table ) { + const row = table.dataset.row.cloneNode( true ); + + const tbody = table.dataset.tbody; + tbody.appendChild( row ); + + const firstInput = row.querySelector( 'input, textarea, select' ); + if ( firstInput ) firstInput.focus(); - return this; + return table; }, - }; - $.fn.manyItemsTable = function ( method ) { - // Method calling logic - if ( methods[ method ] ) { - return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) ); - } else if ( typeof method === 'object' || ! method ) { - return methods.init.apply( this, arguments ); - } - $.error( 'Method ' + method + ' does not exist on jQuery.manyItemsTable' ); - return this; + clickAddRow: function ( table ) { + let tbody = table.lastElementChild; + + while ( tbody && tbody.tagName !== 'TBODY' ) { + tbody = tbody.previousElementSibling; + } + const row = tbody.querySelector( 'tr:first-child' ).cloneNode( true ); + + row.querySelectorAll( 'input, textarea' ).forEach( input => { + input.value = ''; + } ); + + tbody.appendChild( row ); + }, }; - $.fn.clickAddRow = function () { - var tbody = this.find( 'tbody:last' ), - row = tbody.find( 'tr:first' ).clone(); + const observeTableRemoval = function ( list ) { + const observer = new MutationObserver( mutations => { + mutations.forEach( mutation => { + mutation.removedNodes.forEach( node => { + if ( node.matches && node.matches( '.many-items-table' ) ) { + methods.destroy( node ); + } + } ); + } ); + } ); - $( row ).find( 'input, textarea' ).val( '' ); - $( row ).appendTo( tbody ); + observer.observe( list, { childList: true, subtree: true } ); }; -} )( jQuery ); - -jQuery( '.many-items-table' ).one( 'focus', ':input', function ( event ) { - jQuery( event.delegateTarget ).manyItemsTable(); -} ); -jQuery( '.many-items-table' ).on( 'click', 'a.nova-new-row', function ( event ) { - jQuery( event.delegateTarget ).clickAddRow(); -} ); + + // Initialization for many-items-table + document.addEventListener( 'focusin', event => { + const table = event.target.closest( '.many-items-table' ); + if ( table && ! initializedTables.has( table ) ) { + methods.init( table ); + } + } ); + + document.addEventListener( 'click', event => { + if ( event.target.matches( 'a.nova-new-row' ) ) { + const table = event.target.closest( '.many-items-table' ); + if ( table ) { + event.preventDefault(); + methods.clickAddRow( table ); + } + } + } ); + const list = document.querySelector( '#the-list' ); // Scope to the specific table + if ( list ) { + observeTableRemoval( list ); + } +} )(); diff --git a/projects/plugins/jetpack/modules/custom-post-types/js/menu-checkboxes.js b/projects/plugins/jetpack/modules/custom-post-types/js/menu-checkboxes.js index b422eff4e24eb..ed3418d76ccdf 100644 --- a/projects/plugins/jetpack/modules/custom-post-types/js/menu-checkboxes.js +++ b/projects/plugins/jetpack/modules/custom-post-types/js/menu-checkboxes.js @@ -1,48 +1,75 @@ -( function ( $ ) { - var NovaCheckBoxes = { +( function () { + const NovaCheckBoxes = { inputs: null, popInputs: null, initialize: function () { - NovaCheckBoxes.popInputs = $( '#nova_menuchecklist-pop' ).find( ':checkbox' ); + // Get all checkboxes in the "nova_menuchecklist-pop" + NovaCheckBoxes.popInputs = document.querySelectorAll( + '#nova_menuchecklist-pop input[type="checkbox"]' + ); - NovaCheckBoxes.inputs = $( '#nova_menuchecklist' ) - .find( ':checkbox' ) - .change( NovaCheckBoxes.checkOne ) - .change( NovaCheckBoxes.syncPop ); + // Get all checkboxes in the "nova_menuchecklist" and add event listeners + NovaCheckBoxes.inputs = document.querySelectorAll( + '#nova_menuchecklist input[type="checkbox"]' + ); + NovaCheckBoxes.inputs.forEach( input => { + input.addEventListener( 'change', NovaCheckBoxes.checkOne ); + input.addEventListener( 'change', NovaCheckBoxes.syncPop ); + } ); + // If no checkboxes are checked, check the first one if ( ! NovaCheckBoxes.isChecked() ) { NovaCheckBoxes.checkFirst(); } + // Sync the state of the "pop" inputs NovaCheckBoxes.syncPop(); }, syncPop: function () { - NovaCheckBoxes.popInputs.each( function () { - var $this = $( this ); - $this.prop( 'checked', $( '#in-nova_menu-' + $this.val() ).is( ':checked' ) ); + NovaCheckBoxes.popInputs.forEach( popInput => { + const linkedInput = document.querySelector( `#in-nova_menu-${ popInput.value }` ); + popInput.checked = linkedInput ? linkedInput.checked : false; } ); }, isChecked: function () { - return NovaCheckBoxes.inputs.is( ':checked' ); + return Array.from( NovaCheckBoxes.inputs ).some( input => input.checked ); }, checkFirst: function () { - NovaCheckBoxes.inputs.first().prop( 'checked', true ); + const firstInput = NovaCheckBoxes.inputs[ 0 ]; + if ( firstInput ) { + firstInput.checked = true; + } }, - checkOne: function ( /*event*/ ) { - if ( $( this ).is( ':checked' ) ) { - return NovaCheckBoxes.inputs.not( this ).prop( 'checked', false ); + checkOne: function () { + const currentInput = this; + + // If the current checkbox is checked, uncheck all other checkboxes + if ( currentInput.checked ) { + NovaCheckBoxes.inputs.forEach( input => { + if ( input !== currentInput ) { + input.checked = false; + } + } ); + return; } - if ( $( this ).closest( '#nova_menuchecklist' ).find( ':checked' ).length > 0 ) { - return $( this ).prop( 'checked', false ); + const checklist = document.querySelector( '#nova_menuchecklist' ); + + // If at least one checkbox is still checked, uncheck the current one + if ( checklist.querySelectorAll( 'input[type="checkbox"]:checked' ).length > 0 ) { + currentInput.checked = false; + return; } - return NovaCheckBoxes.checkFirst(); + + // Otherwise, check the first checkbox + NovaCheckBoxes.checkFirst(); }, }; - $( NovaCheckBoxes.initialize ); -} )( jQuery ); + // Initialize when the DOM is fully loaded + document.addEventListener( 'DOMContentLoaded', NovaCheckBoxes.initialize ); +} )(); diff --git a/projects/plugins/jetpack/modules/custom-post-types/nova.php b/projects/plugins/jetpack/modules/custom-post-types/nova.php index a5299f4c59fa3..69256670c29aa 100644 --- a/projects/plugins/jetpack/modules/custom-post-types/nova.php +++ b/projects/plugins/jetpack/modules/custom-post-types/nova.php @@ -584,7 +584,7 @@ public function add_admin_menus() { '_inc/build/custom-post-types/js/menu-checkboxes.min.js', 'modules/custom-post-types/js/menu-checkboxes.js' ), - array( 'jquery' ), + array(), $this->version, true ); @@ -1146,7 +1146,7 @@ public function enqueue_many_items_scripts() { '_inc/build/custom-post-types/js/many-items.min.js', 'modules/custom-post-types/js/many-items.js' ), - array( 'jquery' ), + array(), $this->version, true ); From 832e258dc6a097d7de53be27cb6db08a5ea7fc40 Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Wed, 18 Dec 2024 23:19:27 +0700 Subject: [PATCH 26/63] Gutenberg 19.8.0 hotfix: don't show the template-locked rendering mode for pages (#40664) This avoids triggering a bug in Gutenberg 19.8.0 which meant editor roles were unable to edit pages. --- .../changelog/hotfix-template-locked-bug | 4 ++++ .../src/class-jetpack-mu-wpcom.php | 1 + .../wpcom-hotfixes/wpcom-hotfixes.php | 20 +++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/hotfix-template-locked-bug create mode 100644 projects/packages/jetpack-mu-wpcom/src/features/wpcom-hotfixes/wpcom-hotfixes.php diff --git a/projects/packages/jetpack-mu-wpcom/changelog/hotfix-template-locked-bug b/projects/packages/jetpack-mu-wpcom/changelog/hotfix-template-locked-bug new file mode 100644 index 0000000000000..d3c084bd1a72d --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/hotfix-template-locked-bug @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Gutenberg 19.8.0 hotfix: don't show the template-locked rendering mode for pages 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 a607b7e67d910..26cf4192d604a 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 @@ -111,6 +111,7 @@ public static function load_features() { require_once __DIR__ . '/features/wpcom-admin-dashboard/wpcom-admin-dashboard.php'; require_once __DIR__ . '/features/wpcom-block-editor/class-jetpack-wpcom-block-editor.php'; require_once __DIR__ . '/features/wpcom-block-editor/functions.editor-type.php'; + require_once __DIR__ . '/features/wpcom-hotfixes/wpcom-hotfixes.php'; require_once __DIR__ . '/features/wpcom-logout/wpcom-logout.php'; require_once __DIR__ . '/features/wpcom-themes/wpcom-theme-fixes.php'; diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-hotfixes/wpcom-hotfixes.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-hotfixes/wpcom-hotfixes.php new file mode 100644 index 0000000000000..199033db6b7f5 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-hotfixes/wpcom-hotfixes.php @@ -0,0 +1,20 @@ + Date: Wed, 18 Dec 2024 12:31:01 -0500 Subject: [PATCH 27/63] My Jetpack: Plans section- Paid plan expiration date improvements. (#40575) * My Jetpack: Fix & improve plans section expiry date. --- .../_inc/components/plans-section/index.tsx | 37 +++++++++++++++---- .../fix-mj-plans-section-expire-date | 4 ++ 2 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 projects/packages/my-jetpack/changelog/fix-mj-plans-section-expire-date diff --git a/projects/packages/my-jetpack/_inc/components/plans-section/index.tsx b/projects/packages/my-jetpack/_inc/components/plans-section/index.tsx index 0a7f47e1d3f04..b4b3aef6dba03 100644 --- a/projects/packages/my-jetpack/_inc/components/plans-section/index.tsx +++ b/projects/packages/my-jetpack/_inc/components/plans-section/index.tsx @@ -1,5 +1,5 @@ import { Text, H3, Title, Button } from '@automattic/jetpack-components'; -import { dateI18n } from '@wordpress/date'; +import { getDate, dateI18n } from '@wordpress/date'; import { __, _n, sprintf } from '@wordpress/i18n'; import clsx from 'clsx'; import { useCallback } from 'react'; @@ -40,8 +40,16 @@ const PlanSection: FC< PlanSectionProps > = ( { purchase } ) => { }; const PlanExpiry: FC< PlanSectionProps > = ( { purchase } ) => { - const { ID, expiry_date, expiry_status, product_name, product_slug, subscribed_date, domain } = - purchase; + const { + ID, + expiry_date, + expiry_status, + partner_name, + product_name, + product_slug, + subscribed_date, + domain, + } = purchase; const managePurchaseUrl = `https://wordpress.com/me/purchases/${ domain }/${ ID }`; const renewUrl = `https://wordpress.com/checkout/${ product_slug }/renew/${ ID }/${ domain }`; @@ -56,14 +64,19 @@ const PlanExpiry: FC< PlanSectionProps > = ( { purchase } ) => { } ); const expiryMessage = useCallback( () => { - const displayDate = dateI18n( 'F jS, Y', expiry_date ); + const hundredYearDate = getDate( subscribed_date ); + hundredYearDate.setFullYear( hundredYearDate.getFullYear() + 100 ); + + // If expiry_date is null, we'll default to 100 years in the future (same behavior in Store Admin). + const expiryDate = dateI18n( 'F jS, Y', expiry_date ?? hundredYearDate ); + if ( isExpiringPurchase ) { // Expiring soon if ( isExpiringSoon ) { return sprintf( // translators: %1$s is the formatted date to display, i.e.- November 24th, 2024 __( 'Expiring soon on %1$s', 'jetpack-my-jetpack' ), - displayDate + expiryDate ); } @@ -71,16 +84,24 @@ const PlanExpiry: FC< PlanSectionProps > = ( { purchase } ) => { return sprintf( // translators: %1$s is the formatted date to display, i.e.- November 24th, 2024 __( 'Expired on %1$s', 'jetpack-my-jetpack' ), - displayDate + expiryDate ); } + if ( ! expiry_date && partner_name ) { + // This means the subscription was provisioned by a hosting partner. + return sprintf( + // translators: %1$s is the name of the hosting partner. i.e.- Bluehost, InMotion, Pressable, Jurassic Ninja, etc.. + __( 'Managed by: %1$s', 'jetpack-my-jetpack' ), + partner_name + ); + } return sprintf( // translators: %1$s is the formatted date to display, i.e.- November 24th, 2024 __( 'Expires on %1$s', 'jetpack-my-jetpack' ), - displayDate + expiryDate ); - }, [ expiry_date, isExpiringPurchase, isExpiringSoon ] ); + }, [ expiry_date, isExpiringPurchase, isExpiringSoon, partner_name, subscribed_date ] ); const expiryAction = useCallback( () => { if ( ! isExpiringPurchase ) { diff --git a/projects/packages/my-jetpack/changelog/fix-mj-plans-section-expire-date b/projects/packages/my-jetpack/changelog/fix-mj-plans-section-expire-date new file mode 100644 index 0000000000000..06f2b73ed6726 --- /dev/null +++ b/projects/packages/my-jetpack/changelog/fix-mj-plans-section-expire-date @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +My Jetpack: Plans section: Improvements to how we display plan expiration date. From 21b4c4f00c460e8314ba58c7bdbbd702f1342bae Mon Sep 17 00:00:00 2001 From: Ian Ramos <5714212+IanRamosC@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:54:14 -0300 Subject: [PATCH 28/63] My Jetpack: support features as recommendations (#40492) * My Jetpack: add feature as possible module recommendation * changelog * Add the module classes for the 3 features * Add config for feature modules * Refactor module product class * Fix PHAN errors --- .../changelog/add-feature-recommendations | 4 + .../my-jetpack/src/class-products.php | 38 ++-- .../src/products/class-module-product.php | 38 ++++ .../src/products/class-newsletter.php | 179 ++++++++++++++++++ .../src/products/class-related-posts.php | 179 ++++++++++++++++++ .../src/products/class-site-accelerator.php | 179 ++++++++++++++++++ 6 files changed, 600 insertions(+), 17 deletions(-) create mode 100644 projects/packages/my-jetpack/changelog/add-feature-recommendations create mode 100644 projects/packages/my-jetpack/src/products/class-newsletter.php create mode 100644 projects/packages/my-jetpack/src/products/class-related-posts.php create mode 100644 projects/packages/my-jetpack/src/products/class-site-accelerator.php diff --git a/projects/packages/my-jetpack/changelog/add-feature-recommendations b/projects/packages/my-jetpack/changelog/add-feature-recommendations new file mode 100644 index 0000000000000..700e01f03c9a4 --- /dev/null +++ b/projects/packages/my-jetpack/changelog/add-feature-recommendations @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +My Jetpack: add features as possible modules to the recommendations list. diff --git a/projects/packages/my-jetpack/src/class-products.php b/projects/packages/my-jetpack/src/class-products.php index 87e277dcae26a..8f8681fdb2ab8 100644 --- a/projects/packages/my-jetpack/src/class-products.php +++ b/projects/packages/my-jetpack/src/class-products.php @@ -119,24 +119,28 @@ class Products { */ public static function get_products_classes() { $classes = array( - 'anti-spam' => Products\Anti_Spam::class, - 'backup' => Products\Backup::class, - 'boost' => Products\Boost::class, - 'crm' => Products\Crm::class, - 'creator' => Products\Creator::class, - 'extras' => Products\Extras::class, - 'jetpack-ai' => Products\Jetpack_Ai::class, + 'anti-spam' => Products\Anti_Spam::class, + 'backup' => Products\Backup::class, + 'boost' => Products\Boost::class, + 'crm' => Products\Crm::class, + 'creator' => Products\Creator::class, + 'extras' => Products\Extras::class, + 'jetpack-ai' => Products\Jetpack_Ai::class, // TODO: Remove this duplicate class ('ai')? See: https://github.com/Automattic/jetpack/pull/35910#pullrequestreview-2456462227 - 'ai' => Products\Jetpack_Ai::class, - 'scan' => Products\Scan::class, - 'search' => Products\Search::class, - 'social' => Products\Social::class, - 'security' => Products\Security::class, - 'protect' => Products\Protect::class, - 'videopress' => Products\Videopress::class, - 'stats' => Products\Stats::class, - 'growth' => Products\Growth::class, - 'complete' => Products\Complete::class, + 'ai' => Products\Jetpack_Ai::class, + 'scan' => Products\Scan::class, + 'search' => Products\Search::class, + 'social' => Products\Social::class, + 'security' => Products\Security::class, + 'protect' => Products\Protect::class, + 'videopress' => Products\Videopress::class, + 'stats' => Products\Stats::class, + 'growth' => Products\Growth::class, + 'complete' => Products\Complete::class, + // Features + 'newsletter' => Products\Newsletter::class, + 'site-accelerator' => Products\Site_Accelerator::class, + 'related-posts' => Products\Related_Posts::class, ); /** diff --git a/projects/packages/my-jetpack/src/products/class-module-product.php b/projects/packages/my-jetpack/src/products/class-module-product.php index 5472cfec02e26..9d1a14f0fee86 100644 --- a/projects/packages/my-jetpack/src/products/class-module-product.php +++ b/projects/packages/my-jetpack/src/products/class-module-product.php @@ -7,6 +7,7 @@ namespace Automattic\Jetpack\My_Jetpack; +use Automattic\Jetpack\Connection\Manager as Connection_Manager; use Jetpack; use WP_Error; @@ -26,6 +27,13 @@ abstract class Module_Product extends Product { */ public static $module_name = null; + /** + * Whether this module is a Jetpack feature + * + * @var boolean + */ + public static $is_feature = false; + /** * Get the plugin slug - ovewrite it ans return Jetpack's * @@ -79,12 +87,42 @@ public static function is_module_active() { return Jetpack::is_module_active( static::$module_name ); } + /** + * Get the product status. + * We don't use parent::get_status() to avoid complexity. + * + * @return string Product status. + */ + private static function get_feature_status() { + if ( ! static::is_plugin_installed() ) { + return Products::STATUS_PLUGIN_ABSENT; + } + + if ( ! static::is_plugin_active() ) { + return Products::STATUS_INACTIVE; + } + + if ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) { + return Products::STATUS_USER_CONNECTION_ERROR; + } + + if ( ! static::is_module_active() ) { + return Products::STATUS_MODULE_DISABLED; + } + + return Products::STATUS_ACTIVE; + } + /** * Gets the current status of the product * * @return string */ public static function get_status() { + if ( static::$is_feature ) { + return static::get_feature_status(); + } + $status = parent::get_status(); if ( Products::STATUS_INACTIVE === $status && ! static::is_module_active() ) { $status = Products::STATUS_MODULE_DISABLED; diff --git a/projects/packages/my-jetpack/src/products/class-newsletter.php b/projects/packages/my-jetpack/src/products/class-newsletter.php new file mode 100644 index 0000000000000..66d806d5ea6ca --- /dev/null +++ b/projects/packages/my-jetpack/src/products/class-newsletter.php @@ -0,0 +1,179 @@ + true, + 'is_free' => true, + ); + } + + /** + * Checks whether the Product is active. + * + * @return boolean + */ + public static function is_active() { + return static::is_jetpack_plugin_active(); + } + + /** + * Checks whether the plugin is installed + * + * @return boolean + */ + public static function is_plugin_installed() { + return static::is_jetpack_plugin_installed(); + } + + /** + * Get the URL where the user manages the product + * + * @return ?string + */ + public static function get_manage_url() { + return admin_url( 'admin.php?page=jetpack#/settings?term=newsletter' ); + } + + /** + * Activates the Jetpack plugin + * + * @return null|WP_Error Null on success, WP_Error on invalid file. + */ + public static function activate_plugin() { + $plugin_filename = static::get_installed_plugin_filename( self::JETPACK_PLUGIN_SLUG ); + + if ( $plugin_filename ) { + return activate_plugin( $plugin_filename ); + } + } +} diff --git a/projects/packages/my-jetpack/src/products/class-related-posts.php b/projects/packages/my-jetpack/src/products/class-related-posts.php new file mode 100644 index 0000000000000..e5b10ec989931 --- /dev/null +++ b/projects/packages/my-jetpack/src/products/class-related-posts.php @@ -0,0 +1,179 @@ + true, + 'is_free' => true, + ); + } + + /** + * Checks whether the Product is active. + * + * @return boolean + */ + public static function is_active() { + return static::is_jetpack_plugin_active(); + } + + /** + * Checks whether the plugin is installed + * + * @return boolean + */ + public static function is_plugin_installed() { + return static::is_jetpack_plugin_installed(); + } + + /** + * Get the URL where the user manages the product + * + * @return ?string + */ + public static function get_manage_url() { + return admin_url( 'admin.php?page=jetpack#/traffic?term=related%20posts' ); + } + + /** + * Activates the Jetpack plugin + * + * @return null|WP_Error Null on success, WP_Error on invalid file. + */ + public static function activate_plugin() { + $plugin_filename = static::get_installed_plugin_filename( self::JETPACK_PLUGIN_SLUG ); + + if ( $plugin_filename ) { + return activate_plugin( $plugin_filename ); + } + } +} diff --git a/projects/packages/my-jetpack/src/products/class-site-accelerator.php b/projects/packages/my-jetpack/src/products/class-site-accelerator.php new file mode 100644 index 0000000000000..42cda9bc8a798 --- /dev/null +++ b/projects/packages/my-jetpack/src/products/class-site-accelerator.php @@ -0,0 +1,179 @@ + true, + 'is_free' => true, + ); + } + + /** + * Checks whether the Product is active. + * + * @return boolean + */ + public static function is_active() { + return static::is_jetpack_plugin_active(); + } + + /** + * Checks whether the plugin is installed + * + * @return boolean + */ + public static function is_plugin_installed() { + return static::is_jetpack_plugin_installed(); + } + + /** + * Get the URL where the user manages the product + * + * @return ?string + */ + public static function get_manage_url() { + return admin_url( 'admin.php?page=jetpack#/settings?term=site%20accelerator' ); + } + + /** + * Activates the Jetpack plugin + * + * @return null|WP_Error Null on success, WP_Error on invalid file. + */ + public static function activate_plugin() { + $plugin_filename = static::get_installed_plugin_filename( self::JETPACK_PLUGIN_SLUG ); + + if ( $plugin_filename ) { + return activate_plugin( $plugin_filename ); + } + } +} From 3ed576dddb5ab6db78afe0202dfa2731ec45f68e Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Wed, 18 Dec 2024 15:12:18 -0500 Subject: [PATCH 29/63] ci: Check for use of SCSS `random()` (#40666) Normal CSS can't do random numbers. SCSS has a `random()` function, which produces random values in the output CSS. Unfortunately this makes the built CSS change for each build, which means that every PR merged will require a Simple deploy. To avoid this, add a CI check looking for calls to the function in all `.scss` files. Also do the same for `unique-id()`. --- .github/files/lint-project-structure.sh | 11 +++++++++++ .../changelog/update-no-scss-random | 4 ++++ .../src/features/holiday-snow/holiday-snow.scss | 14 +++++++++++--- 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/update-no-scss-random diff --git a/.github/files/lint-project-structure.sh b/.github/files/lint-project-structure.sh index 175f7e6c4575f..194bf66cd9d32 100755 --- a/.github/files/lint-project-structure.sh +++ b/.github/files/lint-project-structure.sh @@ -635,4 +635,15 @@ while IFS= read -r FILE; do done < <( git grep -h --line-number --column -o "$RE" "$FILE" ) done < <( git -c core.quotepath=off grep -l "$RE" ) +# - Check for `random(` in scss files. +debug "Checking for SCSS random." +while IFS= read -r FILE; do + EXIT=1 + while IFS=: read -r LINE COL X; do + X=${X%(} + echo "::error file=$FILE,line=$LINE,col=$COL::Do not use SCSS \`$X()\`. It means that every build will have different CSS, dirtying the diffs (and making for redudant Simple deploys if this gets into a relevant plugin)." + done < <( git grep -h --line-number --column -o '\(random\|unique-id\)\s*(' "$FILE" ) +done < <( git -c core.quotepath=off grep -l '\(random\|unique-id\)\s*(' '*.sass' '*.scss' ) + + exit $EXIT diff --git a/projects/packages/jetpack-mu-wpcom/changelog/update-no-scss-random b/projects/packages/jetpack-mu-wpcom/changelog/update-no-scss-random new file mode 100644 index 0000000000000..dfbf589fd7a43 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/update-no-scss-random @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Holiday snow: Replace SCSS `random()` with pregenerated arrays of random numbers to make builds reproducable. diff --git a/projects/packages/jetpack-mu-wpcom/src/features/holiday-snow/holiday-snow.scss b/projects/packages/jetpack-mu-wpcom/src/features/holiday-snow/holiday-snow.scss index c548402cf8483..093158e54a445 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/holiday-snow/holiday-snow.scss +++ b/projects/packages/jetpack-mu-wpcom/src/features/holiday-snow/holiday-snow.scss @@ -10,14 +10,22 @@ $snow_density: 10; $snow_speed: 9s; $snow_selector: '#jetpack-holiday-snow'; +// Random numbers, to replace the `random` function so we can have reproducable builds. +// Needs $snow_density entries. Intergers 3-6. +$snow_sizes: 6, 6, 3, 5, 3, 4, 6, 5, 4, 6; +// Needs $snow_density entries. Floats 0.6-1.0. +$snow_alphas: 0.8, 1, 0.9, 1, 0.6, 1, 1, 1, 0.8, 1; +// Needs $snow_density * 2 entries. Floats 0-1. +$snow_positions: 0.17857, 0.30782, 0.65476, 0.04422, 0.31145, 0.02525, 0.72542, 0.19153, 0.26094, 0.12121, 0.47466, 0.76351, 0.46259, 0.92007, 0.24915, 0.44915, 0.19088, 0.86993, 0.62925, 0.68707; + html { $grad: ( ); @for $i from 0 to $snow_density { - $v: random(4) + 2; - $alpha: random(5) * .1 + .5; - $grad: $grad, radial-gradient($v+px $v+px at (random($grid_width - $v * 2) + $v)+px (random($grid_width - $v * 2) + $v)+px, + $v: nth($snow_sizes, $i + 1); + $alpha: nth($snow_alphas, $i + 1); + $grad: $grad, radial-gradient($v+px $v+px at (round(nth($snow_positions, $i * 2 + 1) * ($grid_width - $v * 2)) + $v)+px (round(nth($snow_positions, $i * 2 + 2) * ($grid_width - $v * 2)) + $v)+px, rgba(255, 255, 255, $alpha) 50%, rgba(0, 0, 0, 0)); } From 988bafb3ee2136de2ec9d1672cf286502c5448fb Mon Sep 17 00:00:00 2001 From: Nate Weller Date: Wed, 18 Dec 2024 13:20:32 -0700 Subject: [PATCH 30/63] Protect Status: prevent undefined extension warning (#40622) --- .../changelog/fix-php-undefined-array-key-warning | 4 ++++ projects/packages/protect-status/src/class-scan-status.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 projects/packages/protect-status/changelog/fix-php-undefined-array-key-warning diff --git a/projects/packages/protect-status/changelog/fix-php-undefined-array-key-warning b/projects/packages/protect-status/changelog/fix-php-undefined-array-key-warning new file mode 100644 index 0000000000000..72562089ff171 --- /dev/null +++ b/projects/packages/protect-status/changelog/fix-php-undefined-array-key-warning @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fix PHP warnings caused by uninstalled extensions. diff --git a/projects/packages/protect-status/src/class-scan-status.php b/projects/packages/protect-status/src/class-scan-status.php index 32deca2eba526..6f743cc397b63 100644 --- a/projects/packages/protect-status/src/class-scan-status.php +++ b/projects/packages/protect-status/src/class-scan-status.php @@ -243,7 +243,7 @@ private static function normalize_api_data( $scan_data ) { // Theme and Plugin Threats if ( ! empty( $scan_threat->extension ) && in_array( $scan_threat->extension->type, array( 'plugin', 'theme' ), true ) ) { - $installed_extension = 'plugin' === $scan_threat->extension->type ? $plugins[ $scan_threat->extension->slug ] : $themes[ $scan_threat->extension->slug ]; + $installed_extension = 'plugin' === $scan_threat->extension->type ? ( $plugins[ $scan_threat->extension->slug ] ?? null ) : ( $themes[ $scan_threat->extension->slug ] ?? null ); // If the extension is no longer installed, skip this threat. // todo: use version_compare() From 6cc1f1477187a1f37aaaaac9ad0779e2187555ab Mon Sep 17 00:00:00 2001 From: Calypso Bot Date: Wed, 18 Dec 2024 22:30:22 +0100 Subject: [PATCH 31/63] phan: Update custom stubs (#40654) Co-authored-by: matticbot --- .phan/stubs/phpunit-stubs.php | 2 +- .phan/stubs/woocommerce-internal-stubs.php | 3 ++- .phan/stubs/woocommerce-payments-stubs.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.phan/stubs/phpunit-stubs.php b/.phan/stubs/phpunit-stubs.php index 801a8a4e4e6b1..0d417e8d3000a 100644 --- a/.phan/stubs/phpunit-stubs.php +++ b/.phan/stubs/phpunit-stubs.php @@ -1,6 +1,6 @@ Date: Wed, 18 Dec 2024 17:02:30 -0500 Subject: [PATCH 32/63] codeql: Remove unneeded advanced config file (#40674) GitHub has a "default" config and an "advanced" config. It turns out the "advanced" config we have here doesn't do anything very advanced, we can switch to the default config. See https://github.com/Automattic/jetpack/security/code-scanning/438 When I did that, it disabled the workflow but didn't create a PR to delete this file for us. Guess we have to do that manually. --- .github/workflows/codeql-analysis.yml | 39 --------------------------- 1 file changed, 39 deletions(-) delete mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 63a7d71893189..0000000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: "CodeQL Code Scanning Analysis" - -on: - push: - branches: [ trunk ] - pull_request: - branches: [ trunk ] - schedule: - - cron: '0 19 * * 0' -concurrency: - group: codeql-analysis-${{ github.event_name }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - timeout-minutes: 7 # 2021-01-18: Successful runs seem to take 3-5 minutes - - strategy: - fail-fast: false - matrix: - language: ['javascript'] - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 From 6382dad286a31019bd83f2503c62a1cd6f9598c4 Mon Sep 17 00:00:00 2001 From: tbradsha <32492176+tbradsha@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:22:27 -0700 Subject: [PATCH 33/63] Tools: Add steps to release script (#40556) * Add var to disable tracks * First pass at stepification * Rework release steps * Wording tweaks * Add some safety checks * Prevent pipefail * Disable shellcheck SC1091 (not following source files) * Update comment Co-authored-by: Brad Jorsch * Remove spare tab * Rework usage output * Add undocumented --list-steps option * Oops Co-authored-by: Brad Jorsch * Add multi-plugin support back to usage output Co-authored-by: Brad Jorsch --------- Co-authored-by: Brad Jorsch --- tools/includes/send_tracks_event.sh | 4 +- tools/release-plugin.sh | 792 +++++++++++++++------------- 2 files changed, 425 insertions(+), 371 deletions(-) diff --git a/tools/includes/send_tracks_event.sh b/tools/includes/send_tracks_event.sh index 6f6660f6c096b..7f417f1da33a2 100644 --- a/tools/includes/send_tracks_event.sh +++ b/tools/includes/send_tracks_event.sh @@ -14,8 +14,8 @@ function get_uuid { # - 1: Event name # - 2: An optional JSON-formatted payload of extra tracks params, e.g. `{"a":1, "b":2}` function send_tracks_event { - # Bail if no event name is provided. - if [[ -z "$1" ]]; then + # Bail if no event name is provided or JP_DISABLE_TRACKS is set. + if [[ -z "$1" || $JP_DISABLE_TRACKS == 1 ]]; then return fi diff --git a/tools/release-plugin.sh b/tools/release-plugin.sh index 37fa2fe545063..b3ce49b1aac09 100755 --- a/tools/release-plugin.sh +++ b/tools/release-plugin.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 set -eo pipefail @@ -12,128 +13,454 @@ BASE=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) . "$BASE/tools/includes/changelogger.sh" . "$BASE/tools/includes/send_tracks_event.sh" +VERSION_REGEX='^[0-9]+(\.[0-9]+)+(-.*)?$' +CUR_STEP=0 + +RELEASE_STEPS=( + 'do_trunk_and_prelease_branch_prep' + 'do_changelogs' + 'do_readme' + 'do_commit_changelog_and_readme' + 'do_push_and_build' + 'do_packagist_check' + 'do_create_release_branches' + 'do_create_prerelease_PR' + 'do_final_instructions' +) + # Instructions function usage { cat <<-EOH - usage: $0 [options] [] [ [] ...] - - Conduct a full release of specified plugins through release branch creation. Just the plugin name is fine, such as 'jetpack' or 'backup.' The version is optional, and if not specified, will be set to the next stable version. + Usage: + $0 [-s ] [] [ [] ...] Options: - -h Show this help message. + -h, --help Show this help message. + -s , --step Start at a given step. + Plugin name to release (e.g. jetpack) + Version number (defaults to next stable) + + Conduct a full release of specified plugins through release branch creation. + Accepts the plugin name without a prefix, such as 'jetpack' or 'backup'. The + version is optional; if not specified it will be set to the next stable version. + EOH exit 1 } -# Make sure the GitHub CLI is installed. -if ! command -v gh &> /dev/null; then - yellow "This tool requires the GitHub CLI, which was not found." - if command -v brew &> /dev/null; then - proceed_p "Install the GitHub CLI via brew?" "" Y - brew install gh - else - die "Please install the GitHub CLI before proceeding" +# Preliminary environment checks. +function preflight_checks { + # Make sure the GitHub CLI is installed. + if ! command -v gh &> /dev/null; then + yellow "This tool requires the GitHub CLI, which was not found." + if command -v brew &> /dev/null; then + proceed_p "Install the GitHub CLI via brew?" "" Y + brew install gh + else + die "Please install the GitHub CLI before proceeding" + fi fi -fi - -GH_VER="$( gh --version | grep -E -o -m1 '[0-9]+\.[0-9]+\.[0-9]+' )" -if ! version_compare "$GH_VER" "2.21.2"; then - command -v brew &> /dev/null && WITH=" with 'brew upgrade gh'" || WITH= - die "Your version of the GH CLI is out of date. Please upgrade your version$WITH and start again" -fi -# Make sure we're signed into the GitHub CLI. -if ! gh auth status --hostname github.com &> /dev/null; then - yellow "You are not signed into the GitHub CLI." - proceed_p "Sign in to the GitHub CLI?" "" Y - gh auth login -fi - -# Get the options passed and parse them. -while getopts "h" opt; do - case ${opt} in - h) - usage - ;; - ?) - error "Invalid argument" - echo "" - usage - ;; - esac -done -shift "$(($OPTIND -1))" - -# Parse arguments in associated array of plugins/project => version format. -if [ $# -eq 0 ]; then - usage -fi - -declare -A PROJECTS -while [[ $# -gt 0 ]]; do - if [[ "$1" =~ ^[a-zA-Z\-]+$ ]]; then - PLUGIN=$1 - else - die "Script must be passed in [version] format. Got $1" + GH_VER="$( gh --version | grep -E -o -m1 '[0-9]+\.[0-9]+\.[0-9]+' )" + if ! version_compare "$GH_VER" "2.21.2"; then + command -v brew &> /dev/null && WITH=" with 'brew upgrade gh'" || WITH= + die "Your version of the GH CLI is out of date. Please upgrade your version$WITH and start again" fi - if [[ "$2" =~ ^[0-9]+(\.[0-9]+)+(-.*)?$ ]]; then - PROJECTS["$PLUGIN"]=$2 - SHIFT="2" - else - PROJECTS["$PLUGIN"]='' - SHIFT="1" + # Make sure we're signed into the GitHub CLI. + if ! gh auth status --hostname github.com &> /dev/null; then + yellow "You are not signed into the GitHub CLI." + proceed_p "Sign in to the GitHub CLI?" "" Y + gh auth login fi - shift "$SHIFT" -done +} -# Check that the projects are valid. -for PLUGIN in "${!PROJECTS[@]}"; do - # Get the project slug. - SLUG="${PLUGIN#projects/}" # DWIM - SLUG="${SLUG%/}" # Sanitize - SLUG="plugins/$SLUG" +function verify_prerelease_branch { + [[ $(git branch --show-current) != "prerelease" ]] && die 'Not on prerelease branch!' + return 0 +} + +function normalize_plugin_name { + SLUG="$1" + + # Normalize slug + SLUG="${SLUG#projects/}" # Remove leading 'projects/' + SLUG="${SLUG#plugins/}" # Remove leading 'plugins/' + SLUG="${SLUG%/}" # Remove trailing slash + SLUG="plugins/$SLUG" # Add leading 'plugins/' if [[ ! -e "$BASE/projects/$SLUG/composer.json" ]]; then die "$SLUG isn't a valid project!" elif ! jq -e '.extra["release-branch-prefix"]' "$BASE/projects/$SLUG/composer.json" &>/dev/null; then die "$SLUG has no release branch prefix!" + fi + echo "$SLUG" +} + +# Generates a command that can be used to resume the release script from the current step. +# Currently unused. +function generate_resume_command() { + local cmd="$0 -s $CUR_STEP" + for PLUGIN in "${!PROJECTS[@]}"; do + cmd+=" ${PLUGIN#plugins/} ${PROJECTS[$PLUGIN]}" + done + echo "$cmd" +} + +function do_trunk_and_prelease_branch_prep() { + # Make sure we're standing on trunk and working directory is clean + CURRENT_BRANCH="$( git rev-parse --abbrev-ref HEAD )" + if [[ "$CURRENT_BRANCH" != "trunk" ]]; then + proceed_p "Not currently checked out to trunk." "Check out trunk before continuing?" Y + git checkout trunk && git pull + fi + + if [[ -n "$(git status --porcelain)" ]]; then + die "Working directory not clean, make sure you're working from a clean checkout and try again." + fi + + yellow "Installing root packages." + pnpm jetpack install --root + + yellow "Checking out prerelease branch." + # Is there an upstream prerelease branch already? + R=$( git ls-remote --heads origin prerelease ) + if [[ -n "$R" ]]; then + error "There's an existing prerelease branch on GitHub!" + R=${R%%[ $'\t']*} + TMP=$( gh pr list --head=prerelease --state=open --json number,author,url,createdAt,title --jq '.[] | "\( .url ) - \( .createdAt | fromdateiso8601 | strftime( "%F %H:%IZ" ) ) - \( .title ) [\( .author.name )]"' ) + if [[ -n "$TMP" ]]; then + echo "Someone probably needs to merge the following PR:" + echo "$TMP" + exit 1 + fi + git fetch -q origin trunk "$R" || die "Something unexpected went wrong fetching the commit from GitHub" + if git diff --quiet origin/trunk..."$R"; then + echo "There don't seem to be any changes between that and trunk. If its author has abandoned the release it should be safe to delete the branch." + exit 1 + fi + if ! git diff --quiet origin/trunk..."$R" '*/CHANGELOG.md'; then + echo "Looks like someone is in the middle of a release! Wait for them to merge the changes back into trunk." + exit 1 + fi + echo "Looks like someone may be in the middle of a release! Wait for them to merge the changes back into trunk, or to abandon the release." + exit 1 + fi + + # Check out and push pre-release branch + if git rev-parse --verify prerelease &>/dev/null; then + proceed_p "Existing local prerelease branch found." "Delete it?" Y + git branch -D prerelease + fi + + git checkout -b prerelease + if ! git push -u origin HEAD; then + send_tracks_event "jetpack_release_prerelease_push" '{"result": "failure"}' + die "Branch push failed. Check #jetpack-releases and make sure no one is doing a release already, then delete the branch at https://github.com/Automattic/jetpack/branches" + fi + GITBASE=$( git rev-parse --verify HEAD ) + send_tracks_event "jetpack_release_prerelease_push" '{"result": "success"}' +} + +function do_changelogs { + verify_prerelease_branch + # Loop through the projects and update the changelogs after building the arguments. + for PLUGIN in "${!PROJECTS[@]}"; do + yellow "Updating the changelog files for $PLUGIN." + + # Add the PR numbers to the changelog. + ARGS=('-p') + + # Add alpha and beta flags. + VERSION="${PROJECTS[$PLUGIN]}" + case $VERSION in + *-a* ) ARGS+=('-a');; + *-beta ) ARGS+=('-b');; + esac + + # Explicitly pass the version number we want so there are no surprises. + ARGS+=( '-r' "${PROJECTS[$PLUGIN]}" ) + ARGS+=("$PLUGIN"); + tools/changelogger-release.sh "${ARGS[@]}" + done + + # When it completes, wait for user to edit anything they want, then push key to continue. + read -r -s -p $'Edit and save all the changelog entries you want in a separate terminal or in your text editor of choice.\nCheck for consistency between the different entries, and keep in mind that your plugin changelog will be used in the plugin readme file.\n\nOnce you are happy with your work, press enter to continue the release process.' + echo "" +} + +function do_readme { + verify_prerelease_branch + for PLUGIN in "${!PROJECTS[@]}"; do + # check if the plugin even has a readme.txt file. + if [[ ! -e "$BASE/projects/$PLUGIN/readme.txt" ]]; then + yellow "$PLUGIN has no readme.txt file, skipping." + continue + fi + yellow "Updating the readme.txt file for $PLUGIN." + ARGS=() + # Add alpha and beta flags. + VERSION="${PROJECTS[$PLUGIN]}" + case $VERSION in + *-a* ) ARGS+=('-a');; + *-beta ) ARGS+=('-b');; + * ) ARGS+=('-s');; + esac + pnpm jetpack release "$PLUGIN" readme "${ARGS[@]}" + done +} + +function do_commit_changelog_and_readme { + verify_prerelease_branch + yellow "Committing changes." + git add --all + git commit -am "Changelog and readme.txt edits." + + # If we're releasing Jetpack and it's a beta, amend the readme.txt + if [[ -v PROJECTS["plugins/jetpack"] && "${PROJECTS[plugins/jetpack]}" == *-beta ]]; then + yellow "Releasing a beta for Jetpack, amending the readme.txt" + pnpm jetpack changelog squash plugins/jetpack readme + git commit -am "Amend readme.txt" + fi +} + +function do_push_and_build { + verify_prerelease_branch + HEADSHA=$(git rev-parse HEAD) + yellow "Pushing changes." + git push -u origin prerelease + + yellow "Waiting for build to complete and push to mirror repos" + BUILDID= + + # If the build ID doesn't exist, try every five seconds until timeout after a minute. + TIMEOUT=$((SECONDS+60)) + while [[ $SECONDS -lt $TIMEOUT && -z "$BUILDID" ]]; do + echo "Waiting for build to become available..." + sleep 5 + BUILDID="$( gh run list -b prerelease -w Build --json event,databaseId,headSha | jq --arg HEADSHA "$HEADSHA" '.[] | select(.event=="push" and .headSha==$HEADSHA) | .databaseId' )" + done + + if [[ -z "$BUILDID" ]]; then + send_tracks_event "jetpack_release_github_build" '{"result": "not_found"}' + die "Build ID not found. Check GitHub actions to see if build on prerelease branch is running, then continue with manual steps." + fi + + yellow "Build ID found, waiting for build to complete and push to mirror repos." + if ! gh run watch "${BUILDID[0]}" --exit-status; then + send_tracks_event "jetpack_release_github_build" '{"result": "failure"}' + echo "Build failed! Check for build errors on GitHub for more information." && die + fi + + send_tracks_event "jetpack_release_github_build" '{"result": "success"}' + yellow "Build is complete." +} + +function do_packagist_check { + verify_prerelease_branch + # Wait for new versions of any composer packages to be up. + # We expect a new version when (1) the package is touched in this release and (2) it has no change entry files remaining. + POLL_ARGS=() + cd "$BASE" + for PKGDIR in $(git -c core.quotepath=off diff --name-only "$GITBASE..HEAD" projects/packages/ | sed 's!^\(projects/packages/[^/]*\)/.*!\1!' | sort -u); do + cd "$BASE/$PKGDIR" + CHANGES_DIR=$(jq -r '.extra.changelogger["changes-dir"] // "changelog"' composer.json) + if [[ ! -d "$CHANGES_DIR" || -z "$(ls -- "$CHANGES_DIR")" ]]; then + POLL_ARGS+=( "$( jq -r .name composer.json )=$( changelogger version current )" ) + fi + done + cd "$BASE" + if [[ ${#POLL_ARGS[@]} -gt 0 ]]; then + yellow "Waiting for packagist to get updated packages..." + tools/js-tools/await-packagist-updates.mjs "${POLL_ARGS[@]}" + yellow "Packagist is updated!" + fi +} + +function do_create_release_branches { + # Run tools/create-release-branch.sh to create a release branch for each project. + for PREFIX in "${PREFIXES[@]}"; do + git checkout prerelease + PROJECT=$(jq -r --arg prefix "$PREFIX" '.[$prefix] | if length == 1 then "plugins/\(first)" else empty end' <<<"$PREFIXDATA") + if [[ -n "$PROJECT" && -n "${PROJECTS[$PROJECT]}" ]]; then + VERSION="${PROJECTS[$PROJECT]}" + yellow "Creating release branch for $PROJECT $VERSION" + tools/create-release-branch.sh "$PROJECT" "$VERSION" + else + yellow "Creating release branch for $PREFIX" + tools/create-release-branch.sh "$PREFIX" + fi + done + + yellow "Release branches created!" +} + +function do_create_prerelease_PR { + yellow "Creating a PR to merge the prerelease branch into trunk." + git checkout prerelease + + # Handle any package changes merged into trunk while we were working. + git fetch + git merge origin/trunk + tools/fixup-project-versions.sh + if [[ -n "$(git status --porcelain)" ]]; then + git commit -am 'Version bumps' + fi + git push + PLUGINS_CHANGED= + for PLUGIN in "${!PROJECTS[@]}"; do + PLUGINS_CHANGED+="$(basename "$PLUGIN") ${PROJECTS[$PLUGIN]}, " + done + # Remove the trailing comma and space + PLUGINS_CHANGED=${PLUGINS_CHANGED%, } + sed "s/%RELEASED_PLUGINS%/$PLUGINS_CHANGED/g" .github/files/BACKPORT_RELEASE_CHANGES.md > .github/files/TEMP_BACKPORT_RELEASE_CHANGES.md + gh pr create --title "Backport $PLUGINS_CHANGED Changes" --body "$(cat .github/files/TEMP_BACKPORT_RELEASE_CHANGES.md)" --label "[Status] Needs Review" --repo "Automattic/jetpack" --head "$(git rev-parse --abbrev-ref HEAD)" + rm .github/files/TEMP_BACKPORT_RELEASE_CHANGES.md +} + +function do_final_instructions { + yellow "Release script complete!" + + echo '' + echo 'Next you need to merge the above PR into trunk.' + + AUTO=() + MANUALTAG=() + MANUALTAGONLY=() + MANUALPUB=() + MANUALBOTH=() + for PLUGIN in "${!PROJECTS[@]}"; do + F="$BASE/projects/$PLUGIN/composer.json" + if ! jq -e '.extra["mirror-repo"] // false' "$F" &>/dev/null; then + continue + fi + + if ! jq -e '.extra["wp-plugin-slug"] // .extra["wp-theme-slug"] // false' "$F" &>/dev/null; then + if ! jq -e '.extra["autotagger"]' "$F" &>/dev/null; then + MANUALTAGONLY+=( "$PLUGIN" ) + fi + continue + fi + + if jq -e '.extra["autotagger"]' "$F" &>/dev/null; then + if jq -e '.extra["wp-svn-autopublish"] // false' "$F" &>/dev/null; then + AUTO+=( "$PLUGIN" ) + else + MANUALPUB+=( "$PLUGIN" ) + fi + else + if jq -e '.extra["wp-svn-autopublish"] // false' "$F" &>/dev/null; then + MANUALTAG+=( "$PLUGIN" ) + else + MANUALBOTH+=( "$PLUGIN" ) + fi + fi + done + + if [[ ${#AUTO[@]} -gt 0 ]]; then + cat <<-EOM + + For these plugins: ${AUTO[*]} + The release will shortly be tagged to GitHub and released to SVN and you can + then smoke test the release. Once ready, use \`./tools/stable-tag.sh \` + to update the stable tag, and you're done! + EOM + fi + + if [[ ${#MANUALTAGONLY[@]} -gt 0 ]]; then + cat <<-EOM + + For these plugins: ${MANUALTAGONLY[*]} + Wait for the changes to appear in the mirror repo and conduct a GitHub + release. Then you're done! + EOM + fi + + if [[ ${#MANUALTAG[@]} -gt 0 ]]; then + cat <<-EOM + + For these plugins: ${MANUALTAG[*]} + Wait for the changes to appear in the mirror repo and conduct a GitHub + release. The changes will then be automatically released to SVN and you can + then smote test the release. Once ready, use \`./tools/stable-tag.sh \` + to update the stable tag, and you're done! + EOM + fi + + if [[ ${#MANUALPUB[@]} -gt 0 ]]; then + cat <<-EOM + + For these plugins: ${MANUALPUB[*]} + The release will shortly be tagged to GitHub. Once the tag appears, deploy it + to SVN by running \`./tools/deploy-to-svn.sh \`, and smoke test. + When ready, flip the stable tag with \`./tools/stable-tag.sh \` and + you're all set. + EOM + fi + + if [[ ${#MANUALBOTH[@]} -gt 0 ]]; then + cat <<-EOM + + For these plugins: ${MANUALBOTH[*]} + Wait for the changes to appear in the mirror repo, and conduct a GitHub + release. Next, deploy the tag to SVN by running + \`./tools/deploy-to-svn.sh \`, and smoke test. When ready, flip + the stable tag with \`./tools/stable-tag.sh \` and you're all set. + EOM + fi +} + +preflight_checks + +# No args, help flag, or invalid flag, so show usage. +if [[ $# -eq 0 || $1 == '-h' || $1 == '--help' ]]; then + usage +elif [[ $1 == '-s' || $1 == '--step' ]]; then + if [[ $2 =~ ^[0-9]$ ]]; then + CUR_STEP="$2" + shift 2 else - PROJECTS["$SLUG"]="${PROJECTS[$PLUGIN]}" - unset "PROJECTS[$PLUGIN]" + usage fi -done +elif [[ $1 == '--list-steps' ]]; then + echo 'Use the following steps at your own risk:' + for i in "${!RELEASE_STEPS[@]}"; do + echo " $i: ${RELEASE_STEPS[$i]}" + done + exit 1 +fi -# Try to obtain a version number for plugins that we didn't supply. -for SLUG in "${!PROJECTS[@]}"; do - if [[ -z "${PROJECTS[$SLUG]}" ]]; then - cd "$BASE/projects/$SLUG" - PROJECTS["$SLUG"]=$(changelogger version next) || die "Cannot determine version number for $SLUG. Please supply one on the command line." +# Parse arguments in associated array of plugin => version format. +declare -A PROJECTS +while [[ $# -gt 0 ]]; do + # Grab plugin name + PLUGIN=$(normalize_plugin_name "$1") + shift + + # Grab version number if provided. + if [[ "$1" =~ $VERSION_REGEX ]]; then + VERSION=$1 + shift + else + cd "$BASE/projects/$PLUGIN" + VERSION=$(changelogger version next) || die "Cannot determine version number for $PLUGIN. Please supply one on the command line." fi + + PROJECTS["$PLUGIN"]="$VERSION" done cd "$BASE" -# Check the plugin version(s) to use for the plugin(s). -function check_ver { - normalize_version_number "$2" - if [[ ! "$NORMALIZED_VERSION" =~ ^[0-9]+(\.[0-9]+)+(-.*)?$ ]]; then +for PLUGIN in "${!PROJECTS[@]}"; do + normalize_version_number "${PROJECTS[$PLUGIN]}" + if [[ ! "$NORMALIZED_VERSION" =~ $VERSION_REGEX ]]; then red "\"$NORMALIZED_VERSION\" does not appear to be a valid version number." - return 1 + die "Please specify a valid version number." fi - local CUR_VERSION - CUR_VERSION=$("$BASE/tools/plugin-version.sh" "$1") + CUR_VERSION=$("$BASE/tools/plugin-version.sh" "$PLUGIN") # shellcheck disable=SC2310 if version_compare "$CUR_VERSION" "$NORMALIZED_VERSION"; then - proceed_p "Version $NORMALIZED_VERSION <= $CUR_VERSION." - return $? - fi - return 0 -} - -for PLUGIN in "${!PROJECTS[@]}"; do - if ! check_ver "$PLUGIN" "${PROJECTS[$PLUGIN]}"; then - die "Please specify a valid version number." + proceed_p "$PLUGIN: Version $NORMALIZED_VERSION is lower than $CUR_VERSION." || die "User aborted script." fi - echo "Releasing $PLUGIN ${PROJECTS[$PLUGIN]}" + echo "$PLUGIN: $CUR_VERSION -> ${PROJECTS[$PLUGIN]}" done proceed_p "" "Proceed releasing above projects?" Y @@ -191,284 +518,11 @@ if [[ ${#PREFIXES[@]} -gt 1 ]]; then proceed_p "" "" Y fi -# Make sure we're standing on trunk and working directory is clean -CURRENT_BRANCH="$( git rev-parse --abbrev-ref HEAD )" -if [[ "$CURRENT_BRANCH" != "trunk" ]]; then - proceed_p "Not currently checked out to trunk." "Check out trunk before continuing?" Y - git checkout trunk && git pull -fi - -if [[ -n "$(git status --porcelain)" ]]; then - die "Working directory not clean, make sure you're working from a clean checkout and try again." -fi - -yellow "Installing root packages." -pnpm jetpack install --root - -yellow "Checking out prerelease branch." -# Is there an upstream prerelease branch already? -R=$( git ls-remote --heads origin prerelease ) -if [[ -n "$R" ]]; then - error "There's an existing prerelease branch on GitHub!" - R=${R%%[ $'\t']*} - TMP=$( gh pr list --head=prerelease --state=open --json number,author,url,createdAt,title --jq '.[] | "\( .url ) - \( .createdAt | fromdateiso8601 | strftime( "%F %H:%IZ" ) ) - \( .title ) [\( .author.name )]"' ) - if [[ -n "$TMP" ]]; then - echo "Someone probably needs to merge the following PR:" - echo "$TMP" - exit 1 - fi - git fetch -q origin trunk "$R" || die "Something unexpected went wrong fetching the commit from GitHub" - if git diff --quiet origin/trunk..."$R"; then - echo "There don't seem to be any changes between that and trunk, if whoever created it has abandoned the release it should be safe to delete the branch." - exit 1 - fi - if ! git diff --quiet origin/trunk..."$R" '*/CHANGELOG.md'; then - echo "Looks like someone is in the middle of a release! Wait for them merge the changes back into trunk." - exit 1 - fi - echo "Looks like someone may be in the middle of a release! Wait for them merge the changes back into trunk, or to abandon the release." - exit 1 -fi - -# Check out and push pre-release branch -if git rev-parse --verify prerelease &>/dev/null; then - proceed_p "Existing local prerelease branch found." "Delete it?" Y - git branch -D prerelease -fi - -git checkout -b prerelease -if ! git push -u origin HEAD; then - send_tracks_event "jetpack_release_prerelease_push" '{"result": "failure"}' - die "Branch push failed. Check #jetpack-releases and make sure no one is doing a release already, then delete the branch at https://github.com/Automattic/jetpack/branches" -fi -GITBASE=$( git rev-parse --verify HEAD ) -send_tracks_event "jetpack_release_prerelease_push" '{"result": "success"}' - -# Loop through the projects and update the changelogs after building the arguments. -for PLUGIN in "${!PROJECTS[@]}"; do - yellow "Updating the changelog files for $PLUGIN." - - # Add the PR numbers to the changelog. - ARGS=('-p') - - # Add alpha and beta flags. - VERSION="${PROJECTS[$PLUGIN]}" - case $VERSION in - *-a* ) ARGS+=('-a');; - *-beta ) ARGS+=('-b');; - esac - - # Explicitly pass the version number we want so there are no surprises. - ARGS+=( '-r' "${PROJECTS[$PLUGIN]}" ) - ARGS+=("$PLUGIN"); - tools/changelogger-release.sh "${ARGS[@]}" -done - -# When it completes, wait for user to edit anything they want, then push key to continue. -read -r -s -p $'Edit all the changelog entries you want (in a separate terminal or your text editor of choice (make sure to save)).\nCheck for consistency between the different entries, and keep in mind that your plugin changelog will be used in the plugin readme file.\n\nOnce you are happy with your work, press enter to continue the release process.' -echo "" - -for PLUGIN in "${!PROJECTS[@]}"; do - # check if the plugin even has a readme.txt file. - if [[ ! -e "$BASE/projects/$PLUGIN/readme.txt" ]]; then - yellow "$PLUGIN has no readme.txt file, skipping." - continue - fi - yellow "Updating the readme.txt file for $PLUGIN." - ARGS=() - # Add alpha and beta flags. - VERSION="${PROJECTS[$PLUGIN]}" - case $VERSION in - *-a* ) ARGS+=('-a');; - *-beta ) ARGS+=('-b');; - * ) ARGS+=('-s');; - esac - pnpm jetpack release "$PLUGIN" readme "${ARGS[@]}" -done - -yellow "Committing changes." -git add --all -git commit -am "Changelog and readme.txt edits." - -# If we're releasing Jetpack and it's a beta, amend the readme.txt -if [[ -v PROJECTS["plugins/jetpack"] && "${PROJECTS[plugins/jetpack]}" == *-beta ]]; then - yellow "Releasing a beta for Jetpack, amending the readme.txt" - pnpm jetpack changelog squash plugins/jetpack readme - git commit -am "Amend readme.txt" -fi - -HEADSHA=$(git rev-parse HEAD) -yellow "Pushing changes." -git push -u origin prerelease - -yellow "Waiting for build to complete and push to mirror repos" -BUILDID= - -# If the build ID doesn't exist, try every five seconds until timeout after a minute. -TIMEOUT=$((SECONDS+60)) -while [[ $SECONDS -lt $TIMEOUT && -z "$BUILDID" ]]; do - echo "Waiting for build to become available..." - sleep 5 - BUILDID="$( gh run list -b prerelease -w Build --json event,databaseId,headSha | jq --arg HEADSHA "$HEADSHA" '.[] | select(.event=="push" and .headSha==$HEADSHA) | .databaseId' )" -done - -if [[ -z "$BUILDID" ]]; then - send_tracks_event "jetpack_release_github_build" '{"result": "not_found"}' - die "Build ID not found. Check GitHub actions to see if build on prerelease branch is running, then continue with manual steps." -fi - -yellow "Build ID found, waiting for build to complete and push to mirror repos." -if ! gh run watch "${BUILDID[0]}" --exit-status; then - send_tracks_event "jetpack_release_github_build" '{"result": "failure"}' - echo "Build failed! Check for build errors on GitHub for more information." && die -fi - -send_tracks_event "jetpack_release_github_build" '{"result": "success"}' -yellow "Build is complete." - -# Wait for new versions of any composer packages to be up. -# We expect a new version when (1) the package is touched in this release and (2) it has no change entry files remaining. -POLL_ARGS=() -cd "$BASE" -for PKGDIR in $(git -c core.quotepath=off diff --name-only "$GITBASE..HEAD" projects/packages/ | sed 's!^\(projects/packages/[^/]*\)/.*!\1!' | sort -u); do - cd "$BASE/$PKGDIR" - CHANGES_DIR=$(jq -r '.extra.changelogger["changes-dir"] // "changelog"' composer.json) - if [[ ! -d "$CHANGES_DIR" || -z "$(ls -- "$CHANGES_DIR")" ]]; then - POLL_ARGS+=( "$( jq -r .name composer.json )=$( changelogger version current )" ) - fi -done -cd "$BASE" -if [[ ${#POLL_ARGS[@]} -gt 0 ]]; then - yellow "Waiting for packagist to get updated packages..." - tools/js-tools/await-packagist-updates.mjs "${POLL_ARGS[@]}" - yellow "Packagist is updated!" -fi - -# Run tools/create-release-branch.sh to create a release branch for each project. -for PREFIX in "${PREFIXES[@]}"; do - git checkout prerelease - PROJECT=$(jq -r --arg prefix "$PREFIX" '.[$prefix] | if length == 1 then "plugins/\(first)" else empty end' <<<"$PREFIXDATA") - if [[ -n "$PROJECT" && -n "${PROJECTS[$PROJECT]}" ]]; then - VERSION="${PROJECTS[$PROJECT]}" - yellow "Creating release branch for $PROJECT $VERSION" - tools/create-release-branch.sh "$PROJECT" "$VERSION" - else - yellow "Creating release branch for $PREFIX" - tools/create-release-branch.sh "$PREFIX" - fi -done - -yellow "Release branches created!" - -yellow "Creating a PR to merge the prerelease branch into trunk." -git checkout prerelease - -# Handle any package changes merged into trunk while we were working. -git fetch -git merge origin/trunk -tools/fixup-project-versions.sh -if [[ -n "$(git status --porcelain)" ]]; then - git commit -am 'Version bumps' -fi -git push -PLUGINS_CHANGED= -for PLUGIN in "${!PROJECTS[@]}"; do - PLUGINS_CHANGED+="$(basename "$PLUGIN") ${PROJECTS[$PLUGIN]}, " -done -# Remove the trailing comma and space -PLUGINS_CHANGED=${PLUGINS_CHANGED%, } -sed "s/%RELEASED_PLUGINS%/$PLUGINS_CHANGED/g" .github/files/BACKPORT_RELEASE_CHANGES.md > .github/files/TEMP_BACKPORT_RELEASE_CHANGES.md -gh pr create --title "Backport $PLUGINS_CHANGED Changes" --body "$(cat .github/files/TEMP_BACKPORT_RELEASE_CHANGES.md)" --label "[Status] Needs Review" --repo "Automattic/jetpack" --head "$(git rev-parse --abbrev-ref HEAD)" -rm .github/files/TEMP_BACKPORT_RELEASE_CHANGES.md - -yellow "Release script complete!" - -echo '' -echo 'Next you need to merge the above PR into trunk.' - -AUTO=() -MANUALTAG=() -MANUALTAGONLY=() -MANUALPUB=() -MANUALBOTH=() -for PLUGIN in "${!PROJECTS[@]}"; do - F="$BASE/projects/$PLUGIN/composer.json" - if ! jq -e '.extra["mirror-repo"] // false' "$F" &>/dev/null; then - continue - fi - - if ! jq -e '.extra["wp-plugin-slug"] // .extra["wp-theme-slug"] // false' "$F" &>/dev/null; then - if ! jq -e '.extra["autotagger"]' "$F" &>/dev/null; then - MANUALTAGONLY+=( "$PLUGIN" ) - fi - continue - fi - - if jq -e '.extra["autotagger"]' "$F" &>/dev/null; then - if jq -e '.extra["wp-svn-autopublish"] // false' "$F" &>/dev/null; then - AUTO+=( "$PLUGIN" ) - else - MANUALPUB+=( "$PLUGIN" ) - fi - else - if jq -e '.extra["wp-svn-autopublish"] // false' "$F" &>/dev/null; then - MANUALTAG+=( "$PLUGIN" ) - else - MANUALBOTH+=( "$PLUGIN" ) - fi - fi +# Run each release step. +for ((i = CUR_STEP; i < ${#RELEASE_STEPS[@]}; i++)); do + green "Step $i: ${RELEASE_STEPS[$i]}" + "${RELEASE_STEPS[$i]}" + ((CUR_STEP+=1)) done -if [[ ${#AUTO[@]} -gt 0 ]]; then - cat <<-EOM - - For these plugins: ${AUTO[*]} - The release will shortly be tagged to GitHub and released to SVN and you can - then smoke test the release. Once ready, use \`./tools/stable-tag.sh \` - to update the stable tag, and you're done! - EOM -fi - -if [[ ${#MANUALTAGONLY[@]} -gt 0 ]]; then - cat <<-EOM - - For these plugins: ${MANUALTAGONLY[*]} - Wait for the changes to appear in the mirror repo and conduct a GitHub - release. Then you're done! - EOM -fi - -if [[ ${#MANUALTAG[@]} -gt 0 ]]; then - cat <<-EOM - - For these plugins: ${MANUALTAG[*]} - Wait for the changes to appear in the mirror repo and conduct a GitHub - release. The changes will then be automatically released to SVN and you can - then smote test the release. Once ready, use \`./tools/stable-tag.sh \` - to update the stable tag, and you're done! - EOM -fi - -if [[ ${#MANUALPUB[@]} -gt 0 ]]; then - cat <<-EOM - - For these plugins: ${MANUALPUB[*]} - The release will shortly be tagged to GitHub. Once the tag appears, deploy it - to SVN by running \`./tools/deploy-to-svn.sh \`, and smoke test. - When ready, flip the stable tag with \`./tools/stable-tag.sh \` and - you're all set. - EOM -fi - -if [[ ${#MANUALBOTH[@]} -gt 0 ]]; then - cat <<-EOM - - For these plugins: ${MANUALBOTH[*]} - Wait for the changes to appear in the mirror repo, and conduct a GitHub - release. Next, deploy the tag to SVN by running - \`./tools/deploy-to-svn.sh \`, and smoke test. When ready, flip - the stable tag with \`./tools/stable-tag.sh \` and you're all set. - EOM -fi - send_tracks_event "jetpack_release_done" From ae2a3997adb7ea420784de06ec9a61e1c8883bb3 Mon Sep 17 00:00:00 2001 From: Ben Zhu <6070516+benzhu56@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:14:16 +1000 Subject: [PATCH 34/63] Add mask css rule to csstidy allow list (#40655) * Add mask css rule to allow list * Add changelog --------- Co-authored-by: Ben Zhu --- .../changelog/benz56-add-mask-css-rule-to-allowlist | 4 ++++ .../src/features/custom-css/csstidy/data.inc.php | 1 + 2 files changed, 5 insertions(+) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/benz56-add-mask-css-rule-to-allowlist diff --git a/projects/packages/jetpack-mu-wpcom/changelog/benz56-add-mask-css-rule-to-allowlist b/projects/packages/jetpack-mu-wpcom/changelog/benz56-add-mask-css-rule-to-allowlist new file mode 100644 index 0000000000000..0841f37a48c0e --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/benz56-add-mask-css-rule-to-allowlist @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +CSS Tidy: add css rule mask to allowlist diff --git a/projects/packages/jetpack-mu-wpcom/src/features/custom-css/csstidy/data.inc.php b/projects/packages/jetpack-mu-wpcom/src/features/custom-css/csstidy/data.inc.php index 87163a845e502..760f24d8d2782 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/custom-css/csstidy/data.inc.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/custom-css/csstidy/data.inc.php @@ -549,6 +549,7 @@ $GLOBALS['csstidy']['all_properties']['marquee-play-count'] = 'CSS3.0'; $GLOBALS['csstidy']['all_properties']['marquee-speed'] = 'CSS3.0'; $GLOBALS['csstidy']['all_properties']['marquee-style'] = 'CSS3.0'; +$GLOBALS['csstidy']['all_properties']['mask'] = 'CSS3.0'; $GLOBALS['csstidy']['all_properties']['mask-clip'] = 'CSS3.0'; $GLOBALS['csstidy']['all_properties']['mask-composite'] = 'CSS3.0'; $GLOBALS['csstidy']['all_properties']['mask-image'] = 'CSS3.0'; From cff42cc9319a4141640c16e7c81aa881b35e9936 Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Wed, 18 Dec 2024 22:44:49 -0500 Subject: [PATCH 35/63] My Jetpack: Add Backup 'needs-attention' status when backups are failing. (#40454) * Add red bubble alert if Backups failing. --- .../packages/my-jetpack/.phan/baseline.php | 2 +- .../components/product-card/action-button.tsx | 12 +++ .../_inc/components/product-card/index.tsx | 3 +- .../_inc/components/product-card/status.tsx | 3 + .../packages/my-jetpack/_inc/constants.ts | 1 + .../add-backup-needs-attention-status | 4 + .../my-jetpack/src/class-products.php | 2 + .../my-jetpack/src/products/class-backup.php | 97 ++++++++++++++++++- .../my-jetpack/src/products/class-product.php | 14 +++ 9 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 projects/packages/my-jetpack/changelog/add-backup-needs-attention-status diff --git a/projects/packages/my-jetpack/.phan/baseline.php b/projects/packages/my-jetpack/.phan/baseline.php index dd9d2bb32018e..50deef4b803f2 100644 --- a/projects/packages/my-jetpack/.phan/baseline.php +++ b/projects/packages/my-jetpack/.phan/baseline.php @@ -37,7 +37,7 @@ 'src/class-rest-zendesk-chat.php' => ['PhanParamTooMany'], 'src/class-wpcom-products.php' => ['PhanTypeMismatchReturnProbablyReal'], 'src/products/class-anti-spam.php' => ['PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchPropertyDefault'], - 'src/products/class-backup.php' => ['PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchPropertyDefault'], + 'src/products/class-backup.php' => ['PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchPropertyDefault', 'PhanTypeSuspiciousNonTraversableForeach'], 'src/products/class-boost.php' => ['PhanTypeMismatchPropertyDefault'], 'src/products/class-creator.php' => ['PhanTypeMismatchPropertyDefault', 'PhanTypeMismatchReturnProbablyReal'], 'src/products/class-crm.php' => ['PhanTypeMismatchPropertyDefault'], diff --git a/projects/packages/my-jetpack/_inc/components/product-card/action-button.tsx b/projects/packages/my-jetpack/_inc/components/product-card/action-button.tsx index 61984095e7a30..82878556ba7c6 100644 --- a/projects/packages/my-jetpack/_inc/components/product-card/action-button.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-card/action-button.tsx @@ -44,6 +44,8 @@ const ActionButton: FC< ActionButtonProps > = ( { upgradeInInterstitial, isOwned, } ) => { + const troubleshootBackupsUrl = + 'https://jetpack.com/support/backup/troubleshooting-jetpack-backup/'; const [ isDropdownOpen, setIsDropdownOpen ] = useState( false ); const [ currentAction, setCurrentAction ] = useState< ComponentProps< typeof Button > >( {} ); const { detail } = useProduct( slug ); @@ -208,6 +210,16 @@ const ActionButton: FC< ActionButtonProps > = ( { PRODUCT_STATUSES.EXPIRED in primaryActionOverride && primaryActionOverride[ PRODUCT_STATUSES.EXPIRED ] ), }; + case PRODUCT_STATUSES.NEEDS_ATTENTION: + return { + ...buttonState, + href: troubleshootBackupsUrl, + variant: 'primary', + label: __( 'Troubleshoot', 'jetpack-my-jetpack' ), + ...( primaryActionOverride && + PRODUCT_STATUSES.NEEDS_ATTENTION in primaryActionOverride && + primaryActionOverride[ PRODUCT_STATUSES.NEEDS_ATTENTION ] ), + }; default: return null; } diff --git a/projects/packages/my-jetpack/_inc/components/product-card/index.tsx b/projects/packages/my-jetpack/_inc/components/product-card/index.tsx index 72b536197a895..5fbe96fd6f7e5 100644 --- a/projects/packages/my-jetpack/_inc/components/product-card/index.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-card/index.tsx @@ -70,7 +70,8 @@ const ProductCard: FC< ProductCardProps > = props => { const { ownedProducts } = getMyJetpackWindowInitialState( 'lifecycleStats' ); const isOwned = ownedProducts?.includes( slug ); - const isError = status === PRODUCT_STATUSES.EXPIRED; + const isError = + status === PRODUCT_STATUSES.EXPIRED || status === PRODUCT_STATUSES.NEEDS_ATTENTION; const isWarning = status === PRODUCT_STATUSES.EXPIRING_SOON; const isAbsent = status === PRODUCT_STATUSES.ABSENT || status === PRODUCT_STATUSES.ABSENT_WITH_PLAN; diff --git a/projects/packages/my-jetpack/_inc/components/product-card/status.tsx b/projects/packages/my-jetpack/_inc/components/product-card/status.tsx index 51e6d4ed429e7..f46baca25a7d3 100644 --- a/projects/packages/my-jetpack/_inc/components/product-card/status.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-card/status.tsx @@ -40,6 +40,8 @@ const getStatusLabel: StatusStateFunction = ( status, isOwned ) => { const inactiveText = __( 'Inactive', 'jetpack-my-jetpack' ); return isOwned ? needsPlanText : inactiveText; } + case PRODUCT_STATUSES.NEEDS_ATTENTION: + return __( 'Needs attention', 'jetpack-my-jetpack' ); default: return __( 'Inactive', 'jetpack-my-jetpack' ); } @@ -62,6 +64,7 @@ const getStatusClassName: StatusStateFunction = ( status, isOwned ) => { case PRODUCT_STATUSES.NEEDS_PLAN: return isOwned ? styles.warning : styles.inactive; case PRODUCT_STATUSES.EXPIRED: + case PRODUCT_STATUSES.NEEDS_ATTENTION: return styles.error; default: return styles.inactive; diff --git a/projects/packages/my-jetpack/_inc/constants.ts b/projects/packages/my-jetpack/_inc/constants.ts index af4cdacb11fe5..1503114ca4de0 100644 --- a/projects/packages/my-jetpack/_inc/constants.ts +++ b/projects/packages/my-jetpack/_inc/constants.ts @@ -41,4 +41,5 @@ export const PRODUCT_STATUSES = { CAN_UPGRADE: 'can_upgrade', EXPIRING_SOON: 'expiring', EXPIRED: 'expired', + NEEDS_ATTENTION: 'needs_attention', }; diff --git a/projects/packages/my-jetpack/changelog/add-backup-needs-attention-status b/projects/packages/my-jetpack/changelog/add-backup-needs-attention-status new file mode 100644 index 0000000000000..ba8a03a3a2ebc --- /dev/null +++ b/projects/packages/my-jetpack/changelog/add-backup-needs-attention-status @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +My Jetpack: Add 'Needs attention' status to Backup product card when Backups are failing. diff --git a/projects/packages/my-jetpack/src/class-products.php b/projects/packages/my-jetpack/src/class-products.php index 8f8681fdb2ab8..33e894329a507 100644 --- a/projects/packages/my-jetpack/src/class-products.php +++ b/projects/packages/my-jetpack/src/class-products.php @@ -29,6 +29,7 @@ class Products { const STATUS_NEEDS_PLAN = 'needs_plan'; const STATUS_NEEDS_ACTIVATION = 'needs_activation'; const STATUS_NEEDS_FIRST_SITE_CONNECTION = 'needs_first_site_connection'; + const STATUS_NEEDS_ATTENTION = 'needs_attention'; /** * List of statuses that display the module as disabled @@ -107,6 +108,7 @@ class Products { self::STATUS_NEEDS_PLAN, self::STATUS_NEEDS_ACTIVATION, self::STATUS_NEEDS_FIRST_SITE_CONNECTION, + self::STATUS_NEEDS_ATTENTION, ); /** diff --git a/projects/packages/my-jetpack/src/products/class-backup.php b/projects/packages/my-jetpack/src/products/class-backup.php index 8566a4b8738bf..bee360c09ee2a 100644 --- a/projects/packages/my-jetpack/src/products/class-backup.php +++ b/projects/packages/my-jetpack/src/products/class-backup.php @@ -1,6 +1,6 @@ 2 ), null, 'wpcom' ); + $response = Client::wpcom_json_api_request_as_blog( + sprintf( '/sites/%d/rewind', $site_id ) . '?force=wpcom', + '2', + array( 'timeout' => 2 ), + null, + 'wpcom' + ); if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { $status = new WP_Error( 'rewind_state_fetch_failed' ); @@ -200,6 +205,90 @@ private static function get_state_from_wpcom() { return $status; } + /** + * Hits the wpcom api to retrieve the last 10 backup records. + * + * @return Object|WP_Error + */ + public static function get_latest_backups() { + static $backups = null; + + if ( $backups !== null ) { + return $backups; + } + + $site_id = \Jetpack_Options::get_option( 'id' ); + $response = Client::wpcom_json_api_request_as_blog( + sprintf( '/sites/%d/rewind/backups', $site_id ) . '?force=wpcom', + '2', + array( 'timeout' => 2 ), + null, + 'wpcom' + ); + + if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { + $backups = new WP_Error( 'rewind_backups_fetch_failed' ); + return $backups; + } + + $body = wp_remote_retrieve_body( $response ); + $backups = json_decode( $body ); + return $backups; + } + + /** + * Determines whether the module/plugin/product needs the users attention. + * Typically due to some sort of error where user troubleshooting is needed. + * + * @return boolean|array + */ + public static function does_module_need_attention() { + $backup_failed_status = false; + // First check the status of Rewind for failure. + $rewind_state = self::get_state_from_wpcom(); + if ( ! is_wp_error( $rewind_state ) ) { + $backup_failure_reasons = array( + 'unknown_error', + 'no_site_found', + 'missing_plan', + 'no_connected_jetpack', + 'no_connected_jetpack_with_credentials', + 'multisite_not_supported', + 'host_not_supported', + 'vp_active_on_site', + ); + if ( $rewind_state->state === 'unavailable' && ! empty( $rewind_state->reason ) && in_array( $rewind_state->reason, $backup_failure_reasons, true ) ) { + $backup_failed_status = array( + 'status' => $rewind_state->reason, + 'last_updated' => $rewind_state->last_updated, + ); + } + } + // Next check for a failed last backup. + $latest_backups = self::get_latest_backups(); + if ( ! is_wp_error( $latest_backups ) ) { + // Get the last/latest backup record. + $last_backup = null; + foreach ( $latest_backups as $backup ) { + if ( $backup->is_backup ) { + $last_backup = $backup; + break; + } + } + + if ( $last_backup && isset( $last_backup->status ) ) { + if ( $last_backup->status === 'not-accessible' || $last_backup->status === 'error' || $last_backup->status === 'credential-error' ) { + $backup_failed_status = array( + 'status' => $last_backup->status, + 'last_updated' => $last_backup->last_updated, + ); + } + } + } + + return $backup_failed_status; + } + /** * Return product bundles list * that supports the product. diff --git a/projects/packages/my-jetpack/src/products/class-product.php b/projects/packages/my-jetpack/src/products/class-product.php index 128d00ea3307c..b2262eddd6b4a 100644 --- a/projects/packages/my-jetpack/src/products/class-product.php +++ b/projects/packages/my-jetpack/src/products/class-product.php @@ -10,6 +10,7 @@ use Automattic\Jetpack\Connection\Client; use Automattic\Jetpack\Connection\Manager as Connection_Manager; use Automattic\Jetpack\Modules; +use Automattic\Jetpack\My_Jetpack\Products\Backup; use Automattic\Jetpack\Plugins_Installer; use Automattic\Jetpack\Status; use Jetpack_Options; @@ -716,6 +717,9 @@ public static function get_status() { } elseif ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) { $status = Products::STATUS_USER_CONNECTION_ERROR; } elseif ( static::has_paid_plan_for_product() ) { + if ( static::$slug === 'backup' && Backup::does_module_need_attention() ) { + $status = Products::STATUS_NEEDS_ATTENTION; + } if ( static::is_paid_plan_expired() ) { $status = Products::STATUS_EXPIRED; } elseif ( static::is_paid_plan_expiring() ) { @@ -987,4 +991,14 @@ public static function install_and_activate_standalone() { return true; } + + /** + * Determines whether the module/plugin/product needs the users attention. + * Typically due to some sort of error where user troubleshooting is needed. + * + * @return boolean + */ + public static function does_module_need_attention() { + return false; + } } From f12d3de6f331fb7a5b689cbbc297298dad45a9a8 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Wed, 18 Dec 2024 20:37:19 -0800 Subject: [PATCH 36/63] Move wpcom/v2/publicize/connections endpoint to publicize package (#40607) * Move wpcom/v2/publicize/connections endpoint to publicize package * Fix Fatals in Publicize base class on WPCOM * Now we can use v2 for proxy requests * Phan needs a fan because I am not * Update baseline.php * Use publicize for fields for now instead of a new class * Move initialization logic to Publicize_Setup * Clean up * Update phan baseline * Use null for the status field as default * Default external_handle to null * Return null in all cases for external_handle * Make get_username use get_external_handle * Rename rest-endpoints to rest-api * Clean up prepare_item_for_response * Update baseline.php * Use schema caching --- .../packages/publicize/.phan/baseline.php | 8 +- projects/packages/publicize/.phan/config.php | 1 + projects/packages/publicize/actions.php | 4 +- ...pcom-v2-publicize-connections-to-publicize | 4 + .../publicize/src/class-publicize-base.php | 40 ++- .../publicize/src/class-publicize-setup.php | 27 ++ .../src/rest-api/class-base-controller.php | 85 +++++ .../rest-api/class-connections-controller.php | 304 ++++++++++++++++++ projects/plugins/jetpack/.phan/baseline.php | 3 +- .../wpcom-endpoints/publicize-connections.php | 23 +- ...pcom-v2-publicize-connections-to-publicize | 4 + 11 files changed, 463 insertions(+), 40 deletions(-) create mode 100644 projects/packages/publicize/changelog/update-move-wpcom-v2-publicize-connections-to-publicize create mode 100644 projects/packages/publicize/src/rest-api/class-base-controller.php create mode 100644 projects/packages/publicize/src/rest-api/class-connections-controller.php create mode 100644 projects/plugins/jetpack/changelog/update-move-wpcom-v2-publicize-connections-to-publicize diff --git a/projects/packages/publicize/.phan/baseline.php b/projects/packages/publicize/.phan/baseline.php index 276871afd3219..ae1e8cfe766d4 100644 --- a/projects/packages/publicize/.phan/baseline.php +++ b/projects/packages/publicize/.phan/baseline.php @@ -9,17 +9,18 @@ */ return [ // # Issue statistics: - // PhanTypeMismatchArgument : 7 occurrences // PhanPluginDuplicateConditionalNullCoalescing : 6 occurrences + // PhanTypeMismatchArgument : 6 occurrences // PhanTypeMismatchArgumentNullable : 3 occurrences // PhanDeprecatedFunction : 2 occurrences + // PhanPluginMixedKeyNoKey : 2 occurrences // PhanPossiblyUndeclaredVariable : 2 occurrences // PhanTypeMismatchReturnProbablyReal : 2 occurrences // PhanTypeMissingReturn : 2 occurrences // PhanImpossibleCondition : 1 occurrence + // PhanNoopNew : 1 occurrence // PhanParamSignatureMismatch : 1 occurrence // PhanPluginDuplicateExpressionAssignmentOperation : 1 occurrence - // PhanPluginMixedKeyNoKey : 1 occurrence // PhanPluginSimplifyExpressionBool : 1 occurrence // PhanSuspiciousMagicConstant : 1 occurrence // PhanTypeMismatchArgumentNullableInternal : 1 occurrence @@ -33,10 +34,11 @@ 'src/class-connections-post-field.php' => ['PhanPluginDuplicateConditionalNullCoalescing'], 'src/class-keyring-helper.php' => ['PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchDefault'], 'src/class-publicize-base.php' => ['PhanImpossibleCondition', 'PhanPluginDuplicateConditionalNullCoalescing', 'PhanPluginSimplifyExpressionBool', 'PhanSuspiciousMagicConstant', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchArgumentNullableInternal', 'PhanTypeMismatchDimFetch', 'PhanTypeMismatchReturn'], - 'src/class-publicize-setup.php' => ['PhanTypeMismatchArgument'], + 'src/class-publicize-setup.php' => ['PhanNoopNew', 'PhanTypeMismatchArgument'], 'src/class-publicize-ui.php' => ['PhanPluginDuplicateExpressionAssignmentOperation', 'PhanTypeMismatchReturnProbablyReal'], 'src/class-publicize.php' => ['PhanParamSignatureMismatch', 'PhanPossiblyUndeclaredVariable', 'PhanTypeMismatchArgument', 'PhanTypeMissingReturn'], 'src/class-rest-controller.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeMismatchReturnProbablyReal'], + 'src/rest-api/class-connections-controller.php' => ['PhanPluginMixedKeyNoKey'], 'src/social-image-generator/class-post-settings.php' => ['PhanPluginDuplicateConditionalNullCoalescing'], 'src/social-image-generator/class-rest-settings-controller.php' => ['PhanPluginMixedKeyNoKey'], 'src/social-image-generator/class-settings.php' => ['PhanPluginDuplicateConditionalNullCoalescing'], diff --git a/projects/packages/publicize/.phan/config.php b/projects/packages/publicize/.phan/config.php index 7030e2aa8176c..075dd16643b9e 100644 --- a/projects/packages/publicize/.phan/config.php +++ b/projects/packages/publicize/.phan/config.php @@ -24,6 +24,7 @@ __DIR__ . '/../../../plugins/jetpack/_inc/lib/admin-pages/class.jetpack-admin-page.php', // class Jetpack_Admin_Page __DIR__ . '/../../../plugins/jetpack/modules/subscriptions.php', // class Jetpack_Subscriptions __DIR__ . '/../../../plugins/jetpack/functions.global.php', // function jetpack_render_tos_blurb + __DIR__ . '/../../../plugins/jetpack/_inc/lib/core-api/load-wpcom-endpoints.php', // function wpcom_rest_api_v2_load_plugin ), ) ); diff --git a/projects/packages/publicize/actions.php b/projects/packages/publicize/actions.php index 8a1413391ae13..27748d4f9ffd0 100644 --- a/projects/packages/publicize/actions.php +++ b/projects/packages/publicize/actions.php @@ -8,12 +8,12 @@ // If WordPress's plugin API is available already, use it. If not, // drop data into `$wp_filter` for `WP_Hook::build_preinitialized_hooks()`. if ( function_exists( 'add_action' ) ) { - add_action( 'plugins_loaded', array( Automattic\Jetpack\Publicize\Publicize_Assets::class, 'configure' ), 1 ); + add_action( 'plugins_loaded', array( Automattic\Jetpack\Publicize\Publicize_Setup::class, 'pre_initialization' ), 1 ); } else { global $wp_filter; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $wp_filter['plugins_loaded'][1][] = array( 'accepted_args' => 0, - 'function' => array( Automattic\Jetpack\Publicize\Publicize_Assets::class, 'configure' ), + 'function' => array( Automattic\Jetpack\Publicize\Publicize_Setup::class, 'pre_initialization' ), ); } diff --git a/projects/packages/publicize/changelog/update-move-wpcom-v2-publicize-connections-to-publicize b/projects/packages/publicize/changelog/update-move-wpcom-v2-publicize-connections-to-publicize new file mode 100644 index 0000000000000..9d87109f63b2c --- /dev/null +++ b/projects/packages/publicize/changelog/update-move-wpcom-v2-publicize-connections-to-publicize @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Moved wpcom/v2/publicize/connections endpoint to publicize package diff --git a/projects/packages/publicize/src/class-publicize-base.php b/projects/packages/publicize/src/class-publicize-base.php index b88bd1a6b12ca..c2e6c044cc5bf 100644 --- a/projects/packages/publicize/src/class-publicize-base.php +++ b/projects/packages/publicize/src/class-publicize-base.php @@ -497,8 +497,8 @@ public function get_profile_link( $service_name, $connection ) { return 'https://instagram.com/' . $cmeta['connection_data']['meta']['username']; } - if ( 'threads' === $service_name && isset( $connection['external_name'] ) ) { - return 'https://www.threads.net/@' . $connection['external_name']; + if ( 'threads' === $service_name && isset( $cmeta['external_name'] ) ) { + return 'https://www.threads.net/@' . $cmeta['external_name']; } if ( 'mastodon' === $service_name && isset( $cmeta['external_name'] ) ) { @@ -527,7 +527,7 @@ public function get_profile_link( $service_name, $connection ) { } $profile_url_query = wp_parse_url( $cmeta['connection_data']['meta']['profile_url'], PHP_URL_QUERY ); - $profile_url_query_args = null; + $profile_url_query_args = array(); wp_parse_str( $profile_url_query, $profile_url_query_args ); $id = null; @@ -589,17 +589,35 @@ public function get_display_name( $service_name, $connection ) { * @return string */ public function get_username( $service_name, $connection ) { + $handle = $this->get_external_handle( $service_name, $connection ); + + return $handle ?? $this->get_display_name( $service_name, $connection ); + } + + /** + * Returns the external handle for the Connection. + * + * @param string $service_name 'facebook', 'linkedin', etc. + * @param object|array $connection The Connection object (WordPress.com) or array (Jetpack). + * @return string|null + */ + public function get_external_handle( $service_name, $connection ) { $cmeta = $this->get_connection_meta( $connection ); - if ( 'mastodon' === $service_name && isset( $cmeta['external_display'] ) ) { - return $cmeta['external_display']; - } + switch ( $service_name ) { + case 'mastodon': + return $cmeta['external_display'] ?? null; - if ( isset( $cmeta['connection_data']['meta']['username'] ) ) { - return $cmeta['connection_data']['meta']['username']; - } + case 'bluesky': + case 'threads': + return $cmeta['external_name'] ?? null; - return $this->get_display_name( $service_name, $connection ); + case 'instagram-business': + return $cmeta['connection_data']['meta']['username'] ?? null; + + default: + return null; + } } /** @@ -608,7 +626,7 @@ public function get_username( $service_name, $connection ) { * @param object|array $connection The Connection object (WordPress.com) or array (Jetpack). * @return string */ - private function get_profile_picture( $connection ) { + public function get_profile_picture( $connection ) { $cmeta = $this->get_connection_meta( $connection ); if ( isset( $cmeta['profile_picture'] ) ) { diff --git a/projects/packages/publicize/src/class-publicize-setup.php b/projects/packages/publicize/src/class-publicize-setup.php index 383ef289fab5e..dc4dc5a3997c2 100644 --- a/projects/packages/publicize/src/class-publicize-setup.php +++ b/projects/packages/publicize/src/class-publicize-setup.php @@ -7,6 +7,9 @@ namespace Automattic\Jetpack\Publicize; +use Automattic\Jetpack\Publicize\REST_API\Connections_Controller; +use Automattic\Jetpack\Status\Host; + /** * The class to configure and initialize the publicize package. */ @@ -26,6 +29,30 @@ public static function configure() { add_action( 'jetpack_feature_publicize_enabled', array( __CLASS__, 'on_jetpack_feature_publicize_enabled' ) ); } + /** + * Initialization of publicize logic that should always be loaded. + */ + public static function pre_initialization() { + + $is_wpcom = ( new Host() )->is_wpcom_simple(); + + // Assets are to be loaded in all cases. + Publicize_Assets::configure(); + + $rest_controllers = array( + Connections_Controller::class, + ); + + // Load the REST controllers. + foreach ( $rest_controllers as $controller ) { + if ( $is_wpcom ) { + wpcom_rest_api_v2_load_plugin( $controller ); + } else { + new $controller(); + } + } + } + /** * To configure the publicize package, when called via the Config package. */ diff --git a/projects/packages/publicize/src/rest-api/class-base-controller.php b/projects/packages/publicize/src/rest-api/class-base-controller.php new file mode 100644 index 0000000000000..acf0aa0c78cc2 --- /dev/null +++ b/projects/packages/publicize/src/rest-api/class-base-controller.php @@ -0,0 +1,85 @@ +wpcom_is_wpcom_only_endpoint = true; + } + + /** + * Check if we are on WPCOM. + * + * @return bool + */ + public static function is_wpcom() { + return ( new Host() )->is_wpcom_simple(); + } + + /** + * Filters out data based on ?_fields= request parameter + * + * @param array $item Item to prepare. + * @param WP_REST_Request $request Full details about the request. + * + * @return WP_REST_Response filtered item + */ + public function prepare_item_for_response( $item, $request ) { + + $fields = $this->get_fields_for_response( $request ); + + $response_data = array(); + foreach ( $item as $field => $value ) { + if ( rest_is_field_included( $field, $fields ) ) { + $response_data[ $field ] = $value; + } + } + + return rest_ensure_response( $response_data ); + } + + /** + * Verify that user can access Publicize data + * + * @return true|WP_Error + */ + public function get_items_permission_check() { + global $publicize; + + if ( ! $publicize ) { + return new WP_Error( + 'publicize_not_available', + __( 'Sorry, Jetpack Social is not available on your site right now.', 'jetpack-publicize-pkg' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + if ( $publicize->current_user_can_access_publicize_data() ) { + return true; + } + + return new WP_Error( + 'invalid_user_permission_publicize', + __( 'Sorry, you are not allowed to access Jetpack Social data on this site.', 'jetpack-publicize-pkg' ), + array( 'status' => rest_authorization_required_code() ) + ); + } +} diff --git a/projects/packages/publicize/src/rest-api/class-connections-controller.php b/projects/packages/publicize/src/rest-api/class-connections-controller.php new file mode 100644 index 0000000000000..f1710bca05a93 --- /dev/null +++ b/projects/packages/publicize/src/rest-api/class-connections-controller.php @@ -0,0 +1,304 @@ +namespace = 'wpcom/v2'; + $this->rest_base = 'publicize/connections'; + + add_action( 'rest_api_init', array( $this, 'register_routes' ) ); + } + + /** + * Register the routes. + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permission_check' ), + 'args' => array( + 'test_connections' => array( + 'type' => 'boolean', + 'description' => __( 'Whether to test connections.', 'jetpack-publicize-pkg' ), + ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + } + + /** + * Schema for the endpoint. + * + * @return array + */ + public function get_item_schema() { + if ( $this->schema ) { + return $this->add_additional_fields_schema( $this->schema ); + } + $deprecated_fields = array( + 'id' => array( + 'type' => 'string', + 'description' => __( 'Unique identifier for the Jetpack Social connection.', 'jetpack-publicize-pkg' ) . ' ' . sprintf( + /* translators: %s is the new field name */ + __( 'Deprecated in favor of %s.', 'jetpack-publicize-pkg' ), + 'connection_id' + ), + ), + 'username' => array( + 'type' => 'string', + 'description' => __( 'Username of the connected account.', 'jetpack-publicize-pkg' ) . ' ' . sprintf( + /* translators: %s is the new field name */ + __( 'Deprecated in favor of %s.', 'jetpack-publicize-pkg' ), + 'external_handle' + ), + ), + 'profile_display_name' => array( + 'type' => 'string', + 'description' => __( 'The name to display in the profile of the connected account.', 'jetpack-publicize-pkg' ) . ' ' . sprintf( + /* translators: %s is the new field name */ + __( 'Deprecated in favor of %s.', 'jetpack-publicize-pkg' ), + 'display_name' + ), + ), + 'global' => array( + 'type' => 'boolean', + 'description' => __( 'Is this connection available to all users?', 'jetpack-publicize-pkg' ) . ' ' . sprintf( + /* translators: %s is the new field name */ + __( 'Deprecated in favor of %s.', 'jetpack-publicize-pkg' ), + 'shared' + ), + ), + ); + + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'jetpack-publicize-connection', + 'type' => 'object', + 'properties' => array_merge( + $deprecated_fields, + array( + 'connection_id' => array( + 'type' => 'string', + 'description' => __( 'Connection ID of the connected account.', 'jetpack-publicize-pkg' ), + ), + 'display_name' => array( + 'type' => 'string', + 'description' => __( 'Display name of the connected account.', 'jetpack-publicize-pkg' ), + ), + 'external_handle' => array( + 'type' => 'string', + 'description' => __( 'The external handle or username of the connected account.', 'jetpack-publicize-pkg' ), + ), + 'external_id' => array( + 'type' => 'string', + 'description' => __( 'The external ID of the connected account.', 'jetpack-publicize-pkg' ), + ), + 'profile_link' => array( + 'type' => 'string', + 'description' => __( 'Profile link of the connected account.', 'jetpack-publicize-pkg' ), + ), + 'profile_picture' => array( + 'type' => 'string', + 'description' => __( 'URL of the profile picture of the connected account.', 'jetpack-publicize-pkg' ), + ), + 'service_label' => array( + 'type' => 'string', + 'description' => __( 'Human-readable label for the Jetpack Social service.', 'jetpack-publicize-pkg' ), + ), + 'service_name' => array( + 'type' => 'string', + 'description' => __( 'Alphanumeric identifier for the Jetpack Social service.', 'jetpack-publicize-pkg' ), + ), + 'shared' => array( + 'type' => 'boolean', + 'description' => __( 'Whether the connection is shared with other users.', 'jetpack-publicize-pkg' ), + ), + 'status' => array( + 'type' => 'string', + 'description' => __( 'The connection status.', 'jetpack-publicize-pkg' ), + 'enum' => array( + 'ok', + 'broken', + ), + ), + 'user_id' => array( + 'type' => 'integer', + 'description' => __( 'ID of the user the connection belongs to.', 'jetpack-publicize-pkg' ), + ), + ) + ), + ); + + $this->schema = $schema; + + return $this->add_additional_fields_schema( $schema ); + } + + /** + * Get all connections. Meant to be called directly only on WPCOM. + * + * @param bool $run_tests Whether to run tests on the connections. + * + * @return array + */ + protected static function get_all_connections( $run_tests = false ) { + /** + * Publicize instance. + * + * @var \Automattic\Jetpack\Publicize\Publicize $publicize + */ + global $publicize; + + $items = array(); + + $test_results = $run_tests ? self::get_connections_test_status() : array(); + + foreach ( (array) $publicize->get_services( 'connected' ) as $service_name => $connections ) { + foreach ( $connections as $connection ) { + + $connection_id = $publicize->get_connection_id( $connection ); + + $connection_meta = $publicize->get_connection_meta( $connection ); + $connection_data = $connection_meta['connection_data']; + + $items[] = array( + 'connection_id' => $connection_id, + 'display_name' => $publicize->get_display_name( $service_name, $connection ), + 'external_handle' => $publicize->get_external_handle( $service_name, $connection ), + 'external_id' => $connection_meta['external_id'] ?? '', + 'profile_link' => $publicize->get_profile_link( $service_name, $connection ), + 'profile_picture' => $publicize->get_profile_picture( $connection ), + 'service_label' => Publicize::get_service_label( $service_name ), + 'service_name' => $service_name, + 'shared' => ! $connection_data['user_id'], + 'status' => $test_results[ $connection_id ] ?? null, + 'user_id' => (int) $connection_data['user_id'], + + // Deprecated fields. + 'id' => (string) $publicize->get_connection_unique_id( $connection ), + 'username' => $publicize->get_username( $service_name, $connection ), + 'profile_display_name' => ! empty( $connection_meta['profile_display_name'] ) ? $connection_meta['profile_display_name'] : '', + // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- We expect an integer, but do loose comparison below in case some other type is stored. + 'global' => 0 == $connection_data['user_id'], + + ); + } + } + + return $items; + } + + /** + * Get a list of publicize connections. + * + * @param bool $run_tests Whether to run tests on the connections. + * + * @return array + */ + public static function get_connections( $run_tests = false ) { + if ( self::is_wpcom() ) { + return self::get_all_connections( $run_tests ); + } + + $site_id = Manager::get_site_id( true ); + if ( ! $site_id ) { + return array(); + } + + $path = add_query_arg( + array( 'test_connections' => $run_tests ), + sprintf( '/sites/%d/publicize/connections', $site_id ) + ); + + $response = Client::wpcom_json_api_request_as_user( $path, 'v2', array( 'method' => 'GET' ) ); + + if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { + // TODO log error. + return array(); + } + + $body = wp_remote_retrieve_body( $response ); + + $items = json_decode( $body, true ); + + return $items ? $items : array(); + } + + /** + * Get list of connected Publicize connections. + * + * @param WP_REST_Request $request Full details about the request. + * + * @return WP_REST_Response suitable for 1-page collection + */ + public function get_items( $request ) { + $items = array(); + + $run_tests = $request->get_param( 'test_connections' ); + + foreach ( self::get_connections( $run_tests ) as $item ) { + $data = $this->prepare_item_for_response( $item, $request ); + + $items[] = $this->prepare_response_for_collection( $data ); + } + + $response = rest_ensure_response( $items ); + $response->header( 'X-WP-Total', (string) count( $items ) ); + $response->header( 'X-WP-TotalPages', '1' ); + + return $response; + } + + /** + * Get the connections test status. + * + * @return array + */ + protected static function get_connections_test_status() { + /** + * Publicize instance. + * + * @var \Automattic\Jetpack\Publicize\Publicize $publicize + */ + global $publicize; + + $test_results = $publicize->get_publicize_conns_test_results(); + + $test_results_map = array(); + + foreach ( $test_results as $test_result ) { + // Compare to `true` because the API returns a 'must_reauth' for LinkedIn. + $test_results_map[ $test_result['connectionID'] ] = true === $test_result['connectionTestPassed'] ? 'ok' : 'broken'; + } + + return $test_results_map; + } +} diff --git a/projects/plugins/jetpack/.phan/baseline.php b/projects/plugins/jetpack/.phan/baseline.php index d3614e00bc549..741c58a413969 100644 --- a/projects/plugins/jetpack/.phan/baseline.php +++ b/projects/plugins/jetpack/.phan/baseline.php @@ -81,7 +81,6 @@ // PhanTypeMismatchArgumentInternalProbablyReal : 2 occurrences // PhanUndeclaredClassInCallable : 2 occurrences // PhanUndeclaredClassMethod : 2 occurrences - // PhanCommentParamWithoutRealParam : 1 occurrence // PhanDeprecatedPartiallySupportedCallable : 1 occurrence // PhanEmptyForeach : 1 occurrence // PhanPluginDuplicateSwitchCase : 1 occurrence @@ -139,7 +138,7 @@ '_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php' => ['PhanPluginMixedKeyNoKey'], '_inc/lib/core-api/wpcom-endpoints/memberships.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanPluginUnreachableCode', 'PhanTypeArraySuspicious'], '_inc/lib/core-api/wpcom-endpoints/publicize-connection-test-results.php' => ['PhanPluginMixedKeyNoKey', 'PhanTypeMismatchArgument'], - '_inc/lib/core-api/wpcom-endpoints/publicize-connections.php' => ['PhanParamSignatureMismatch', 'PhanPluginMixedKeyNoKey', 'PhanTypeMismatchArgument'], + '_inc/lib/core-api/wpcom-endpoints/publicize-connections.php' => ['PhanParamSignatureMismatch', 'PhanTypeMismatchArgument'], '_inc/lib/core-api/wpcom-endpoints/publicize-services.php' => ['PhanParamSignatureMismatch', 'PhanPluginMixedKeyNoKey', 'PhanTypeMismatchArgument'], '_inc/lib/core-api/wpcom-endpoints/service-api-keys.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeArraySuspicious', 'PhanTypeMismatchReturnProbablyReal'], '_inc/lib/core-api/wpcom-endpoints/trait-wpcom-rest-api-proxy-request-trait.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanUndeclaredProperty'], diff --git a/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php index 67fa5b25ced9e..35ffcbed109a9 100644 --- a/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php +++ b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php @@ -29,32 +29,11 @@ class WPCOM_REST_API_V2_Endpoint_List_Publicize_Connections extends WP_REST_Cont */ public $wpcom_is_wpcom_only_endpoint = true; - /** - * Constructor. - */ - public function __construct() { - $this->namespace = 'wpcom/v2'; - $this->rest_base = 'publicize/connections'; - - add_action( 'rest_api_init', array( $this, 'register_routes' ) ); - } - /** * Called automatically on `rest_api_init()`. */ public function register_routes() { - register_rest_route( - $this->namespace, - '/' . $this->rest_base, - array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permission_check' ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); + // Endpoint moved to publicize package. } /** diff --git a/projects/plugins/jetpack/changelog/update-move-wpcom-v2-publicize-connections-to-publicize b/projects/plugins/jetpack/changelog/update-move-wpcom-v2-publicize-connections-to-publicize new file mode 100644 index 0000000000000..5cb09fcc5fe9d --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-move-wpcom-v2-publicize-connections-to-publicize @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Moved wpcom/v2/publicize/connections endpoint to publicize package From 56c0fbc7c6bca58785d5a3a46ba12f83292cd9fd Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Thu, 19 Dec 2024 00:01:28 -0500 Subject: [PATCH 37/63] My Jetpack: Add Backup 'needs-attention' red bubble and notice banner (#40512) * Add redbubble & notice when backups are failing. --- .../notice-component-minor-css-adjustment | 5 + .../components/notice/style.module.scss | 6 + .../_inc/components/info-tooltip/index.tsx | 2 + .../my-jetpack-screen/styles.module.scss | 1 + .../backup-card/index.jsx | 56 +++++- .../backup-card/style.module.scss | 42 +++++ .../protect-card/style.scss | 9 - .../product-cards-section/style.module.scss | 12 ++ .../hooks/use-notification-watcher/index.ts | 10 +- .../use-backup-needs-attention-notice.tsx | 115 ++++++++++++ .../use-get-readable-failed-backup-reason.tsx | 173 ++++++++++++++++++ .../my-jetpack/_inc/utils/apply-timezone.ts | 15 ++ ...dd-backup-redbubble-needs-attention-notice | 4 + projects/packages/my-jetpack/global.d.ts | 33 +++- .../my-jetpack/src/class-initializer.php | 33 +++- .../my-jetpack/src/class-products.php | 1 + .../my-jetpack/src/products/class-backup.php | 18 +- 17 files changed, 502 insertions(+), 33 deletions(-) create mode 100644 projects/js-packages/components/changelog/notice-component-minor-css-adjustment create mode 100644 projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-backup-needs-attention-notice.tsx create mode 100644 projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-get-readable-failed-backup-reason.tsx create mode 100644 projects/packages/my-jetpack/_inc/utils/apply-timezone.ts create mode 100644 projects/packages/my-jetpack/changelog/add-backup-redbubble-needs-attention-notice diff --git a/projects/js-packages/components/changelog/notice-component-minor-css-adjustment b/projects/js-packages/components/changelog/notice-component-minor-css-adjustment new file mode 100644 index 0000000000000..a005ea7cb6a5b --- /dev/null +++ b/projects/js-packages/components/changelog/notice-component-minor-css-adjustment @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Notice component: Minor css adjustment. + + diff --git a/projects/js-packages/components/components/notice/style.module.scss b/projects/js-packages/components/components/notice/style.module.scss index f765d25ab4566..1bf9ef4a578d2 100644 --- a/projects/js-packages/components/components/notice/style.module.scss +++ b/projects/js-packages/components/components/notice/style.module.scss @@ -62,6 +62,12 @@ display: flex; align-items: center; margin-top: 20px; + row-gap: 20px; + flex-wrap: wrap; + + @media screen and ( max-width: 600px ) { + justify-content: center; + } a { &, &:hover, &:active, &:focus { diff --git a/projects/packages/my-jetpack/_inc/components/info-tooltip/index.tsx b/projects/packages/my-jetpack/_inc/components/info-tooltip/index.tsx index be873a602847a..b4e6a62eae873 100644 --- a/projects/packages/my-jetpack/_inc/components/info-tooltip/index.tsx +++ b/projects/packages/my-jetpack/_inc/components/info-tooltip/index.tsx @@ -23,6 +23,7 @@ export const InfoTooltip: FC< Props > = ( { iconSize = 14, tracksEventName, tracksEventProps = {}, + ...rest } ) => { const { recordEvent } = useAnalytics(); const useTooltipRef = useRef< HTMLButtonElement >(); @@ -66,6 +67,7 @@ export const InfoTooltip: FC< Props > = ( { offset={ 10 } focusOnMount={ 'container' } onClose={ hideTooltip } + { ...rest } >
{ children }
diff --git a/projects/packages/my-jetpack/_inc/components/my-jetpack-screen/styles.module.scss b/projects/packages/my-jetpack/_inc/components/my-jetpack-screen/styles.module.scss index ff620626fc455..48fa729fd473c 100644 --- a/projects/packages/my-jetpack/_inc/components/my-jetpack-screen/styles.module.scss +++ b/projects/packages/my-jetpack/_inc/components/my-jetpack-screen/styles.module.scss @@ -29,6 +29,7 @@ font-size: 16px; font-weight: 600; padding: var(--spacing-base) calc(var(--spacing-base)* 3) !important; + white-space: nowrap; } // X close button diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/backup-card/index.jsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/backup-card/index.jsx index 0ab9107bf5649..03b669c4d4421 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/backup-card/index.jsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/backup-card/index.jsx @@ -17,8 +17,10 @@ import useProduct from '../../../data/products/use-product'; import useSimpleQuery from '../../../data/use-simple-query'; import { getMyJetpackWindowInitialState } from '../../../data/utils/get-my-jetpack-window-state'; import useAnalytics from '../../../hooks/use-analytics'; +import { useGetReadableFailedBackupReason } from '../../../hooks/use-notification-watcher/use-get-readable-failed-backup-reason'; import numberFormat from '../../../utils/format-number'; import ProductCard from '../../connected-product-card'; +import { InfoTooltip } from '../../info-tooltip'; import styles from './style.module.scss'; const productSlug = PRODUCT_SLUGS.BACKUP; @@ -128,12 +130,58 @@ const getTimeSinceLastRenewableEvent = lastRewindableEventTime => { const BackupCard = props => { const { detail } = useProduct( productSlug ); const { status } = detail; + const { backup_failure: backupFailure } = + getMyJetpackWindowInitialState( 'redBubbleAlerts' ) || {}; + const { status: lastBackupStatus } = backupFailure || {}; const hasBackups = status === PRODUCT_STATUSES.ACTIVE || status === PRODUCT_STATUSES.CAN_UPGRADE; + const noDescription = () => null; - return hasBackups ? ( - - ) : ( - + const { title: errorTitle, text: errorDescription } = useGetReadableFailedBackupReason() || {}; + + if ( hasBackups ) { + return ; + } + + return ( + // eslint-disable-next-line react/jsx-no-bind + + { status === PRODUCT_STATUSES.NEEDS_ATTENTION && backupFailure && ( +
+
+ +
+
+ + { __( 'The last backup attempt failed.', 'jetpack-my-jetpack' ) } + + <> +

{ errorTitle }

+

{ errorDescription }

+

+ { __( + 'Check out our troubleshooting guide or contact your hosting provider to resolve the issue.', + 'jetpack-my-jetpack' + ) } +

+ +
+
+ + { __( 'Check out our troubleshooting guide.', 'jetpack-my-jetpack' ) } + +
+
+ ) } +
); }; diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/backup-card/style.module.scss b/projects/packages/my-jetpack/_inc/components/product-cards-section/backup-card/style.module.scss index 42cf0f6c65494..1f3dbd21d24d8 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/backup-card/style.module.scss +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/backup-card/style.module.scss @@ -56,3 +56,45 @@ .time { color: var( --jp-gray-40 ); } + +.backupWarning { + display: flex; + align-items: center; + gap: 0.25rem; + font-weight: 500; + + svg.iconError { + fill: var( --jp-red-60 ); + } +} + +.backupErrorContainer { + display: flex; + align-items: flex-start; + gap: 0.25rem; + font-size: var(--font-body-extra-small); + line-height: var(--font-title-small); + color: var(--jp-gray-50); + + .iconContainer { + padding-top: calc( var( --jp-underline-thickness) * 2 ); + > svg { + fill: var( --jp-red ); + } + } + + .errorDescription { + font-size: var( --font-label ); + font-weight: 400; + line-height: 20px; + } +} + +:global(.value-section__heading) { + display: flex; + align-items: center; + text-wrap: nowrap; + color: var(--jp-gray-100); + font-weight: 500; +} + diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/style.scss b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/style.scss index 77bb791e18b45..5592085c3a657 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/style.scss +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/style.scss @@ -96,12 +96,3 @@ color: var(--jp-black); } } - -.info-tooltip { - &__content { - width: calc(100vw - 64px); - max-width: 340px; - min-height: 150px; - padding: 24px; - } -} \ No newline at end of file diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/style.module.scss b/projects/packages/my-jetpack/_inc/components/product-cards-section/style.module.scss index 7be0d183cbe66..c471b95443613 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/style.module.scss +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/style.module.scss @@ -66,6 +66,18 @@ line-height: var(--font-title-small); } +:global(.info-tooltip__content) { + width: calc(100vw - 64px); + max-width: 340px; + min-height: 150px; + padding: 24px; +} + +:global(.components-popover.is-expanded .info-tooltip__content) { + width: auto; + max-width: unset; +} + // Since we're adding so much info into the My Jetpack product cards, here we'll increase // the min-width of the cards to 300px, and otherwise wrap around. @media screen and (min-width: 599px) and (max-width: 1290px) { diff --git a/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/index.ts b/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/index.ts index d5055aefdf56b..0efbfe8f3da00 100644 --- a/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/index.ts +++ b/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/index.ts @@ -1,4 +1,5 @@ import { getMyJetpackWindowInitialState } from '../../data/utils/get-my-jetpack-window-state'; +import useBackupNeedsAttentionNotice from './use-backup-needs-attention-notice'; import useBadInstallNotice from './use-bad-install-notice'; import useConnectionErrorsNotice from './use-connection-errors-notice'; import useDeprecateFeatureNotice from './use-deprecate-feature-notice'; @@ -8,11 +9,12 @@ import useSiteConnectionNotice from './use-site-connection-notice'; const useNotificationWatcher = () => { const { redBubbleAlerts } = getMyJetpackWindowInitialState(); - useBadInstallNotice( redBubbleAlerts ); - useSiteConnectionNotice( redBubbleAlerts ); - useConnectionErrorsNotice(); - useDeprecateFeatureNotice( redBubbleAlerts ); useExpiringPlansNotice( redBubbleAlerts ); + useBackupNeedsAttentionNotice( redBubbleAlerts ); + useDeprecateFeatureNotice( redBubbleAlerts ); + useConnectionErrorsNotice(); + useSiteConnectionNotice( redBubbleAlerts ); + useBadInstallNotice( redBubbleAlerts ); }; export default useNotificationWatcher; diff --git a/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-backup-needs-attention-notice.tsx b/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-backup-needs-attention-notice.tsx new file mode 100644 index 0000000000000..0940ea6ff9438 --- /dev/null +++ b/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-backup-needs-attention-notice.tsx @@ -0,0 +1,115 @@ +import { Col, getRedirectUrl, Text } from '@automattic/jetpack-components'; +import { getSettings as getDateSettings, dateI18n } from '@wordpress/date'; +import { __, sprintf } from '@wordpress/i18n'; +import { useContext, useEffect, useCallback } from 'react'; +import { NOTICE_PRIORITY_HIGH } from '../../context/constants'; +import { NoticeContext } from '../../context/notices/noticeContext'; +import { applyTimezone } from '../../utils/apply-timezone'; +import preventWidows from '../../utils/prevent-widows'; +import useAnalytics from '../use-analytics'; +import { useGetReadableFailedBackupReason } from './use-get-readable-failed-backup-reason'; +import type { NoticeOptions } from '../../context/notices/types'; + +type RedBubbleAlerts = Window[ 'myJetpackInitialState' ][ 'redBubbleAlerts' ]; + +const useBackupNeedsAttentionNotice = ( redBubbleAlerts: RedBubbleAlerts ) => { + const { recordEvent } = useAnalytics(); + const { setNotice } = useContext( NoticeContext ); + + const { status, last_updated: lastUpdated } = redBubbleAlerts?.backup_failure || {}; + const { text: errorDescription } = useGetReadableFailedBackupReason() || {}; + + const { + timezone: { offset }, + } = getDateSettings() || { offset: '0' }; + // Using dateI18n() to apply internationalization and formatting. + const backupStatusLastUpdatedDate = dateI18n( + 'F jS, Y g:ia', + applyTimezone( lastUpdated, parseInt( offset ) ) + ); + + const troubleshootBackupsUrl = getRedirectUrl( 'jetpack-support-troubleshooting-backup' ); + const contactSupportUrl = getRedirectUrl( 'jetpack-support' ); + + const noticeTitle = __( 'Oops! We couldn’t back up your site', 'jetpack-my-jetpack' ); + + const onPrimaryCtaClick = useCallback( () => { + window.open( troubleshootBackupsUrl ); + recordEvent( 'jetpack_my_jetpack_backup_needs_attention_notice_primary_cta_click', { + backup_status: status, + } ); + }, [ recordEvent, status, troubleshootBackupsUrl ] ); + + const onSecondaryCtaClick = useCallback( () => { + window.open( contactSupportUrl ); + recordEvent( 'jetpack_my_jetpack_backup_needs_attention_notice_secondary_cta_click', { + backup_status: status, + } ); + }, [ recordEvent, status, contactSupportUrl ] ); + + useEffect( () => { + if ( ! redBubbleAlerts?.backup_failure ) { + return; + } + + const noticeMessage = ( + + + { preventWidows( + sprintf( + // Translators: %1$s is the date the last backup took place, i.e.- "Dec 7, 2024" + __( 'The last backup attempted on %1$s was unsuccessful.', 'jetpack-my-jetpack' ), + backupStatusLastUpdatedDate + ) + ) } + + { errorDescription && ( + { preventWidows( errorDescription as string ) } + ) } + + { preventWidows( + __( + 'Check out our troubleshooting guide or contact your hosting provider to resolve the issue.', + 'jetpack-my-jetpack' + ) + ) } + + + ); + + const noticeOptions: NoticeOptions = { + id: 'backup-needs-attention-notice', + level: 'error', + actions: [ + { + label: __( 'Read troubleshooting guide', 'jetpack-my-jetpack' ), + onClick: onPrimaryCtaClick, + noDefaultClasses: true, + }, + { + label: __( 'Contact support', 'jetpack-my-jetpack' ), + onClick: onSecondaryCtaClick, + isExternalLink: true, + }, + ], + priority: NOTICE_PRIORITY_HIGH, + }; + + setNotice( { + title: noticeTitle, + message: noticeMessage, + options: noticeOptions, + } ); + }, [ + redBubbleAlerts, + setNotice, + recordEvent, + onPrimaryCtaClick, + onSecondaryCtaClick, + noticeTitle, + backupStatusLastUpdatedDate, + errorDescription, + ] ); +}; + +export default useBackupNeedsAttentionNotice; diff --git a/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-get-readable-failed-backup-reason.tsx b/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-get-readable-failed-backup-reason.tsx new file mode 100644 index 0000000000000..23e1f2fb7b51f --- /dev/null +++ b/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-get-readable-failed-backup-reason.tsx @@ -0,0 +1,173 @@ +import { __, sprintf } from '@wordpress/i18n'; +import { useMemo, type ReactElement } from 'react'; +import { getMyJetpackWindowInitialState } from '../../data/utils/get-my-jetpack-window-state'; + +export type ReasonContent = { + title?: ReactElement | string; + text?: ReactElement | string; +}; + +/** + * Gets the translated human readable descriptions of Backup failure codes. + * + * @return {ReasonContent} An object containing each tooltip's title and text content. + */ +export function useGetReadableFailedBackupReason(): ReasonContent { + const { backup_failure: backupFailure } = + getMyJetpackWindowInitialState( 'redBubbleAlerts' ) || {}; + const { status } = backupFailure || {}; + + const reasonContent = useMemo( () => { + switch ( status ) { + // Rewind errors: + case 'missing_plan': + return { + title: __( 'Missing Backup plan', 'jetpack-my-jetpack' ), + text: __( + 'The site does not appear to have a valid Backup plan. Please purchase a Backup plan in order activate the features of Jetpack Backup.', + 'jetpack-my-jetpack' + ), + }; + case 'no_connected_jetpack': + return { + title: __( 'Not connected', 'jetpack-my-jetpack' ), + text: __( + 'The site doesn’t appear to be connected. Backup requires an active Jetpack connection in order to function properly.', + 'jetpack-my-jetpack' + ), + }; + case 'no_connected_jetpack_with_credentials': + return { + title: __( 'Not connected', 'jetpack-my-jetpack' ), + text: __( + 'The site doesn’t appear to be connected. Backup requires an active Jetpack connection in order to function properly, although successful Backups may still appear in the logs.', + 'jetpack-my-jetpack' + ), + }; + case 'vp_active_on_site': + return { + title: __( 'Backup plugin conflict', 'jetpack-my-jetpack' ), + text: __( + 'We’ve detected VaultPress is currently active on the site. VaultPress and Jetpack Backup cannot run simultaneously. In order to activate Jetpack Backup, you will first need to deactivate VaultPress.', + 'jetpack-my-jetpack' + ), + }; + case 'vp_can_transfer': + return { + title: __( 'Transfer VaultPress', 'jetpack-my-jetpack' ), + text: __( + 'We’ve detected VaultPress is currently active on the site, and we can automatically transfer it over to Jetpack Backup (Rewind), but you will need to trigger the transfer manually.', + 'jetpack-my-jetpack' + ), + }; + case 'host_not_supported': + return { + title: __( 'Host not supported', 'jetpack-my-jetpack' ), + text: __( + 'Backup doesn’t currently support the host that the site is hosted on.', + 'jetpack-my-jetpack' + ), + }; + case 'multisite_not_supported': + return { + title: __( 'Multi-site not supported', 'jetpack-my-jetpack' ), + text: __( + 'Backup can’t be activated on multi-site installations, neither the network site or its sub-sites.', + 'jetpack-my-jetpack' + ), + }; + case 'no_site_found': + return { + title: __( 'No site record', 'jetpack-my-jetpack' ), + text: __( + 'The VaultPress API could not recognize the site ID associated with your site, or multiple site ID’s are associated with the same site domain.', + 'jetpack-my-jetpack' + ), + }; + // Last backup errors: + case 'no-credentials': // This error is depreciated, now that all backups work via http only. + return { + title: __( 'No credentials found', 'jetpack-my-jetpack' ), + text: __( + 'No remote server credentials were found. Please add your website’s server credentials in Backup settings so Backup can fully function properly.', + 'jetpack-my-jetpack' + ), + }; + case 'no-credentials-atomic': // This error is depreciated, and an extreeeeemely rare edge case scenario. + return { + title: __( 'No credentials (Atomic)', 'jetpack-my-jetpack' ), + text: __( + 'There appears to be some issue with the Atomic API or a networking type issue. Please try again shortly to see if the issue has resolved.', + 'jetpack-my-jetpack' + ), + }; + case 'credential-error': + return { + title: __( 'Backup error', 'jetpack-my-jetpack' ), + text: __( + 'Although the site appears to be up and accessible, and remote server credentials are set, Backup still encountered an error during the last backup attempt.', + 'jetpack-my-jetpack' + ), + }; + case 'http-only-error': + return { + title: __( 'Backup error', 'jetpack-my-jetpack' ), + text: __( + 'Although the site appears to be up and accessible, Backup still encountered an error during the last backup attempt.', + 'jetpack-my-jetpack' + ), + }; + case 'not-accessible': + return { + title: __( 'Site unavailable', 'jetpack-my-jetpack' ), + text: __( + 'Backup was unable to access your site during the last backup attempt. This could be due to networking issues, a block from your host, or other server issues.', + 'jetpack-my-jetpack' + ), + }; + case 'Kill switch active': + case 'backup-deactivated': + return { + title: __( 'Backup is deactivated', 'jetpack-my-jetpack' ), + text: __( + 'It appear Backup has been manually deactivated, either by a site attribute or a general kill switch.', + 'jetpack-my-jetpack' + ), + }; + case 'error': + return { + title: __( 'Backup system error', 'jetpack-my-jetpack' ), + text: __( + 'Backup has encountered a general system error and was unable to complete the last backup attempt.', + 'jetpack-my-jetpack' + ), + }; + default: { + if ( ! status ) { + return { + title: null, + text: null, + }; + } + const statusTitleSplit = status.split( /[_\s-]/ ); + statusTitleSplit[ 0 ] = + statusTitleSplit[ 0 ].charAt( 0 ).toUpperCase() + statusTitleSplit[ 0 ].slice( 1 ); + const statusTitle = statusTitleSplit.join( ' ' ); + return { + title: sprintf( + // translators: %s is the error code coming from the server (formatted, i.e.- first word capitalized, hypen's removed, etc. ) i.e.- 'Invalid credentials', 'File not found', etc. + __( '%s error', 'jetpack-my-jetpack' ), + statusTitle + ), + text: sprintf( + // translators: %s is the error code coming from the server. i.e.- 'invalid-credentials', 'file-not-found', etc. + __( 'Error code: %s', 'jetpack-my-jetpack' ), + status + ), + }; + } + } + }, [ status ] ); + + return reasonContent; +} diff --git a/projects/packages/my-jetpack/_inc/utils/apply-timezone.ts b/projects/packages/my-jetpack/_inc/utils/apply-timezone.ts new file mode 100644 index 0000000000000..4a3afe7f4d698 --- /dev/null +++ b/projects/packages/my-jetpack/_inc/utils/apply-timezone.ts @@ -0,0 +1,15 @@ +import { getDate } from '@wordpress/date'; + +/** + * Applies local timezone to date (via timezone offset) + * @param {string} date - a date string with format like: 2024-12-08T14:41:45.170+00:00 + * @param {number} offset - the timezone offset in hours, like: -3 + * + * @return {Date} a JavaScript Date object + */ +export function applyTimezone( date: string, offset: number ): Date { + const dateObject = getDate( date ); + dateObject.setHours( dateObject.getHours() + offset ); + + return dateObject; +} diff --git a/projects/packages/my-jetpack/changelog/add-backup-redbubble-needs-attention-notice b/projects/packages/my-jetpack/changelog/add-backup-redbubble-needs-attention-notice new file mode 100644 index 0000000000000..80a1e18215b3f --- /dev/null +++ b/projects/packages/my-jetpack/changelog/add-backup-redbubble-needs-attention-notice @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +My Jetpack: Add red bubble and notice/banner when Backup has 'needs-attention' status. diff --git a/projects/packages/my-jetpack/global.d.ts b/projects/packages/my-jetpack/global.d.ts index d5c120766c91d..5ae0a3a54fcf6 100644 --- a/projects/packages/my-jetpack/global.d.ts +++ b/projects/packages/my-jetpack/global.d.ts @@ -22,7 +22,10 @@ type ProductStatus = | 'needs_activation' | 'needs_first_site_connection' | 'user_connection_error' - | 'can_upgrade'; + | 'can_upgrade' + | 'needs_attention' + | 'expired' + | 'expiring'; type JetpackModule = | 'anti-spam' @@ -69,6 +72,29 @@ type ScanItem = { version: string; }; +type RewindStatus = + | 'missing_plan' + | 'no_connected_jetpack' + | 'no_connected_jetpack_with_credentials' + | 'vp_active_on_site' + | 'vp_can_transfer' + | 'host_not_supported' + | 'multisite_not_supported' + | 'no_site_found'; + +type BackupStatus = + | 'started' + | 'finished' + | 'no-credentials' + | 'backups-deactivated' + | 'no-credentials-atomic' + | 'credential-error' + | 'http-only-error' + | 'not-accessible' + | 'backup-deactivated' + | 'Kill switch active' + | 'error' + | 'error-will-retry'; interface Window { myJetpackInitialState?: { siteSuffix: string; @@ -361,6 +387,11 @@ interface Window { plugin: string; }; }; + backup_failure?: { + source: 'rewind' | 'last_backup'; + status: RewindStatus | BackupStatus; + last_updated: string; + }; [ key: `${ string }--plan_expired` ]: { product_slug: string; product_name?: string; diff --git a/projects/packages/my-jetpack/src/class-initializer.php b/projects/packages/my-jetpack/src/class-initializer.php index 3d8bba78d485a..9ccb21cadaa6d 100644 --- a/projects/packages/my-jetpack/src/class-initializer.php +++ b/projects/packages/my-jetpack/src/class-initializer.php @@ -929,6 +929,7 @@ public static function add_red_bubble_alerts( array $red_bubble_slugs ) { } else { return array_merge( self::alert_if_missing_connection( $red_bubble_slugs ), + self::alert_if_last_backup_failed( $red_bubble_slugs ), self::alert_if_paid_plan_expiring( $red_bubble_slugs ) ); } @@ -983,14 +984,22 @@ public static function alert_if_paid_plan_expiring( array $red_bubble_slugs ) { if ( ! $connection->is_connected() ) { return $red_bubble_slugs; } - $product_classes = Products::get_products_classes(); + $product_classes = Products::get_products_classes(); + $not_shown_products = array( + 'scan', + 'extras', + 'ai', + 'newsletter', + 'site-accelerator', + 'related-posts', + ); $products_included_in_expiring_plan = array(); foreach ( $product_classes as $key => $product ) { // Skip these- we don't show them in My Jetpack. // ('ai' is a duplicate class of 'jetpack-ai', and therefore not needed). // See `get_product_classes() in projects/packages/my-jetpack/src/class-products.php for more info. - if ( 'scan' === $key || 'extras' === $key || 'ai' === $key ) { + if ( in_array( $key, $not_shown_products, true ) ) { continue; } @@ -1028,4 +1037,24 @@ public static function alert_if_paid_plan_expiring( array $red_bubble_slugs ) { return $red_bubble_slugs; } + + /** + * Add an alert slug if Backups are failing or having an issue. + * + * @param array $red_bubble_slugs - slugs that describe the reasons the red bubble is showing. + * @return array + */ + public static function alert_if_last_backup_failed( array $red_bubble_slugs ) { + // Make sure we're dealing with the backup product only + if ( ! Products\Backup::has_paid_plan_for_product() ) { + return $red_bubble_slugs; + } + + $backup_failed_status = Products\Backup::does_module_need_attention(); + if ( $backup_failed_status ) { + $red_bubble_slugs['backup_failure'] = $backup_failed_status; + } + + return $red_bubble_slugs; + } } diff --git a/projects/packages/my-jetpack/src/class-products.php b/projects/packages/my-jetpack/src/class-products.php index 33e894329a507..b6a96e4304ce1 100644 --- a/projects/packages/my-jetpack/src/class-products.php +++ b/projects/packages/my-jetpack/src/class-products.php @@ -67,6 +67,7 @@ class Products { self::STATUS_USER_CONNECTION_ERROR, self::STATUS_PLUGIN_ABSENT_WITH_PLAN, self::STATUS_NEEDS_PLAN, + self::STATUS_NEEDS_ATTENTION, ); /** diff --git a/projects/packages/my-jetpack/src/products/class-backup.php b/projects/packages/my-jetpack/src/products/class-backup.php index bee360c09ee2a..829044b11d5ec 100644 --- a/projects/packages/my-jetpack/src/products/class-backup.php +++ b/projects/packages/my-jetpack/src/products/class-backup.php @@ -247,19 +247,10 @@ public static function does_module_need_attention() { // First check the status of Rewind for failure. $rewind_state = self::get_state_from_wpcom(); if ( ! is_wp_error( $rewind_state ) ) { - $backup_failure_reasons = array( - 'unknown_error', - 'no_site_found', - 'missing_plan', - 'no_connected_jetpack', - 'no_connected_jetpack_with_credentials', - 'multisite_not_supported', - 'host_not_supported', - 'vp_active_on_site', - ); - if ( $rewind_state->state === 'unavailable' && ! empty( $rewind_state->reason ) && in_array( $rewind_state->reason, $backup_failure_reasons, true ) ) { + if ( $rewind_state->state !== 'active' && $rewind_state->state !== 'provisioning' && $rewind_state->state !== 'awaiting_credentials' ) { $backup_failed_status = array( - 'status' => $rewind_state->reason, + 'source' => 'rewind', + 'status' => isset( $rewind_state->reason ) && ! empty( $rewind_state->reason ) ? $rewind_state->reason : $rewind_state->state, 'last_updated' => $rewind_state->last_updated, ); } @@ -277,8 +268,9 @@ public static function does_module_need_attention() { } if ( $last_backup && isset( $last_backup->status ) ) { - if ( $last_backup->status === 'not-accessible' || $last_backup->status === 'error' || $last_backup->status === 'credential-error' ) { + if ( $last_backup->status !== 'started' && ! preg_match( '/-will-retry$/', $last_backup->status ) && $last_backup->status !== 'finished' ) { $backup_failed_status = array( + 'source' => 'last_backup', 'status' => $last_backup->status, 'last_updated' => $last_backup->last_updated, ); From 440a2342f7b360e071e50f643af02db1a3a59a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donncha=20=C3=93=20Caoimh?= <5656673+donnchawp@users.noreply.github.com> Date: Thu, 19 Dec 2024 08:19:54 +0000 Subject: [PATCH 38/63] Super Cache: fix how the plugin uses apache_request_headers (#39951) * Fix up apache_request_headers() * changelog * Fix static analysis issues * Fix the changelog message --------- Co-authored-by: Peter Petrov --- .../plugins/super-cache/.phan/baseline.php | 3 +- .../update-super-cache-get-apache-headers | 4 +++ .../plugins/super-cache/wp-cache-phase2.php | 28 ++++++++++--------- projects/plugins/super-cache/wp-cache.php | 6 ---- 4 files changed, 20 insertions(+), 21 deletions(-) create mode 100644 projects/plugins/super-cache/changelog/update-super-cache-get-apache-headers diff --git a/projects/plugins/super-cache/.phan/baseline.php b/projects/plugins/super-cache/.phan/baseline.php index bc33b588e8e44..6a814a0fb43f6 100644 --- a/projects/plugins/super-cache/.phan/baseline.php +++ b/projects/plugins/super-cache/.phan/baseline.php @@ -46,7 +46,6 @@ // PhanTypeMismatchReturn : 2 occurrences // PhanCommentParamWithoutRealParam : 1 occurrence // PhanPluginDuplicateIfCondition : 1 occurrence - // PhanRedefineFunctionInternal : 1 occurrence // PhanRedundantConditionInLoop : 1 occurrence // PhanTypeArraySuspicious : 1 occurrence // PhanTypeConversionFromArray : 1 occurrence @@ -80,7 +79,7 @@ 'tests/e2e/tools/mu-test-helpers.php' => ['PhanTypeMismatchArgument'], 'wp-cache-base.php' => ['PhanTypeMismatchArgumentNullableInternal'], 'wp-cache-phase1.php' => ['PhanRedundantConditionInGlobalScope', 'PhanTypeNonVarPassByRef'], - 'wp-cache-phase2.php' => ['PhanImpossibleCondition', 'PhanPluginDuplicateConditionalNullCoalescing', 'PhanPluginDuplicateIfCondition', 'PhanPluginRedundantAssignment', 'PhanPluginSimplifyExpressionBool', 'PhanPluginUnreachableCode', 'PhanPossiblyUndeclaredVariable', 'PhanRedefineFunctionInternal', 'PhanRedundantCondition', 'PhanSuspiciousValueComparison', 'PhanTypeArraySuspicious', 'PhanTypeArraySuspiciousNull', 'PhanTypeArraySuspiciousNullable', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentInternal', 'PhanTypeMismatchArgumentInternalProbablyReal', 'PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchArgumentNullableInternal', 'PhanTypeNonVarPassByRef', 'PhanTypePossiblyInvalidDimOffset', 'PhanTypeSuspiciousNonTraversableForeach', 'PhanTypeSuspiciousStringExpression', 'PhanUndeclaredConstant', 'PhanUndeclaredVariableDim'], + 'wp-cache-phase2.php' => ['PhanImpossibleCondition', 'PhanPluginDuplicateConditionalNullCoalescing', 'PhanPluginDuplicateIfCondition', 'PhanPluginRedundantAssignment', 'PhanPluginSimplifyExpressionBool', 'PhanPluginUnreachableCode', 'PhanPossiblyUndeclaredVariable', 'PhanRedundantCondition', 'PhanSuspiciousValueComparison', 'PhanTypeArraySuspicious', 'PhanTypeArraySuspiciousNull', 'PhanTypeArraySuspiciousNullable', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentInternal', 'PhanTypeMismatchArgumentInternalProbablyReal', 'PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchArgumentNullableInternal', 'PhanTypeNonVarPassByRef', 'PhanTypePossiblyInvalidDimOffset', 'PhanTypeSuspiciousNonTraversableForeach', 'PhanTypeSuspiciousStringExpression', 'PhanUndeclaredConstant', 'PhanUndeclaredVariableDim'], 'wp-cache.php' => ['PhanImpossibleCondition', 'PhanPluginDuplicateAdjacentStatement', 'PhanPluginDuplicateExpressionAssignmentOperation', 'PhanPluginNeverReturnFunction', 'PhanPluginSimplifyExpressionBool', 'PhanPossiblyUndeclaredVariable', 'PhanRedundantCondition', 'PhanRedundantConditionInLoop', 'PhanSuspiciousValueComparison', 'PhanTypeArraySuspiciousNullable', 'PhanTypeInvalidDimOffset', 'PhanTypeInvalidLeftOperandOfBitwiseOp', 'PhanTypeInvalidLeftOperandOfNumericOp', 'PhanTypeInvalidRightOperandOfAdd', 'PhanTypeInvalidRightOperandOfBitwiseOp', 'PhanTypeMismatchArgumentInternal', 'PhanTypeMismatchArgumentInternalProbablyReal', 'PhanTypeMismatchArgumentInternalReal', 'PhanTypeMismatchArgumentNullableInternal', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeNonVarPassByRef', 'PhanTypePossiblyInvalidDimOffset', 'PhanTypeSuspiciousNonTraversableForeach', 'PhanTypeSuspiciousStringExpression', 'PhanUndeclaredConstant', 'PhanUndeclaredFunction', 'PhanUndeclaredVariable', 'PhanUndeclaredVariableDim'], ], // 'directory_suppressions' => ['src/directory_name' => ['PhanIssueName1', 'PhanIssueName2']] can be manually added if needed. diff --git a/projects/plugins/super-cache/changelog/update-super-cache-get-apache-headers b/projects/plugins/super-cache/changelog/update-super-cache-get-apache-headers new file mode 100644 index 0000000000000..d43c4d6b80f59 --- /dev/null +++ b/projects/plugins/super-cache/changelog/update-super-cache-get-apache-headers @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Super Cache: Fixed the apache_request_headers fallback so it works when that command is disabled diff --git a/projects/plugins/super-cache/wp-cache-phase2.php b/projects/plugins/super-cache/wp-cache-phase2.php index 44287b4635f1e..adddbe6c0a96e 100644 --- a/projects/plugins/super-cache/wp-cache-phase2.php +++ b/projects/plugins/super-cache/wp-cache-phase2.php @@ -310,7 +310,7 @@ function wp_cache_serve_cache_file() { // don't try to match modified dates if using dynamic code. if ( $wp_cache_mfunc_enabled == 0 && $wp_supercache_304 ) { wp_cache_debug( 'wp_cache_serve_cache_file: checking age of cached vs served files.' ); - $headers = apache_request_headers(); + $headers = wpsc_apache_request_headers(); $remote_mod_time = isset( $headers['If-Modified-Since'] ) ? $headers['If-Modified-Since'] : null; if ( $remote_mod_time === null && isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { @@ -1751,7 +1751,7 @@ function wp_cache_user_agent_is_rejected() { return false; } - $headers = apache_request_headers(); + $headers = wpsc_apache_request_headers(); if ( empty( $headers['User-Agent'] ) ) { return false; } @@ -3582,23 +3582,25 @@ function wpsc_is_get_query() { return $is_get_query; } -if ( ! function_exists( 'apache_request_headers' ) ) { - /** - * A fallback for get request headers. - * Based on comments from http://php.net/manual/en/function.apache-request-headers.php - * - * @return array List of request headers - */ - function apache_request_headers() { - $headers = array(); +/** + * A fallback for get request headers. + * Based on comments from http://php.net/manual/en/function.apache-request-headers.php + * + * @return array List of request headers + */ +function wpsc_apache_request_headers() { + if ( ! function_exists( 'apache_request_headers' ) || ! is_callable( 'apache_request_headers' ) ) { + $headers = array(); foreach ( array_keys( $_SERVER ) as $skey ) { if ( str_starts_with( $skey, 'HTTP_' ) ) { $header = implode( '-', array_map( 'ucfirst', array_slice( explode( '_', strtolower( $skey ) ), 1 ) ) ); $headers[ $header ] = $_SERVER[ $skey ]; } } - - return $headers; + } else { + $headers = apache_request_headers(); } + + return $headers; } diff --git a/projects/plugins/super-cache/wp-cache.php b/projects/plugins/super-cache/wp-cache.php index 97347ef8bd543..5aa1c630b8af9 100644 --- a/projects/plugins/super-cache/wp-cache.php +++ b/projects/plugins/super-cache/wp-cache.php @@ -1676,8 +1676,6 @@ function wp_cache_sanitize_value($text, & $array) { function wp_cache_update_rejected_ua() { global $cache_rejected_user_agent, $wp_cache_config_file, $valid_nonce; - if ( !function_exists( 'apache_request_headers' ) ) return; - if ( isset( $_POST[ 'wp_rejected_user_agent' ] ) && $valid_nonce ) { $_POST[ 'wp_rejected_user_agent' ] = str_replace( ' ', '___', $_POST[ 'wp_rejected_user_agent' ] ); $text = str_replace( '___', ' ', wp_cache_sanitize_value( $_POST[ 'wp_rejected_user_agent' ], $cache_rejected_user_agent ) ); @@ -1692,10 +1690,6 @@ function wp_cache_update_rejected_ua() { function wpsc_edit_rejected_ua() { global $cache_rejected_user_agent; - if ( ! function_exists( 'apache_request_headers' ) ) { - return; - } - $admin_url = admin_url( 'options-general.php?page=wpsupercache' ); wp_cache_update_rejected_ua(); wpsc_render_partial( From 129f0de5ccecd14a1e53b7180bf16ed019207dba Mon Sep 17 00:00:00 2001 From: Richard Ortiz Date: Thu, 19 Dec 2024 10:10:56 +0100 Subject: [PATCH 39/63] Duplicated views: add untangled comments to include list (#40649) * Added the comments page to the list of untangled pages under the experiment * changelog * Added notice to the comments section * We now use the clean title * Improved image * Edited and improved image --- .../changelog/add-untangled-comments-experiment | 4 ++++ .../screens/edit-comments-php.webp | Bin 0 -> 64250 bytes .../wpcom-admin-interface.php | 7 +++++-- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/add-untangled-comments-experiment create mode 100644 projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/screens/edit-comments-php.webp diff --git a/projects/packages/jetpack-mu-wpcom/changelog/add-untangled-comments-experiment b/projects/packages/jetpack-mu-wpcom/changelog/add-untangled-comments-experiment new file mode 100644 index 0000000000000..ac7a04cbe6b38 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/add-untangled-comments-experiment @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Added the comments page to the list of untangled pages under the experiment diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/screens/edit-comments-php.webp b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/screens/edit-comments-php.webp new file mode 100644 index 0000000000000000000000000000000000000000..e552a79e58657b638802281af1686b29b1857485 GIT binary patch literal 64250 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 ccic?wF9nZv#Uz?<^oJCu`8mh8 plugins_url( 'screens/' . sanitize_title( $current_screen ) . '.webp', __FILE__ ), - 'title' => $title, + 'title' => $clean_title, 'screen' => $current_screen, 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'dismissNonce' => wp_create_nonce( 'wpcom_dismiss_removed_calypso_screen_notice' ), From 354fbd46b5f4dbc5e87e3e24a2df4a7dadbe500e Mon Sep 17 00:00:00 2001 From: Bogdan Ungureanu Date: Thu, 19 Dec 2024 15:20:03 +0200 Subject: [PATCH 40/63] Promote with Blaze: Purge cache on Atomic sites when site visibility changes (#40650) * Promote with Blaze: Purge cache on Atomic sites when site visibility changes * Linting * Linting... * Enable the quicklink from wpcomsh * Fix linting * Add guard * Remove changelog for Jetpack --- .../changelog/fix-atomic-promote-with-blaze | 4 ++ .../plugins/wpcomsh/feature-plugins/blaze.php | 54 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 projects/plugins/wpcomsh/changelog/fix-atomic-promote-with-blaze diff --git a/projects/plugins/wpcomsh/changelog/fix-atomic-promote-with-blaze b/projects/plugins/wpcomsh/changelog/fix-atomic-promote-with-blaze new file mode 100644 index 0000000000000..8841c588231a2 --- /dev/null +++ b/projects/plugins/wpcomsh/changelog/fix-atomic-promote-with-blaze @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Purge the cache when the site visibility changes on Atomic sites diff --git a/projects/plugins/wpcomsh/feature-plugins/blaze.php b/projects/plugins/wpcomsh/feature-plugins/blaze.php index 1c269f7e9fe43..a680f185664bb 100644 --- a/projects/plugins/wpcomsh/feature-plugins/blaze.php +++ b/projects/plugins/wpcomsh/feature-plugins/blaze.php @@ -6,6 +6,8 @@ * @package wpcomsh */ +use Automattic\Jetpack\Connection\Manager as Jetpack_Connection; + /** * Activate the Blaze module * If you use a version of Jetpack that supports it, @@ -61,5 +63,57 @@ function wpcomsh_activate_blaze_module_on_launching( $old_value, $new_value ) { if ( $blog_public === 1 ) { wpcomsh_activate_blaze_module(); } + + return $new_value; } add_filter( 'update_option_blog_public', 'wpcomsh_activate_blaze_module_on_launching', 10, 2 ); + +/** + * Delete the transient for the given site id. + * + * @return void + */ +function wpcomsh_blaze_purge_transient_cache() { + $site_id = Jetpack_Connection::get_site_id(); + + if ( is_wp_error( $site_id ) ) { + return; + } + + $transient = 'jetpack_blaze_site_supports_blaze_' . $site_id; + delete_transient( $transient ); +} + +/** + * Delete the caching transient when coming soon is changed. + */ +add_action( + 'pre_update_option_wpcom_public_coming_soon', + function ( $option ) { + wpcomsh_blaze_purge_transient_cache(); + return $option; + } +); + +/** + * Delete the caching transient when the blog visibility option changes. + */ +add_action( + 'pre_update_option_blog_public', + function ( $option ) { + wpcomsh_blaze_purge_transient_cache(); + return $option; + } +); + +/** + * On Atomic sites the Promote with Blaze option is enabled. + * + * @phan-suppress PhanUndeclaredFunctionInCallable -- jetpack_blaze_post_row_actions_disable is part of jetpack. + */ +add_action( + 'jetpack_modules_loaded', + function () { + remove_filter( 'jetpack_blaze_post_row_actions_enable', 'jetpack_blaze_post_row_actions_disable' ); + } +); From ff26ecb6c6ac8876b9562205034b437a9e8efe87 Mon Sep 17 00:00:00 2001 From: Bogdan Ungureanu Date: Thu, 19 Dec 2024 15:25:53 +0200 Subject: [PATCH 41/63] RDV: Remove the wpcom_admin_interface hook for the admin_menu hook (#40669) * RDV: Remove the wpcom_admin_interface hook for the admin_menu hook * Remove the alias functions with calls directly to get_option --- .../changelog/fix-wp-admin-rdv | 4 ++++ .../wpcom-admin-bar/wpcom-admin-bar.php | 2 +- .../wpcom-admin-interface.php | 15 +++++++++++++++ .../wpcom-admin-menu/wpcom-admin-menu.php | 19 +++---------------- .../wpcom-sidebar-notice.php | 2 +- .../features/wpcom-themes/wpcom-themes.php | 2 +- .../masterbar/changelog/fix-wp-admin-rdv | 4 ++++ .../src/admin-menu/class-admin-menu.php | 2 +- .../admin-menu/class-atomic-admin-menu.php | 12 ++++++------ .../src/admin-menu/class-base-admin-menu.php | 15 --------------- 10 files changed, 36 insertions(+), 41 deletions(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/fix-wp-admin-rdv create mode 100644 projects/packages/masterbar/changelog/fix-wp-admin-rdv diff --git a/projects/packages/jetpack-mu-wpcom/changelog/fix-wp-admin-rdv b/projects/packages/jetpack-mu-wpcom/changelog/fix-wp-admin-rdv new file mode 100644 index 0000000000000..cc90987517731 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/fix-wp-admin-rdv @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Exclude the wpcom_admin_interface from the admin_menu action diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-bar/wpcom-admin-bar.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-bar/wpcom-admin-bar.php index a4e006a51ce38..575a7e3218f49 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-bar/wpcom-admin-bar.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-bar/wpcom-admin-bar.php @@ -263,7 +263,7 @@ function wpcom_replace_edit_profile_menu_to_me( $wp_admin_bar ) { * @return string Name of the admin bar class. */ function wpcom_custom_wpcom_admin_bar_class( $wp_admin_bar_class ) { - if ( ! wpcom_is_using_default_admin_menu() ) { + if ( get_option( 'wpcom_admin_interface' ) === 'wp-admin' ) { return $wp_admin_bar_class; } diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php index e6a7caecc799e..91e2394b86a7b 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-interface/wpcom-admin-interface.php @@ -194,6 +194,21 @@ function wpcom_admin_get_user_option_jetpack( $value ) { add_filter( 'get_user_option_jetpack_admin_menu_preferred_views', 'wpcom_admin_get_user_option_jetpack' ); add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option', 10 ); +add_action( + 'admin_menu', + function () { + remove_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' ); + }, + PHP_INT_MIN +); + +add_action( + 'admin_menu', + function () { + add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option', 10 ); + }, + PHP_INT_MAX +); /** * Hides the "View" switcher on WP Admin screens enforced by the "Remove duplicate views" experiment. */ diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-menu/wpcom-admin-menu.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-menu/wpcom-admin-menu.php index aafe2e0edb12c..a44371e89ec14 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-menu/wpcom-admin-menu.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-admin-menu/wpcom-admin-menu.php @@ -33,24 +33,11 @@ function current_user_has_wpcom_account() { return $has_account; } -/** - * Check if the user has the default (Calypso) Admin menu. - * - * @return bool - */ -function wpcom_is_using_default_admin_menu() { - remove_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' ); - $option = get_option( 'wpcom_admin_interface' ) !== 'wp-admin'; - add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option', 10 ); - - return $option; -} - /** * Adds a Hosting menu. */ function wpcom_add_hosting_menu() { - if ( wpcom_is_using_default_admin_menu() ) { + if ( get_option( 'wpcom_admin_interface' ) !== 'wp-admin' ) { return; } @@ -162,7 +149,7 @@ function wpcom_add_hosting_menu() { function wpcom_add_jetpack_submenu() { $is_simple_site = defined( 'IS_WPCOM' ) && IS_WPCOM; $is_atomic_site = ! $is_simple_site; - $uses_wp_admin_interface = ! wpcom_is_using_default_admin_menu(); + $uses_wp_admin_interface = get_option( 'wpcom_admin_interface' ) === 'wp-admin'; if ( ! $uses_wp_admin_interface ) { return; @@ -392,7 +379,7 @@ function wpcom_add_plugins_menu() { global $menu; $is_simple_site = defined( 'IS_WPCOM' ) && IS_WPCOM; $is_atomic_site = ! $is_simple_site; - $uses_wp_admin_interface = ! wpcom_is_using_default_admin_menu(); + $uses_wp_admin_interface = get_option( 'wpcom_admin_interface' ) === 'wp-admin'; if ( $is_simple_site ) { $has_plugins_menu = false; diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-sidebar-notice/wpcom-sidebar-notice.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-sidebar-notice/wpcom-sidebar-notice.php index d8ce8b474ddd2..90b1dd2898ff6 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-sidebar-notice/wpcom-sidebar-notice.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-sidebar-notice/wpcom-sidebar-notice.php @@ -10,7 +10,7 @@ use Automattic\Jetpack\Connection\Manager as Connection_Manager; use Automattic\Jetpack\Jetpack_Mu_Wpcom; -if ( wpcom_is_using_default_admin_menu() ) { +if ( get_option( 'wpcom_admin_interface' ) !== 'wp-admin' ) { return; } diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-themes/wpcom-themes.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-themes/wpcom-themes.php index f5932690ed893..6d6ede007345a 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-themes/wpcom-themes.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-themes/wpcom-themes.php @@ -56,7 +56,7 @@ function wpcom_themes_show_banner() { * Registers an "Appearance > Theme Showcase" menu. */ function wpcom_themes_add_theme_showcase_menu() { - if ( wpcom_is_using_default_admin_menu() ) { + if ( get_option( 'wpcom_admin_interface' ) !== 'wp-admin' ) { return; } diff --git a/projects/packages/masterbar/changelog/fix-wp-admin-rdv b/projects/packages/masterbar/changelog/fix-wp-admin-rdv new file mode 100644 index 0000000000000..cc90987517731 --- /dev/null +++ b/projects/packages/masterbar/changelog/fix-wp-admin-rdv @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Exclude the wpcom_admin_interface from the admin_menu action diff --git a/projects/packages/masterbar/src/admin-menu/class-admin-menu.php b/projects/packages/masterbar/src/admin-menu/class-admin-menu.php index eb0c7f3cdd7eb..3b0085f29391d 100644 --- a/projects/packages/masterbar/src/admin-menu/class-admin-menu.php +++ b/projects/packages/masterbar/src/admin-menu/class-admin-menu.php @@ -356,7 +356,7 @@ public function add_tools_menu() { // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- Core should ideally document null for no-callback arg. https://core.trac.wordpress.org/ticket/52539. add_submenu_page( 'tools.php', esc_attr__( 'Marketing', 'jetpack-masterbar' ), __( 'Marketing', 'jetpack-masterbar' ), 'publish_posts', 'https://wordpress.com/marketing/tools/' . $this->domain, null, 0 ); - if ( $this->is_using_default_admin_menu() ) { + if ( ! $this->use_wp_admin_interface() ) { // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- Core should ideally document null for no-callback arg. https://core.trac.wordpress.org/ticket/52539. add_submenu_page( 'tools.php', esc_attr__( 'Monetize', 'jetpack-masterbar' ), __( 'Monetize', 'jetpack-masterbar' ), 'manage_options', 'https://wordpress.com/earn/' . $this->domain, null, 1 ); } diff --git a/projects/packages/masterbar/src/admin-menu/class-atomic-admin-menu.php b/projects/packages/masterbar/src/admin-menu/class-atomic-admin-menu.php index c58244fd06a2c..017891701b8c5 100644 --- a/projects/packages/masterbar/src/admin-menu/class-atomic-admin-menu.php +++ b/projects/packages/masterbar/src/admin-menu/class-atomic-admin-menu.php @@ -47,7 +47,7 @@ function () { ); // Add notices to the settings pages when there is a Calypso page available. - if ( get_option( 'wpcom_admin_interface' ) === 'wp-admin' ) { + if ( $this->use_wp_admin_interface() ) { add_action( 'current_screen', array( $this, 'add_settings_page_notice' ) ); } } @@ -79,7 +79,7 @@ public function reregister_menu_items() { $this->remove_gutenberg_menu(); // We don't need the `My Mailboxes` when the interface is set to wp-admin or the site is a staging site, - if ( $this->is_using_default_admin_menu() && ! get_option( 'wpcom_is_staging_site' ) ) { + if ( ! $this->use_wp_admin_interface() && ! get_option( 'wpcom_is_staging_site' ) ) { $this->add_my_mailboxes_menu(); } @@ -132,7 +132,7 @@ public function add_users_menu() { $this->update_submenus( $slug, $submenus_to_update ); } - if ( $this->is_using_default_admin_menu() ) { + if ( ! $this->use_wp_admin_interface() ) { // The 'Subscribers' menu exists in the Jetpack menu for Classic wp-admin interface, so only add it for non-wp-admin interfaces. // // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- Core should ideally document null for no-callback arg. https://core.trac.wordpress.org/ticket/52539. add_submenu_page( 'users.php', esc_attr__( 'Subscribers', 'jetpack-masterbar' ), __( 'Subscribers', 'jetpack-masterbar' ), 'list_users', 'https://wordpress.com/subscribers/' . $this->domain, null ); @@ -322,7 +322,7 @@ public function get_upsell_nudge() { */ public function add_jetpack_menu() { // This is supposed to be the same as class-admin-menu but with a different position specified for the Jetpack menu. - if ( ! $this->is_using_default_admin_menu() ) { + if ( $this->use_wp_admin_interface() ) { parent::create_jetpack_menu( 2, false ); } else { parent::add_jetpack_menu(); @@ -445,7 +445,7 @@ public function add_options_menu() { // Hide Settings > Performance when the interface is set to wp-admin. // This is due to these settings are mostly also available in Jetpack > Settings, in the Performance tab. - if ( ! $this->is_using_default_admin_menu() ) { + if ( $this->use_wp_admin_interface() ) { $this->hide_submenu_page( 'options-general.php', 'https://wordpress.com/settings/performance/' . $this->domain ); } } @@ -457,7 +457,7 @@ public function add_tools_menu() { parent::add_tools_menu(); // Link the Tools menu to Available Tools when the interface is set to wp-admin. - if ( ! $this->is_using_default_admin_menu() ) { + if ( $this->use_wp_admin_interface() ) { // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- Core should ideally document null for no-callback arg. https://core.trac.wordpress.org/ticket/52539. add_submenu_page( 'tools.php', esc_attr__( 'Available Tools', 'jetpack-masterbar' ), __( 'Available Tools', 'jetpack-masterbar' ), 'edit_posts', 'tools.php', null, 0 ); } diff --git a/projects/packages/masterbar/src/admin-menu/class-base-admin-menu.php b/projects/packages/masterbar/src/admin-menu/class-base-admin-menu.php index 65400fff1d1bc..dfc5ccb3954c1 100644 --- a/projects/packages/masterbar/src/admin-menu/class-base-admin-menu.php +++ b/projects/packages/masterbar/src/admin-menu/class-base-admin-menu.php @@ -756,21 +756,6 @@ public function use_wp_admin_interface() { return 'wp-admin' === get_option( 'wpcom_admin_interface' ); } - /** - * Check if the user has the default (Calypso) Admin menu. - * - * @return bool - */ - public function is_using_default_admin_menu() { - remove_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' ); - $option = get_option( 'wpcom_admin_interface' ) !== 'wp-admin'; - if ( function_exists( 'wpcom_admin_interface_pre_get_option' ) ) { - add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option', 10 ); - } - - return $option; - } - /** * Create the desired menu output. */ From b3ee2c2bf4f9020dfa3c61fc7e54cd0b794cc3a7 Mon Sep 17 00:00:00 2001 From: Miguel Torres <1233880+mmtr@users.noreply.github.com> Date: Thu, 19 Dec 2024 15:50:23 +0100 Subject: [PATCH 42/63] Post categories: Add quick action to change the default category (#40667) --- .../add-set-default-category-quick-action | 4 + .../src/class-jetpack-mu-wpcom.php | 1 + .../post-categories/quick-actions.php | 87 +++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/add-set-default-category-quick-action create mode 100644 projects/packages/jetpack-mu-wpcom/src/features/post-categories/quick-actions.php diff --git a/projects/packages/jetpack-mu-wpcom/changelog/add-set-default-category-quick-action b/projects/packages/jetpack-mu-wpcom/changelog/add-set-default-category-quick-action new file mode 100644 index 0000000000000..562cea91dca9a --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/add-set-default-category-quick-action @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Post categories: Add quick action to change default category 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 26cf4192d604a..89597cf7ce895 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 @@ -107,6 +107,7 @@ public static function load_features() { require_once __DIR__ . '/features/import-customizations/import-customizations.php'; require_once __DIR__ . '/features/marketplace-products-updater/class-marketplace-products-updater.php'; require_once __DIR__ . '/features/media/heif-support.php'; + require_once __DIR__ . '/features/post-categories/quick-actions.php'; require_once __DIR__ . '/features/site-editor-dashboard-link/site-editor-dashboard-link.php'; require_once __DIR__ . '/features/wpcom-admin-dashboard/wpcom-admin-dashboard.php'; require_once __DIR__ . '/features/wpcom-block-editor/class-jetpack-wpcom-block-editor.php'; diff --git a/projects/packages/jetpack-mu-wpcom/src/features/post-categories/quick-actions.php b/projects/packages/jetpack-mu-wpcom/src/features/post-categories/quick-actions.php new file mode 100644 index 0000000000000..3259f07466dca --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/src/features/post-categories/quick-actions.php @@ -0,0 +1,87 @@ +term_id === $default_category ) { + return $actions; + } + + $action = 'set-default'; + + $link = add_query_arg( array( $action => $category->term_id ) ); + $link = wp_nonce_url( $link, $action . '_' . $category->term_id ); + + $actions[ $action ] = sprintf( + '%3$s', + esc_url( $link ), + /* translators: category name */ + esc_attr( sprintf( __( 'Set “%s” as the default category', 'jetpack-mu-wpcom' ), $category->name ) ), + esc_html( __( 'Set as default', 'jetpack-mu-wpcom' ) ) + ); + return $actions; +} +add_filter( 'category_row_actions', 'wpcom_add_set_default_category_quick_action', 10, 2 ); + +/** + * Changes the default post category. + */ +function wpcom_set_default_category() { + if ( ! isset( $_GET['taxonomy'] ) || 'category' !== sanitize_text_field( wp_unslash( $_GET['taxonomy'] ) ) ) { + return; + } + + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + $action = 'set-default'; + + if ( ! isset( $_GET[ $action ] ) ) { + return; + } + + $category_id = sanitize_text_field( wp_unslash( $_GET[ $action ] ) ); + if ( ! is_numeric( $category_id ) ) { + return; + } + + check_admin_referer( $action . '_' . $category_id ); + + $category = get_category( (int) $category_id ); + if ( is_wp_error( $category ) || ! $category ) { + return; + } + + update_option( 'default_category', $category->term_id ); + + add_action( + 'admin_notices', + function () { + wp_admin_notice( + __( 'Default category changed successfully.', 'jetpack-mu-wpcom' ), + array( + 'type' => 'success', + 'dismissible' => true, + ) + ); + } + ); +} +add_action( 'load-edit-tags.php', 'wpcom_set_default_category' ); From b0015a6680899ce9a964e36e3a4d7726a6234ce7 Mon Sep 17 00:00:00 2001 From: Dylan Munson <65001528+CodeyGuyDylan@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:04:03 -0700 Subject: [PATCH 43/63] Remove/old welcome banner code (#40671) * Remove old welcome banner code * changelog --- .../assets/images/site-cards.png | Bin 179712 -> 0 bytes .../_inc/components/welcome-banner/index.jsx | 94 ------------------ .../stories/broken/index.stories.js | 18 ---- .../welcome-banner/style.module.scss | 75 -------------- .../changelog/remove-old-welcome-banner-code | 5 + 5 files changed, 5 insertions(+), 187 deletions(-) delete mode 100644 projects/packages/my-jetpack/_inc/components/welcome-banner/assets/images/site-cards.png delete mode 100644 projects/packages/my-jetpack/_inc/components/welcome-banner/index.jsx delete mode 100644 projects/packages/my-jetpack/_inc/components/welcome-banner/stories/broken/index.stories.js delete mode 100644 projects/packages/my-jetpack/_inc/components/welcome-banner/style.module.scss create mode 100644 projects/packages/my-jetpack/changelog/remove-old-welcome-banner-code diff --git a/projects/packages/my-jetpack/_inc/components/welcome-banner/assets/images/site-cards.png b/projects/packages/my-jetpack/_inc/components/welcome-banner/assets/images/site-cards.png deleted file mode 100644 index 83e562ede663c0bd77cd92602acfc4488f2e832c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179712 zcmV)RK(oJzP)jmlw2?}sTvlJV3?ed2)6|bED=WV= zV8DRiTjLN&pIduhh}X-3t_c29cqf;_J*gCetFO8l&p-bH1i90SFyM;e7+BOFlZr;; zi{n3mLSG&-+&&x$(P$Jq0#$gWWs_={_GatQ?Hp-v=T-;FaC zSw7IXLBM$>kjuo`_CDt9_x$+34N5;VuDtTf+Jb@tbar-vTbIjawzszv>kA4VkH;K7 zd^pwD*J~|hWMr7IK^-ThFGHP!*I$2~RBP1M*0Lr|)*_GdV{Z98^%|@@%-GeJrC1NAv@EB8M7`xG!jK596@ta1FH9x!=s*`lT&~n{P=sw%g@8H zAR1317>(m-;dZ&;&+sBk@pPcEDS#$L7zSJh=~nfiwPrxK+y%HbAaKcXIS2ozZQ>Tz zaVW@d%MXYL2_r`gg4g55*$AbLgKoEb26vf7MMYpJU2>cxZk2XeXC~{3^{4bjN^hj} zhFP;_5fpNBa|sqi%S!JOTS?-oNXj1bRSPfSHLzLXUCKXlQ52*YmTnAQbv^zYyQTJ!*? zv4fyKa9k#iLCG}iIInal1pYifK5+SbTyyca@!NSf;FmMLhJpSPI89vAOrbs2iANSL z!q>j@Bi#J-qjA{AYB6DNNV06S7?oY9CRV3gItc%vK?j)RDtbeis7`vP0v zar~TznEo}&#MQB3ftvT-=I@ zv(Dky(1A!zoM1t;sHx1PA!B7_JO9^ECXUGF@L)hpocvdJ>_*&xGd$_%L6PLe6uSad z@OexuSa2!oYU>dW1yQ+sr!r$tp{uJCVj6}GI}h{bT&PVPgUCcGnSyDW@OnI;bOJ?% zMcBA;GrHRkqcF#f>EjC#423~}HhB&8HAtCh^%{Q6nSaSKn>YfHI7Y3eO%tJL9BthZ z#1bi-Y77}%f@xDnp`3Wj_1RaK|Nkvln0O7g|ijU zJoC(F0GLV#5EG{+R^TwnlOClVC=hFs^<+z67|P6nv32WK;uxuDD9WS_c31`tS6p$0 z%a&BpFx z|Ni}unZIP?#Ky)`E?a9CyhH+#AP}zjz1w|6DqksPrWh`@W<&~eq zLl54GyDz^Q^M=eumM8Ow26M~d-FSH0QvB@Ad+|!i4un7Ifq!@oGzc)<2FB-SA>ZYO zVC1Obu!EuroE7Mb!gzTXjIvr#ICdmM*Q<#WIQv5T*trg#^*{go^A9K!XV)H^IdpV% zkYoUug9Z)K=1cS@**Bp;G{@NirA3kn6=%lfxaxx`6D$r)0E|E&px@h;8Kgyv7M1oN z(EmD#;2>uP)gIh!8s$bn&hSs-^OT|TVql3EjaAR0YUi6M9X1qW$BqS=X_W8Uj3*yo z&)C=5!Pl7sKuG zArgxqD?1nc`uE307hi;91rd-;n-CnUq3e#s6yq>W!l{JM=f#NOgD`2r2;B#X=1dTz zHN|W@w5SNOIt0^dnZ$%4&QQbW^(rHmd)DeW91gGf*0;V@2Y_{8S|*MFu!#dtewdcF zh-qcNB_qcs&fu8HF;cbQR!p2@iepHYPU4(o;1uLBDWXRs*T+8E1E;;DBC`T@j?5NF zE=11CslLoIxHb5~18}PyTbK!A$;D7{8#1e5F4z;1Oy&B!|5Zrz@JcT|8 z%t#7@rWN6lF~jhKq9S+<1B_9lM!*g;(2cVS^=&ZT-3z1o@UfV4a#E9 z#&vadImgYCk`jUf&-__Cs?5V?PD~mYl9h8nj2%y?3;?1t$J_|Vy;!wsE&M(= za&rCry&;h_q06d~F!J+q0r=qcX5f-J7vn#{iKzU?5=jKQ!w5!Cy`l5_GcaZHNOg@2 zfY0j&5kWkfXw77r1Y!~s0+3UeLL*4j5xgl#zk*@KK^<9OUM zaE5KUgCS)jE~i>#UAmS$_E>3dZvOW{24GkQilC?;KuT)iUvUsbcMEJ+g_LF=GaC3}iE> zVxS2HDXXeP2xs!RwXb=TvZ`GZi7G<*i%-fr*S$VCeCK0Izs3PH8piu zz46CTzTRC14N`Z6R?Q` zKvG790tXCkt;aee2OGq=iS9U<$ha(R5GpNlO8^YkB*WV1!0YfJn~$u|!L6YJ8a_B@ z{&g@2!~_A5#qZGQXzy0<*MgQn7reeqWM*a{kunhsMbXtAMKYBJ!B7NjPuFEqf0j>| z__B&-Vf2Ix5O4WAw!FR;fuMoxF+adX^FEI;qXyx^ISbI<8N|XRYw+&I?YcZ^rb#gk zxfT+M6pD%qv32ui#G_Hb?S;2+oGzutVllM0wISRcLOc>x#$_Nr{n=09c#xgxJz=Iq z%B{S9j?L+*F1HRISd1x?Mxv-F53trfN|}{xB~&r-rY6CF=rOI2!OD^j8C=}!-)|ew z$jQ~PVZ-j{j5KGa*;sMBlBGnl!9muTLV;tQBCj8Zf^2eNkm?)UTGcD5Xh}>p6kXi* z^7l=aJrVlQha?5+Vv5D+R?x^9XvI^R?X(v7R^b53N=^9$H@Gtbt zQ_s@zf-kx5aEuOh1W!1-)a$0xCY=uO_ql6N4Q<`Nm$oUE?bxTeedm7KvFiX+^$*a_ zUDbS^=5Aqm9o09qif%=(N6j+_y5qP$Ge3OH zx~>)H6zbc6cdMk_dQ5BOq&?alu4f}&c;STyq=YIZRRA*k=XGnc7RR*Knd*`#9I(o) z&hn~#*D8-opLGbPGKh9f8=QR*O>#Mjmpcp`IPhB0q-;ige`6W8Y%NE9{b8N$%E
()Jq-FwQBh{ZuL@W$(JW5x0(5D0W2nNI0jj%+X6fn+Ly zciwy#ZWp*D44>BruP*~?Vt`;*H$tHx(&~Hy;nF#m;CNveK(^m^Qf?
*vC;1Wq;b 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 YZ%B=!PGCsM$LC)N6({gqUF?hq~d&W?#_V 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ξkHY$8_D{8od4kaG4Ayc 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 IM6N<$f+C%xg#Z8m diff --git a/projects/packages/my-jetpack/_inc/components/welcome-banner/index.jsx b/projects/packages/my-jetpack/_inc/components/welcome-banner/index.jsx deleted file mode 100644 index c40848b4610d7..0000000000000 --- a/projects/packages/my-jetpack/_inc/components/welcome-banner/index.jsx +++ /dev/null @@ -1,94 +0,0 @@ -import { Container, Col, Button, Text } from '@automattic/jetpack-components'; -import { __ } from '@wordpress/i18n'; -import { close } from '@wordpress/icons'; -import { useEffect, useCallback, useState } from 'react'; -import { MyJetpackRoutes } from '../../constants'; -import useWelcomeBanner from '../../data/welcome-banner/use-welcome-banner'; -import useAnalytics from '../../hooks/use-analytics'; -import useMyJetpackConnection from '../../hooks/use-my-jetpack-connection'; -import useMyJetpackNavigate from '../../hooks/use-my-jetpack-navigate'; -import { CardWrapper } from '../card'; -import styles from './style.module.scss'; - -/** - * Component that renders the Welcome banner on My Jetpack. - * - * @return {object} The WelcomeBanner component. - */ -const WelcomeBanner = () => { - const { recordEvent } = useAnalytics(); - const { isWelcomeBannerVisible, dismissWelcomeBanner } = useWelcomeBanner(); - const { isRegistered, isUserConnected } = useMyJetpackConnection(); - const navigateToConnectionPage = useMyJetpackNavigate( MyJetpackRoutes.Connection ); - const [ bannerVisible, setBannerVisible ] = useState( isWelcomeBannerVisible ); - const shouldDisplayConnectionButton = ! isRegistered || ! isUserConnected; - - useEffect( () => { - if ( bannerVisible ) { - recordEvent( 'jetpack_myjetpack_welcome_banner_view' ); - } - }, [ bannerVisible, recordEvent ] ); - - const onDismissClick = useCallback( () => { - recordEvent( 'jetpack_myjetpack_welcome_banner_dismiss_click' ); - setBannerVisible( false ); - dismissWelcomeBanner(); - }, [ recordEvent, dismissWelcomeBanner ] ); - - const onFinishConnectionClick = useCallback( () => { - recordEvent( 'jetpack_myjetpack_welcome_banner_finish_connection_click' ); - navigateToConnectionPage(); - }, [ recordEvent, navigateToConnectionPage ] ); - - if ( ! bannerVisible ) { - return null; - } - - return ( - - - - - - - { __( 'Welcome to Jetpack!', 'jetpack-my-jetpack' ) } - - - { __( - 'Jetpack is a suite of security, performance, and growth tools made for WordPress sites by the WordPress experts.', - 'jetpack-my-jetpack' - ) } - - - { __( - 'It’s the ultimate toolkit for best-in-class websites, with everything you need to grow your business. Choose a plan below to get started.', - 'jetpack-my-jetpack' - ) } - - { shouldDisplayConnectionButton && ( - - ) } - - - - -
@@ -162,7 +165,7 @@ class="" -
+ ); +} diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-feedback/style.scss b/projects/js-packages/ai-client/src/components/ai-feedback/style.scss similarity index 70% rename from projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-feedback/style.scss rename to projects/js-packages/ai-client/src/components/ai-feedback/style.scss index 9697d17aff1ad..89b8f9f2053bc 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-feedback/style.scss +++ b/projects/js-packages/ai-client/src/components/ai-feedback/style.scss @@ -11,6 +11,6 @@ } &__thumb-selected { - color: var(--wp-components-color-accent,var(--wp-admin-theme-color,#3858e9)); + color: var( --wp-components-color-accent, var( --wp-admin-theme-color, #3858e9 ) ); } } diff --git a/projects/js-packages/ai-client/src/components/index.ts b/projects/js-packages/ai-client/src/components/index.ts index 20d5714db6baf..1ef8b40b80426 100644 --- a/projects/js-packages/ai-client/src/components/index.ts +++ b/projects/js-packages/ai-client/src/components/index.ts @@ -1,4 +1,5 @@ export { AIControl, BlockAIControl, ExtensionAIControl } from './ai-control/index.js'; +export { default as AiFeedbackThumbs } from './ai-feedback/index.js'; export { default as AiStatusIndicator } from './ai-status-indicator/index.js'; export { default as AudioDurationDisplay } from './audio-duration-display/index.js'; export { default as AiModalFooter } from './ai-modal-footer/index.js'; diff --git a/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.tsx b/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.tsx index e539e1d751b6a..2b7e63ff9c9e6 100644 --- a/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.tsx +++ b/projects/js-packages/ai-client/src/logo-generator/components/logo-presenter.tsx @@ -9,6 +9,7 @@ import debugFactory from 'debug'; /** * Internal dependencies */ +import AiFeedbackThumbs from '../../components/ai-feedback/index.js'; import CheckIcon from '../assets/icons/check.js'; import LogoIcon from '../assets/icons/logo.js'; import MediaIcon from '../assets/icons/media.js'; @@ -152,11 +153,50 @@ const LogoEmpty: React.FC = () => { ); }; +const RateLogo: React.FC< { + disabled: boolean; + ratedItem: string; + onRate: ( rating: string ) => void; +} > = ( { disabled, ratedItem, onRate } ) => { + const { logos, selectedLogo } = useLogoGenerator(); + const savedRatings = logos + .filter( logo => logo.rating ) + .reduce( ( acc, logo ) => { + acc[ logo.url ] = logo.rating; + return acc; + }, {} ); + + return ( + + ); +}; + const LogoReady: React.FC< { siteId: string; logo: Logo; onApplyLogo: ( mediaId: number ) => void; } > = ( { siteId, logo, onApplyLogo } ) => { + const handleRateLogo = ( rating: string ) => { + // Update localStorage + updateLogo( { + siteId, + url: logo.url, + newUrl: logo.url, + mediaId: logo.mediaId, + rating, + } ); + }; + return ( <> +
diff --git a/projects/js-packages/ai-client/src/logo-generator/hooks/use-logo-generator.ts b/projects/js-packages/ai-client/src/logo-generator/hooks/use-logo-generator.ts index 70acbf3fe98e0..d2d232da900d0 100644 --- a/projects/js-packages/ai-client/src/logo-generator/hooks/use-logo-generator.ts +++ b/projects/js-packages/ai-client/src/logo-generator/hooks/use-logo-generator.ts @@ -412,10 +412,13 @@ User request:${ prompt }`; throw error; } + const revisedPrompt = image.data[ 0 ].revised_prompt || null; + // response_format=url returns object with url, otherwise b64_json const logo: Logo = { url: 'data:image/png;base64,' + image.data[ 0 ].b64_json, description: prompt, + revisedPrompt, }; try { @@ -424,6 +427,7 @@ User request:${ prompt }`; url: savedLogo.mediaURL, description: prompt, mediaId: savedLogo.mediaId, + revisedPrompt, } ); } catch ( error ) { storeLogo( logo ); diff --git a/projects/js-packages/ai-client/src/logo-generator/lib/logo-storage.ts b/projects/js-packages/ai-client/src/logo-generator/lib/logo-storage.ts index 81e9b08319575..e6c6bb1176e83 100644 --- a/projects/js-packages/ai-client/src/logo-generator/lib/logo-storage.ts +++ b/projects/js-packages/ai-client/src/logo-generator/lib/logo-storage.ts @@ -10,21 +10,28 @@ const MAX_LOGOS = 10; /** * Add an entry to the site's logo history. * - * @param {SaveToStorageProps} saveToStorageProps - The properties to save to storage - * @param {SaveToStorageProps.siteId} saveToStorageProps.siteId - The site ID - * @param {SaveToStorageProps.url} saveToStorageProps.url - The URL of the logo - * @param {SaveToStorageProps.description} saveToStorageProps.description - The description of the logo, based on the prompt used to generate it - * @param {SaveToStorageProps.mediaId} saveToStorageProps.mediaId - The media ID of the logo on the backend - * + * @param {SaveToStorageProps} saveToStorageProps - The properties to save to storage + * @param {SaveToStorageProps.siteId} saveToStorageProps.siteId - The site ID + * @param {SaveToStorageProps.url} saveToStorageProps.url - The URL of the logo + * @param {SaveToStorageProps.description} saveToStorageProps.description - The description of the logo, based on the prompt used to generate it + * @param {SaveToStorageProps.mediaId} saveToStorageProps.mediaId - The media ID of the logo on the backend + * @param {SaveToStorageProps.revisedPrompt} saveToStorageProps.revisedPrompt - The revised prompt of the logo * @return {Logo} The logo that was saved */ -export function stashLogo( { siteId, url, description, mediaId }: SaveToStorageProps ) { +export function stashLogo( { + siteId, + url, + description, + mediaId, + revisedPrompt, +}: SaveToStorageProps ) { const storedContent = getSiteLogoHistory( siteId ); const logo: Logo = { url, description, mediaId, + revisedPrompt, }; storedContent.push( logo ); @@ -45,9 +52,10 @@ export function stashLogo( { siteId, url, description, mediaId }: SaveToStorageP * @param {UpdateInStorageProps.url} updateInStorageProps.url - The URL of the logo to update * @param {UpdateInStorageProps.newUrl} updateInStorageProps.newUrl - The new URL of the logo * @param {UpdateInStorageProps.mediaId} updateInStorageProps.mediaId - The new media ID of the logo + * @param {UpdateInStorageProps.rating} updateInStorageProps.rating - The new rating of the logo * @return {Logo} The logo that was updated */ -export function updateLogo( { siteId, url, newUrl, mediaId }: UpdateInStorageProps ) { +export function updateLogo( { siteId, url, newUrl, mediaId, rating }: UpdateInStorageProps ) { const storedContent = getSiteLogoHistory( siteId ); const index = storedContent.findIndex( logo => logo.url === url ); @@ -55,6 +63,7 @@ export function updateLogo( { siteId, url, newUrl, mediaId }: UpdateInStoragePro if ( index > -1 ) { storedContent[ index ].url = newUrl; storedContent[ index ].mediaId = mediaId; + storedContent[ index ].rating = rating; } localStorage.setItem( @@ -96,6 +105,7 @@ export function getSiteLogoHistory( siteId: string ) { url: logo.url, description: logo.description, mediaId: logo.mediaId, + rating: logo.rating, } ) ); return storedContent; diff --git a/projects/js-packages/ai-client/src/logo-generator/store/types.ts b/projects/js-packages/ai-client/src/logo-generator/store/types.ts index b34f3b9f8a22e..d8c0f0fbcb1bf 100644 --- a/projects/js-packages/ai-client/src/logo-generator/store/types.ts +++ b/projects/js-packages/ai-client/src/logo-generator/store/types.ts @@ -140,6 +140,8 @@ export type Logo = { url: string; description: string; mediaId?: number; + rating?: string; + revisedPrompt?: string; }; export type RequestError = string | Error | null; diff --git a/projects/js-packages/ai-client/src/logo-generator/types.ts b/projects/js-packages/ai-client/src/logo-generator/types.ts index e54cf774ac98e..2a617a2b97d58 100644 --- a/projects/js-packages/ai-client/src/logo-generator/types.ts +++ b/projects/js-packages/ai-client/src/logo-generator/types.ts @@ -92,6 +92,7 @@ export type UpdateInStorageProps = { url: Logo[ 'url' ]; newUrl: Logo[ 'url' ]; mediaId: Logo[ 'mediaId' ]; + rating?: Logo[ 'rating' ]; }; export type RemoveFromStorageProps = { diff --git a/projects/plugins/jetpack/changelog/change-jetpack-ai-rate-logo-generator b/projects/plugins/jetpack/changelog/change-jetpack-ai-rate-logo-generator new file mode 100644 index 0000000000000..931f46e447031 --- /dev/null +++ b/projects/plugins/jetpack/changelog/change-jetpack-ai-rate-logo-generator @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Jetpack AI: Add thumbs up/down component to AI logo generator diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/lib/utils/get-feature-availability.ts b/projects/plugins/jetpack/extensions/blocks/ai-assistant/lib/utils/get-feature-availability.ts index b18ea2a171d07..f6be56c958493 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/lib/utils/get-feature-availability.ts +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/lib/utils/get-feature-availability.ts @@ -3,6 +3,7 @@ */ import { getJetpackExtensionAvailability } from '@automattic/jetpack-shared-extension-utils'; +// TODO: Move to the AI Client js-package export function getFeatureAvailability( feature: string ): boolean { return getJetpackExtensionAvailability( feature ).available === true; } diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-feedback/index.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-feedback/index.tsx deleted file mode 100644 index fb99d2cb0fd0d..0000000000000 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-feedback/index.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; -import { Button, Tooltip } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { thumbsUp, thumbsDown } from '@wordpress/icons'; -import clsx from 'clsx'; -import { useState } from 'react'; -import { getFeatureAvailability } from '../../../../blocks/ai-assistant/lib/utils/get-feature-availability'; - -import './style.scss'; - -export default function AiFeedbackThumbs( { - disabled = false, - iconSize = 24, - ratedItem, - feature, -} ) { - const [ itemsRated, setItemsRated ] = useState( {} ); - const { tracks } = useAnalytics(); - - const rateAI = ( isThumbsUp: boolean ) => { - const aiRating = isThumbsUp ? 'thumbs-up' : 'thumbs-down'; - - setItemsRated( { - ...itemsRated, - [ ratedItem ]: aiRating, - } ); - - tracks.recordEvent( 'jetpack_ai_feedback', { - type: feature, - rating: aiRating, - } ); - }; - - const checkThumb = ( thumbValue: string ) => { - if ( ! itemsRated[ ratedItem ] ) { - return false; - } - - return itemsRated[ ratedItem ] === thumbValue; - }; - - return getFeatureAvailability( 'ai-response-feedback' ) ? ( -
- -
- ) : ( - <> - ); -} diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.tsx index 97fc5acec4950..f9dec07e3cb2b 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.tsx @@ -1,6 +1,7 @@ /** * External dependencies */ +import { AiFeedbackThumbs } from '@automattic/jetpack-ai-client'; import { Spinner } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { Icon, chevronLeft, chevronRight } from '@wordpress/icons'; @@ -8,13 +9,14 @@ import clsx from 'clsx'; /** * Internal dependencies */ -import AiFeedbackThumbs from '../../ai-feedback'; import AiIcon from '../../ai-icon'; import './carrousel.scss'; export type CarrouselImageData = { image?: string; libraryId?: number | string; + prompt?: string; + revisedPrompt?: string; libraryUrl?: string; generating?: boolean; error?: { @@ -108,7 +110,7 @@ export default function Carrousel( {
{ images.length > 1 && prevButton } - { images.map( ( { image, generating, error }, index ) => ( + { images.map( ( { image, generating, error, revisedPrompt }, index ) => (
) : ( - + { ) } ) } @@ -171,6 +177,11 @@ export default function Carrousel( { disabled={ aiFeedbackDisabled( images[ current ] ) } ratedItem={ images[ current ].libraryUrl || '' } iconSize={ 20 } + options={ { + mediaLibraryId: Number( images[ current ].libraryId ), + prompt: images[ current ].prompt, + revisedPrompt: images[ current ].revisedPrompt, + } } feature="image-generator" />
diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/hooks/use-ai-image.ts b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/hooks/use-ai-image.ts index b8897913d99a4..bd2fcdd96695a 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/hooks/use-ai-image.ts +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/hooks/use-ai-image.ts @@ -167,7 +167,9 @@ export default function useAiImage( { .then( result => { if ( result.data.length > 0 ) { const image = 'data:image/png;base64,' + result.data[ 0 ].b64_json; - updateImages( { image }, pointer.current ); + const prompt = userPrompt || null; + const revisedPrompt = result.data[ 0 ].revised_prompt || null; + updateImages( { image, prompt, revisedPrompt }, pointer.current ); updateRequestsCount(); saveToMediaLibrary( image, name ) .then( savedImage => { @@ -181,7 +183,7 @@ export default function useAiImage( { image, libraryId: savedImage?.id, libraryUrl: savedImage?.url, - revisedPrompt: result.data[ 0 ].revised_prompt || '', + revisedPrompt, } ); } ) .catch( () => { From e564fe0d3a5d24651d6dc6c059678712955b5ebc Mon Sep 17 00:00:00 2001 From: Peter Petrov Date: Fri, 20 Dec 2024 23:36:25 +0200 Subject: [PATCH 63/63] Boost: Fix ISA showing an error if no report was found (#40660) * Fix boost API client not properly parsing errors returned by wpcom * Fix showing error when a report wasn't found User might not have requested an analysis yet. That doesn't mean the UI should show an error. This restores behavior from pre-Boost 3.6.0. * Add changelogs * Revert boost core changes * remove changelog * Revert "Revert boost core changes" This reverts commit afa7179a61bdc347cb203233f9f4f33778ce2a29. * Revert "remove changelog" This reverts commit 870e13d0c0fd09966e3f36d1834bb9b5d67c6cff. * Clarify comment --------- Co-authored-by: Adnan Haque --- ...fix-not-parsing-some-wpcom-errors-properly | 4 ++++ .../boost-core/src/lib/class-utils.php | 23 +++++++++++++++---- .../recommendations-meta.tsx | 2 +- ...fix-isa-showing-error-if-no-report-present | 4 ++++ 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 projects/packages/boost-core/changelog/fix-not-parsing-some-wpcom-errors-properly create mode 100644 projects/plugins/boost/changelog/fix-isa-showing-error-if-no-report-present diff --git a/projects/packages/boost-core/changelog/fix-not-parsing-some-wpcom-errors-properly b/projects/packages/boost-core/changelog/fix-not-parsing-some-wpcom-errors-properly new file mode 100644 index 0000000000000..d24d162efc1e6 --- /dev/null +++ b/projects/packages/boost-core/changelog/fix-not-parsing-some-wpcom-errors-properly @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +General: Fixed not parsing error responses from WordPress.com properly. diff --git a/projects/packages/boost-core/src/lib/class-utils.php b/projects/packages/boost-core/src/lib/class-utils.php index 848cf3009c0b3..52207e115f075 100644 --- a/projects/packages/boost-core/src/lib/class-utils.php +++ b/projects/packages/boost-core/src/lib/class-utils.php @@ -133,11 +133,26 @@ public static function send_wpcom_request( $method, $endpoint, $args = null, $bo $code ); + /* + * Normalize error responses from WordPress.com. + * + * When WordPress.com returns an error from Boost Cloud, the body contains + * statusCode and error. When it returns a WP_Error, it contains code and message. + */ // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - $err_code = empty( $data['statusCode'] ) ? 'http_error' : $data['statusCode']; - $message = empty( $data['error'] ) ? $default_message : $data['error']; - - return new \WP_Error( $err_code, $message ); + if ( isset( $data['statusCode'] ) && isset( $data['error'] ) ) { + // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + $data_code = $data['statusCode']; + $data_message = $data['error']; + } elseif ( isset( $data['code'] ) && isset( $data['message'] ) ) { + $data_code = $data['code']; + $data_message = $data['message']; + } + + $error_code = empty( $data_code ) ? 'http_error' : $data_code; + $message = empty( $data_message ) ? $default_message : $data_message; + + return new \WP_Error( $error_code, $message ); } return $data; diff --git a/projects/plugins/boost/app/assets/src/js/features/image-size-analysis/recommendations-meta/recommendations-meta.tsx b/projects/plugins/boost/app/assets/src/js/features/image-size-analysis/recommendations-meta/recommendations-meta.tsx index 049d1bb448a59..f475249d3f8b6 100644 --- a/projects/plugins/boost/app/assets/src/js/features/image-size-analysis/recommendations-meta/recommendations-meta.tsx +++ b/projects/plugins/boost/app/assets/src/js/features/image-size-analysis/recommendations-meta/recommendations-meta.tsx @@ -87,7 +87,7 @@ const RecommendationsMeta: React.FC< Props > = ( { isCdnActive } ) => { isaRequest.isError; const getErrorMessage = ( report: typeof isaReport ) => { - if ( report?.status === 'error' || report?.status === 'not-found' ) { + if ( report?.status === 'error' ) { return report.message; } diff --git a/projects/plugins/boost/changelog/fix-isa-showing-error-if-no-report-present b/projects/plugins/boost/changelog/fix-isa-showing-error-if-no-report-present new file mode 100644 index 0000000000000..ec4939628d4b8 --- /dev/null +++ b/projects/plugins/boost/changelog/fix-isa-showing-error-if-no-report-present @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +UI: Fixed showing an error if no ISA report was found.