diff --git a/projects/js-packages/ai-client/changelog/add-jetpack-ai-general-image-style-selector b/projects/js-packages/ai-client/changelog/add-jetpack-ai-general-image-style-selector new file mode 100644 index 0000000000000..86eb508e7c24f --- /dev/null +++ b/projects/js-packages/ai-client/changelog/add-jetpack-ai-general-image-style-selector @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +AI Client: export image generator hook constants diff --git a/projects/js-packages/ai-client/src/index.ts b/projects/js-packages/ai-client/src/index.ts index fb185b2309ea2..b16c51b865ea8 100644 --- a/projects/js-packages/ai-client/src/index.ts +++ b/projects/js-packages/ai-client/src/index.ts @@ -16,6 +16,7 @@ export { default as useAudioTranscription } from './hooks/use-audio-transcriptio export { default as useTranscriptionPostProcessing } from './hooks/use-transcription-post-processing/index.js'; export { default as useAudioValidation } from './hooks/use-audio-validation/index.js'; export { default as useImageGenerator } from './hooks/use-image-generator/index.js'; +export * from './hooks/use-image-generator/constants.js'; /* * Components: Icons diff --git a/projects/js-packages/components/components/threat-fixer-button/index.tsx b/projects/js-packages/components/components/threat-fixer-button/index.tsx new file mode 100644 index 0000000000000..95788ac4334e7 --- /dev/null +++ b/projects/js-packages/components/components/threat-fixer-button/index.tsx @@ -0,0 +1,135 @@ +import { Button, Text, ActionPopover } from '@automattic/jetpack-components'; +import { CONTACT_SUPPORT_URL, type Threat, fixerStatusIsStale } from '@automattic/jetpack-scan'; +import { ExternalLink } from '@wordpress/components'; +import { createInterpolateElement, useCallback, useMemo, useState } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; +import styles from './styles.module.scss'; + +/** + * Threat Fixer Button component. + * + * @param {object} props - Component props. + * @param {object} props.threat - The threat. + * @param {Function} props.onClick - The onClick function. + * @param {string} props.className - The className. + * + * @return {JSX.Element} The component. + */ +export default function ThreatFixerButton( { + threat, + className, + onClick, +}: { + threat: Threat; + onClick: ( items: Threat[] ) => void; + className?: string; +} ): JSX.Element { + const [ isPopoverVisible, setIsPopoverVisible ] = useState( false ); + + const [ anchor, setAnchor ] = useState( null ); + + const children = useMemo( () => { + if ( ! threat.fixable ) { + return null; + } + if ( threat.fixer && threat.fixer.error ) { + return __( 'Error', 'jetpack' ); + } + if ( threat.fixer && threat.fixer.status === 'in_progress' ) { + return __( 'Fixing…', 'jetpack' ); + } + if ( threat.fixable.fixer === 'delete' ) { + return __( 'Delete', 'jetpack' ); + } + if ( threat.fixable.fixer === 'update' ) { + return __( 'Update', 'jetpack' ); + } + return __( 'Fix', 'jetpack' ); + }, [ threat.fixable, threat.fixer ] ); + + const errorMessage = useMemo( () => { + if ( threat.fixer && fixerStatusIsStale( threat.fixer ) ) { + return __( 'Fixer is taking longer than expected.', 'jetpack' ); + } + + if ( threat.fixer && 'error' in threat.fixer && threat.fixer.error ) { + return __( 'An error occurred auto-fixing this threat.', 'jetpack' ); + } + + return null; + }, [ threat.fixer ] ); + + const handleClick = useCallback( + ( event: React.MouseEvent ) => { + event.stopPropagation(); + if ( errorMessage && ! isPopoverVisible ) { + setIsPopoverVisible( true ); + return; + } + onClick( [ threat ] ); + }, + [ onClick, errorMessage, isPopoverVisible, threat ] + ); + + const closePopover = useCallback( () => { + setIsPopoverVisible( false ); + }, [] ); + + if ( ! threat.fixable ) { + return null; + } + + return ( +
+
+ ); +} diff --git a/projects/js-packages/components/components/threat-fixer-button/stories/index.stories.tsx b/projects/js-packages/components/components/threat-fixer-button/stories/index.stories.tsx new file mode 100644 index 0000000000000..6b378030a2d89 --- /dev/null +++ b/projects/js-packages/components/components/threat-fixer-button/stories/index.stories.tsx @@ -0,0 +1,30 @@ +import ThreatFixerButton from '../index.js'; + +export default { + title: 'JS Packages/Components/Threat Fixer Button', + component: ThreatFixerButton, +}; + +export const Default = args => ; +Default.args = { + threat: { fixable: { fixer: 'edit' } }, + onClick: () => alert( 'Edit fixer callback triggered' ), // eslint-disable-line no-alert +}; + +export const Update = args => ; +Update.args = { + threat: { fixable: { fixer: 'update' } }, + onClick: () => alert( 'Update fixer callback triggered' ), // eslint-disable-line no-alert +}; + +export const Delete = args => ; +Delete.args = { + threat: { fixable: { fixer: 'delete' } }, + onClick: () => alert( 'Delete fixer callback triggered' ), // eslint-disable-line no-alert +}; + +export const Loading = args => ; +Loading.args = { + threat: { fixable: { fixer: 'edit' }, fixer: { status: 'in_progress' } }, + onClick: () => alert( 'Fixer callback triggered' ), // eslint-disable-line no-alert +}; diff --git a/projects/js-packages/components/components/threat-fixer-button/styles.module.scss b/projects/js-packages/components/components/threat-fixer-button/styles.module.scss new file mode 100644 index 0000000000000..071761daff049 --- /dev/null +++ b/projects/js-packages/components/components/threat-fixer-button/styles.module.scss @@ -0,0 +1,9 @@ +.support-link { + color: inherit; + + &:focus, + &:hover { + color: inherit; + box-shadow: none; + } +} diff --git a/projects/js-packages/components/components/threats-data-views/constants.ts b/projects/js-packages/components/components/threats-data-views/constants.ts index a3b8f4449f885..721ebcd7f4034 100644 --- a/projects/js-packages/components/components/threats-data-views/constants.ts +++ b/projects/js-packages/components/components/threats-data-views/constants.ts @@ -1,7 +1,5 @@ import { __ } from '@wordpress/i18n'; -export const PAID_PLUGIN_SUPPORT_URL = 'https://jetpack.com/contact-support/?rel=support'; - export const THREAT_STATUSES: { value: string; label: string; variant?: 'success' | 'warning' }[] = [ { value: 'current', label: __( 'Active', 'jetpack' ), variant: 'warning' }, diff --git a/projects/js-packages/components/components/threats-data-views/index.tsx b/projects/js-packages/components/components/threats-data-views/index.tsx index 66846fcde9948..bbfd6df36edbc 100644 --- a/projects/js-packages/components/components/threats-data-views/index.tsx +++ b/projects/js-packages/components/components/threats-data-views/index.tsx @@ -126,7 +126,7 @@ export default function ThreatsDataViews( { isThreatEligibleForFix?: ( threat: Threat ) => boolean; isThreatEligibleForIgnore?: ( threat: Threat ) => boolean; isThreatEligibleForUnignore?: ( threat: Threat ) => boolean; - onFixThreats?: ActionButton< Threat >[ 'callback' ]; + onFixThreats?: ( threats: Threat[] ) => void; onIgnoreThreats?: ActionButton< Threat >[ 'callback' ]; onUnignoreThreats?: ActionButton< Threat >[ 'callback' ]; } ): JSX.Element { @@ -151,14 +151,22 @@ export default function ThreatsDataViews( { const defaultLayouts: SupportedLayouts = { table: { ...baseView, - fields: [ 'severity', 'threat', 'auto-fix' ], + fields: [ 'severity', 'threat', 'type', 'auto-fix' ], layout: { primaryField: 'severity', + combinedFields: [ + { + id: 'threat', + label: __( 'Threat', 'jetpack' ), + children: [ 'title', 'description' ], + direction: 'vertical', + }, + ], }, }, list: { ...baseView, - fields: [ 'severity', 'subtitle', 'signature', 'auto-fix' ], + fields: [ 'severity', 'type', 'signature' ], layout: { primaryField: 'title', mediaField: 'icon', @@ -238,31 +246,22 @@ export default function ThreatsDataViews( { const fields = useMemo( () => { const result: Field< Threat >[] = [ { - id: 'threat', - label: __( 'Threat', 'jetpack' ), + id: 'title', + label: __( 'Title', 'jetpack' ), enableGlobalSearch: true, enableHiding: false, - getValue( { item }: { item: Threat } ) { - return item.title + item.description; - }, - render( { item }: { item: Threat } ) { - return ( -
-
- - { getThreatSubtitle( item ) } -
-
{ item.title }
-
{ item.description }
-
- ); - }, + render: ( { item }: { item: Threat } ) => ( +
{ item.title }
+ ), }, { - id: 'title', - label: __( 'Title', 'jetpack' ), + id: 'description', + label: __( 'Description', 'jetpack' ), enableGlobalSearch: true, enableHiding: false, + render: ( { item }: { item: Threat } ) => ( +
{ item.description }
+ ), }, { id: 'icon', @@ -272,13 +271,51 @@ export default function ThreatsDataViews( { return getThreatType( item ); }, render( { item }: { item: Threat } ) { + const type = getThreatType( item ); + + let icon; + switch ( type ) { + case 'plugin': + icon = plugins; + break; + case 'theme': + icon = color; + break; + case 'core': + icon = wordpress; + break; + case 'file': + icon = code; + break; + case 'database': + icon = grid; + break; + default: + icon = shield; + } + return (
- +
); }, }, + ...( dataFields.includes( 'severity' ) + ? [ + { + id: 'severity', + label: __( 'Severity', 'jetpack' ), + type: 'integer' as FieldType, + getValue( { item }: { item: Threat } ) { + return item.severity ?? 0; + }, + render( { item }: { item: Threat } ) { + return ; + }, + }, + ] + : [] ), { id: 'status', label: __( 'Status', 'jetpack' ), @@ -312,31 +349,10 @@ export default function ThreatsDataViews( { }, { id: 'type', - label: __( 'Category', 'jetpack' ), + label: __( 'Type', 'jetpack' ), elements: THREAT_TYPES, getValue( { item }: { item: Threat } ) { - if ( item.signature === 'Vulnerable.WP.Core' ) { - return 'core'; - } - if ( item.extension ) { - return item.extension.type; - } - if ( item.filename ) { - return 'file'; - } - if ( item.table ) { - return 'database'; - } - - return 'uncategorized'; - }, - }, - { - id: 'subtitle', - label: __( 'Affected Item', 'jetpack' ), - enableHiding: false, - getValue( { item }: { item: Threat } ) { - return getThreatSubtitle( item ); + return getThreatType( item ) ?? ''; }, }, ...( dataFields.includes( 'signature' ) @@ -352,45 +368,31 @@ export default function ThreatsDataViews( { }, ] : [] ), - ...( dataFields.includes( 'severity' ) - ? [ - { - id: 'severity', - label: __( 'Severity', 'jetpack' ), - type: 'integer' as FieldType, - getValue( { item }: { item: Threat } ) { - return item.severity ?? 0; - }, - render( { item }: { item: Threat } ) { - return ; - }, - }, - ] - : [] ), ...( dataFields.includes( 'fixable' ) ? [ { id: 'auto-fix', - label: __( 'Auto-fix', 'jetpack' ), + label: __( 'Auto-Fix', 'jetpack' ), enableHiding: false, - type: 'integer' as FieldType, + elements: [ + { + value: 'yes', + label: __( 'Yes', 'jetpack' ), + }, + { + value: 'no', + label: __( 'No', 'jetpack' ), + }, + ], getValue( { item }: { item: Threat } ) { - return item.fixable ? 1 : 0; + return item.fixable ? 'yes' : 'no'; }, render( { item }: { item: Threat } ) { if ( ! item.fixable ) { return null; } - if ( view.type === 'table' ) { - return ( -
- -
- ); - } - - return ; + return ; }, }, ] @@ -436,7 +438,7 @@ export default function ThreatsDataViews( { ]; return result; - }, [ extensions, signatures, dataFields, view ] ); + }, [ dataFields, extensions, signatures, onFixThreats ] ); const isStatusFilterSelected = ( threatStatuses: ThreatStatus[] ) => view.filters.some( diff --git a/projects/js-packages/components/components/threats-data-views/stories/index.stories.tsx b/projects/js-packages/components/components/threats-data-views/stories/index.stories.tsx index 522b4c4b075d5..4693c94fdd093 100644 --- a/projects/js-packages/components/components/threats-data-views/stories/index.stories.tsx +++ b/projects/js-packages/components/components/threats-data-views/stories/index.stories.tsx @@ -286,6 +286,15 @@ FixerStatuses.args = { source: null, }, ], + onFixThreats: () => + alert( 'Threat fix action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + onIgnoreThreats: () => + alert( 'Ignore threat action callback triggered! This is handled by the component consumer.' ), // eslint-disable-line no-alert + onUnignoreThreats: () => + // eslint-disable-next-line no-alert + alert( + 'Unignore threat action callback triggered! This is handled by the component consumer.' + ), }; export const FreeResults = args => ; diff --git a/projects/js-packages/components/components/threats-data-views/styles.module.scss b/projects/js-packages/components/components/threats-data-views/styles.module.scss index 21bbc984a3dcf..98789b2f794ec 100644 --- a/projects/js-packages/components/components/threats-data-views/styles.module.scss +++ b/projects/js-packages/components/components/threats-data-views/styles.module.scss @@ -1,33 +1,15 @@ @import '@wordpress/dataviews/build-style/style.css'; -.threat__primary { - display: flex; - flex-direction: column; - gap: 4px; - white-space: initial; -} - -.threat__subtitle { - display: flex; - align-items: center; - gap: 6px; - font-size: 12px; - color: var( --jp-gray-80 ); - margin-bottom: 4px; - - > svg { - color: currentColor; - } -} - .threat__title { color: var( --jp-gray-80 ); font-weight: 510; + white-space: initial; } .threat__description { color: var( --jp-gray-80 ); font-size: 12px; + white-space: initial; } .threat__fixedOn, @@ -39,94 +21,16 @@ color: var( --jp-green-70 ); } - .threat__media { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; - color: black; background-color: #EDFFEE; border-color: #EDFFEE; svg { - fill: currentColor; - } -} - -/** - * Auto-fix status icons - */ - - .threat__fixer { - min-width: 54px; - display: flex; - justify-content: center; -} - - -.fixer-status { - display: flex; - align-items: center; - line-height: 0; - - .icon-spinner { - margin-left: 1px; + fill: black; } - - .icon-info { - margin-left: -3px; - } - - .icon-check { - margin-left: -6px; - } -} - -.icon-check { - fill: var( --jp-green-40 ); -} - -.icon-tooltip { - width: fit-content; -} - -.icon-tooltip__container { - text-align: left; - height: 20px; -} - -.icon-tooltip__icon { - color: var( --jp-red ); -} - -.icon-spinner svg { - margin: 0; -} - -.spinner-spacer { - margin-left: 8px; -} - -.info-spacer { - margin-left: 4px; -} - -.check-spacer { - margin-left: -2px; -} - -.support-link { - color: inherit; - - &:focus, - &:hover { - color: inherit; - box-shadow: none; - } -} - -.toggle-control { - white-space: nowrap; } diff --git a/projects/js-packages/components/index.ts b/projects/js-packages/components/index.ts index 3cfdd7aa00065..eb90df97ad5fe 100644 --- a/projects/js-packages/components/index.ts +++ b/projects/js-packages/components/index.ts @@ -44,6 +44,7 @@ export { default as CopyToClipboard } from './components/copy-to-clipboard'; export * from './components/icons'; export { default as SplitButton } from './components/split-button'; export { default as ThemeProvider } from './components/theme-provider'; +export { default as ThreatFixerButton } from './components/threat-fixer-button'; export { default as ThreatSeverityBadge } from './components/threat-severity-badge'; export { default as ThreatsDataViews } from './components/threats-data-views'; export { default as Text, H2, H3, Title } from './components/text'; diff --git a/projects/js-packages/publicize-components/changelog/update-base-control-bottom-margin-style-deprecation b/projects/js-packages/publicize-components/changelog/update-base-control-bottom-margin-style-deprecation new file mode 100644 index 0000000000000..b15eec1ee9e9a --- /dev/null +++ b/projects/js-packages/publicize-components/changelog/update-base-control-bottom-margin-style-deprecation @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Components: Add __nextHasNoMarginBottom to BaseControl-based components, preventing deprecation notices. diff --git a/projects/js-packages/publicize-components/src/components/connection-management/mark-as-shared.tsx b/projects/js-packages/publicize-components/src/components/connection-management/mark-as-shared.tsx index 913a1c9b2016f..d11f32b3dab76 100644 --- a/projects/js-packages/publicize-components/src/components/connection-management/mark-as-shared.tsx +++ b/projects/js-packages/publicize-components/src/components/connection-management/mark-as-shared.tsx @@ -45,6 +45,7 @@ export function MarkAsShared( { connection }: MarkAsSharedProps ) { onChange={ onChange } disabled={ isUpdating || connection.status === 'broken' } label={ __( 'Mark the connection as shared', 'jetpack' ) } + __nextHasNoMarginBottom={ true } /> ); } diff --git a/projects/js-packages/publicize-components/src/components/message-box-control/index.js b/projects/js-packages/publicize-components/src/components/message-box-control/index.js index 76c62f701fd96..f9be2097578d2 100644 --- a/projects/js-packages/publicize-components/src/components/message-box-control/index.js +++ b/projects/js-packages/publicize-components/src/components/message-box-control/index.js @@ -55,6 +55,7 @@ export default function MessageBoxControl( { _n( '%d character remaining', '%d characters remaining', charactersRemaining, 'jetpack' ), charactersRemaining ) } + __nextHasNoMarginBottom={ true } /> ); } diff --git a/projects/js-packages/publicize-components/src/components/panel/index.jsx b/projects/js-packages/publicize-components/src/components/panel/index.jsx index 710d7031b6216..06166ee4d26f0 100644 --- a/projects/js-packages/publicize-components/src/components/panel/index.jsx +++ b/projects/js-packages/publicize-components/src/components/panel/index.jsx @@ -68,6 +68,7 @@ const PublicizePanel = ( { prePublish, children } ) => { onChange={ togglePublicizeFeature } checked={ isPublicizeEnabled && hasConnections } disabled={ ! hasConnections } + __nextHasNoMarginBottom={ true } /> ) } diff --git a/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js b/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js index fd5f7ec71fd26..6d2f903aeb541 100644 --- a/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js +++ b/projects/js-packages/publicize-components/src/components/social-image-generator/panel/index.js @@ -51,6 +51,7 @@ const SocialImageGeneratorPanel = ( { prePublish = false } ) => { help={ ! isEnabled ? __( 'Social Image is disabled for this post.', 'jetpack' ) : '' } checked={ isEnabled } onChange={ setIsEnabled } + __nextHasNoMarginBottom={ true } /> { isEnabled && ( <> diff --git a/projects/js-packages/publicize-components/src/components/social-image-generator/panel/modal.js b/projects/js-packages/publicize-components/src/components/social-image-generator/panel/modal.js index ed6f8b475f6f0..cc430bb371e05 100644 --- a/projects/js-packages/publicize-components/src/components/social-image-generator/panel/modal.js +++ b/projects/js-packages/publicize-components/src/components/social-image-generator/panel/modal.js @@ -70,6 +70,7 @@ const SocialImageGeneratorSettingsModal = ( { onClose } ) => { { label: __( 'No Image', 'jetpack' ), value: 'none' }, ] } onChange={ setEditedImageType } + __nextHasNoMarginBottom={ true } /> { localImageType === 'custom' && ( @@ -92,6 +93,7 @@ const SocialImageGeneratorSettingsModal = ( { onClose } ) => { 'By default the post title is used for the image. You can use this field to set your own text.', 'jetpack' ) } + __nextHasNoMarginBottom={ true } /> { __( 'Templates', 'jetpack' ) } diff --git a/projects/js-packages/publicize-components/src/components/social-post-modal/preview-section.tsx b/projects/js-packages/publicize-components/src/components/social-post-modal/preview-section.tsx index 3a917dd4882a1..dd81de8c1285e 100644 --- a/projects/js-packages/publicize-components/src/components/social-post-modal/preview-section.tsx +++ b/projects/js-packages/publicize-components/src/components/social-post-modal/preview-section.tsx @@ -98,6 +98,7 @@ export function PreviewSection() { } checked={ isEnabled } onChange={ toggleConnection( tab.connection_id, tab ) } + __nextHasNoMarginBottom={ true } /> ) : null } diff --git a/projects/js-packages/scan/src/constants/index.ts b/projects/js-packages/scan/src/constants/index.ts new file mode 100644 index 0000000000000..bccc025dbe2e9 --- /dev/null +++ b/projects/js-packages/scan/src/constants/index.ts @@ -0,0 +1,4 @@ +export const CONTACT_SUPPORT_URL = 'https://jetpack.com/contact-support/?rel=support'; + +/** Once a fixer has been running for this specified amount of time (in ms), it should be considered "stale". */ +export const FIXER_IS_STALE_THRESHOLD = 1000 * 60 * 60 * 24; // 24 hours diff --git a/projects/js-packages/scan/src/index.ts b/projects/js-packages/scan/src/index.ts index 314a00ec1f4fb..8009266228a9b 100644 --- a/projects/js-packages/scan/src/index.ts +++ b/projects/js-packages/scan/src/index.ts @@ -1 +1,3 @@ export * from './types/index.js'; +export * from './constants/index.js'; +export * from './utils/index.js'; diff --git a/projects/js-packages/scan/src/types/fixers.ts b/projects/js-packages/scan/src/types/fixers.ts new file mode 100644 index 0000000000000..6ba9433122dbb --- /dev/null +++ b/projects/js-packages/scan/src/types/fixers.ts @@ -0,0 +1,17 @@ +export type FixerStatus = 'not_started' | 'in_progress' | 'fixed' | 'not_fixed'; + +/** + * Threat Fix Status + * + * Individual fixer status for a threat. + */ +export type ThreatFixStatusError = { + error: string; +}; + +export type ThreatFixStatusSuccess = { + status: FixerStatus; + last_updated: string; +}; + +export type ThreatFixStatus = ThreatFixStatusError | ThreatFixStatusSuccess; diff --git a/projects/js-packages/scan/src/types/threats.ts b/projects/js-packages/scan/src/types/threats.ts new file mode 100644 index 0000000000000..2c75f88bb80fa --- /dev/null +++ b/projects/js-packages/scan/src/types/threats.ts @@ -0,0 +1,72 @@ +import { ThreatFixStatus } from './fixers.js'; + +export type ThreatStatus = 'fixed' | 'ignored' | 'current'; + +export type ThreatFixType = 'replace' | 'delete' | 'update' | string; + +export type Threat = { + /** The threat's unique ID. */ + id: string | number; + + /** The threat's signature. */ + signature: string; + + /** The threat's title. */ + title: string; + + /** The threat's description. */ + description: string; + + /** The threat's current status. */ + status: ThreatStatus; + + /** The threat's severity level (0-10). */ + severity: number; + + /** The date the threat was first detected on the site, in YYYY-MM-DDTHH:MM:SS.000Z format. */ + firstDetected: string; + + /** The version the threat is fixed in. */ + fixedIn?: string | null; + + /** The date the threat was fixed, in YYYY-MM-DDTHH:MM:SS.000Z format. */ + fixedOn?: string | null; + + /** The fixable details. */ + fixable: + | { + fixer: ThreatFixType; + target?: string | null; + extensionStatus?: string | null; + } + | false; + + /** The fixer status. */ + fixer: ThreatFixStatus; + + /** The threat's source. */ + source?: string; + + /** The threat's context. */ + context?: Record< string, unknown > | null; + + /** The name of the affected file. */ + filename: string | null; + + /** The rows affected by the database threat. */ + rows?: unknown; + + /** The table name of the database threat. */ + table?: string; + + /** The diff showing the threat's modified file contents. */ + diff?: string; + + /** The affected extension. */ + extension?: { + slug: string; + name: string; + version: string; + type: 'plugin' | 'theme' | 'core'; + }; +}; diff --git a/projects/js-packages/scan/src/utils/index.ts b/projects/js-packages/scan/src/utils/index.ts new file mode 100644 index 0000000000000..2827c94fe51be --- /dev/null +++ b/projects/js-packages/scan/src/utils/index.ts @@ -0,0 +1,34 @@ +import { FIXER_IS_STALE_THRESHOLD } from '../constants/index.js'; +import { ThreatFixStatus } from '../types/fixers.js'; +import { Threat } from '../types/threats.js'; + +export const getThreatType = ( threat: Threat ) => { + if ( threat.signature === 'Vulnerable.WP.Core' ) { + return 'core'; + } + if ( threat.extension ) { + return threat.extension.type; + } + if ( threat.filename ) { + return 'file'; + } + if ( threat.table ) { + return 'database'; + } + + return null; +}; + +export const fixerTimestampIsStale = ( lastUpdatedTimestamp: string ) => { + const now = new Date(); + const lastUpdated = new Date( lastUpdatedTimestamp ); + return now.getTime() - lastUpdated.getTime() >= FIXER_IS_STALE_THRESHOLD; +}; + +export const fixerStatusIsStale = ( fixerStatus: ThreatFixStatus ) => { + return ( + 'status' in fixerStatus && + fixerStatus.status === 'in_progress' && + fixerTimestampIsStale( fixerStatus.last_updated ) + ); +}; diff --git a/projects/packages/forms/changelog/update-base-control-bottom-margin-style-deprecation b/projects/packages/forms/changelog/update-base-control-bottom-margin-style-deprecation new file mode 100644 index 0000000000000..b15eec1ee9e9a --- /dev/null +++ b/projects/packages/forms/changelog/update-base-control-bottom-margin-style-deprecation @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Components: Add __nextHasNoMarginBottom to BaseControl-based components, preventing deprecation notices. diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-crm-integration/jetpack-crm-integration-settings-plugin-state.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-crm-integration/jetpack-crm-integration-settings-plugin-state.js index a5812bfc8c0d1..f12a4cf58c657 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-crm-integration/jetpack-crm-integration-settings-plugin-state.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-crm-integration/jetpack-crm-integration-settings-plugin-state.js @@ -126,6 +126,7 @@ const CRMPluginIsActive = ( { crmData, setCRMData, jetpackCRM, setAttributes } ) checked={ jetpackCRM } onChange={ value => setAttributes( { jetpackCRM: value } ) } help={ __( 'Store contact form submissions in your CRM.', 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } /> ); }; diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-crm-integration/jetpack-crm-integration-settings.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-crm-integration/jetpack-crm-integration-settings.js index b56a398b80bb9..2ecbb722c1a9a 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-crm-integration/jetpack-crm-integration-settings.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-crm-integration/jetpack-crm-integration-settings.js @@ -65,7 +65,7 @@ const CRMPluginData = ( { jetpackCRM, setAttributes } ) => { const CRMIntegrationSettings = ( { jetpackCRM, setAttributes } ) => { return ( - + ); diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-email-connection-settings.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-email-connection-settings.js index c7fb6fd26529c..77579069223b4 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-email-connection-settings.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-email-connection-settings.js @@ -109,6 +109,7 @@ const JetpackEmailConnectionSettings = ( { 'You can enter multiple email addresses separated by commas.', 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } /> @@ -120,6 +121,7 @@ const JetpackEmailConnectionSettings = ( { value={ emailSubject } placeholder={ __( 'Enter a subject', 'jetpack-forms' ) } onChange={ newSubject => setAttributes( { subject: newSubject } ) } + __nextHasNoMarginBottom={ true } /> ); diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-checkbox.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-checkbox.js index 06a0782c60db0..d6d18dc61379f 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-checkbox.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-checkbox.js @@ -63,6 +63,7 @@ function JetpackFieldCheckbox( props ) { label={ __( 'Checked by default', 'jetpack-forms' ) } checked={ defaultValue } onChange={ value => setAttributes( { defaultValue: value ? 'true' : '' } ) } + __nextHasNoMarginBottom={ true } /> @@ -77,6 +78,7 @@ function JetpackFieldCheckbox( props ) { checked={ required } onChange={ value => setAttributes( { required: value } ) } help={ __( 'You can edit the "required" label in the editor', 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } /> @@ -85,6 +87,7 @@ function JetpackFieldCheckbox( props ) { checked={ attributes.shareFieldAttributes } onChange={ value => setAttributes( { shareFieldAttributes: value } ) } help={ __( 'Deactivate for individual styling of this block', 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } /> setAttributes( { shareFieldAttributes: value } ) } help={ __( 'Deactivate for individual styling of this block', 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } /> - + setAttributes( { consentType: value } ) } + __nextHasNoMarginBottom={ true } /> diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-controls.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-controls.js index d9dcda1926c5c..23e65e7e64f51 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-controls.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-controls.js @@ -113,6 +113,7 @@ const JetpackFieldControls = ( { checked={ required } onChange={ value => setAttributes( { required: value } ) } help={ __( 'You can edit the "required" label in the editor', 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } />, ! hidePlaceholder && ( ), , @@ -133,6 +135,7 @@ const JetpackFieldControls = ( { checked={ attributes.shareFieldAttributes } onChange={ value => setAttributes( { shareFieldAttributes: value } ) } help={ __( 'Deactivate for individual styling of this block', 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } />, ]; @@ -203,6 +206,7 @@ const JetpackFieldControls = ( { onChange={ setNumberAttribute( 'buttonBorderWidth' ) } min={ 0 } max={ 100 } + __nextHasNoMarginBottom={ true } /> ) } @@ -223,6 +228,7 @@ const JetpackFieldControls = ( { onChange={ setNumberAttribute( 'borderWidth' ) } min={ 0 } max={ 100 } + __nextHasNoMarginBottom={ true } /> ) } @@ -266,6 +273,7 @@ const JetpackFieldControls = ( { "Customize the input's name/ID. Only alphanumeric, dash and underscore characters are allowed", 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } /> diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-datepicker.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-datepicker.js index ab41c2a6d3977..47188aa488e71 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-datepicker.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-datepicker.js @@ -97,6 +97,7 @@ const JetpackDatePicker = props => { 'Select the format in which the date will be displayed.', 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } /> ), }, diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-width.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-width.js index c6c3eaf69fc19..e56af4a8f2234 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-width.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-field-width.js @@ -9,6 +9,7 @@ export default function JetpackFieldWidth( { setAttributes, width } ) { 'jetpack-forms' ) } className="jetpack-field-label__width" + __nextHasNoMarginBottom={ true } > { __( 'Field Width', 'jetpack-forms' ) } diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-newsletter-integration-settings.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-newsletter-integration-settings.js index 56091b44ab7c4..651d52c49a37e 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-newsletter-integration-settings.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-newsletter-integration-settings.js @@ -4,7 +4,7 @@ import CreativeMailPlugin from './jetpack-newsletter-integration-settings-creati const NewsletterIntegrationSettings = () => { return ( - + diff --git a/projects/packages/forms/src/blocks/contact-form/components/jetpack-salesforce-lead-form/jetpack-salesforce-lead-form-settings.js b/projects/packages/forms/src/blocks/contact-form/components/jetpack-salesforce-lead-form/jetpack-salesforce-lead-form-settings.js index 989d8aa665215..343b9ec1fce2b 100644 --- a/projects/packages/forms/src/blocks/contact-form/components/jetpack-salesforce-lead-form/jetpack-salesforce-lead-form-settings.js +++ b/projects/packages/forms/src/blocks/contact-form/components/jetpack-salesforce-lead-form/jetpack-salesforce-lead-form-settings.js @@ -129,7 +129,7 @@ export default ( { salesforceData, setAttributes, instanceId } ) => { return ( - + { onBlur={ onBlurOrgIdField } onChange={ setOrganizationId } help={ __( 'Enter the Salesforce organization ID to send Leads to.', 'jetpack-forms' ) } + __nextHasNoMarginBottom={ true } /> { organizationIdError && ( diff --git a/projects/packages/forms/src/blocks/contact-form/edit.js b/projects/packages/forms/src/blocks/contact-form/edit.js index 80d281df06f45..1dc222dacf1da 100644 --- a/projects/packages/forms/src/blocks/contact-form/edit.js +++ b/projects/packages/forms/src/blocks/contact-form/edit.js @@ -174,6 +174,7 @@ export const JetpackContactFormEdit = forwardRef( { label: __( 'Redirect to another webpage', 'jetpack-forms' ), value: 'redirect' }, ] } onChange={ newMessage => setAttributes( { customThankyou: newMessage } ) } + __nextHasNoMarginBottom={ true } /> { 'redirect' !== customThankyou && ( @@ -182,6 +183,7 @@ export const JetpackContactFormEdit = forwardRef( value={ customThankyouHeading } placeholder={ __( 'Your message has been sent', 'jetpack-forms' ) } onChange={ newHeading => setAttributes( { customThankyouHeading: newHeading } ) } + __nextHasNoMarginBottom={ true } /> ) } @@ -191,6 +193,7 @@ export const JetpackContactFormEdit = forwardRef( value={ customThankyouMessage } placeholder={ __( 'Thank you for your submission!', 'jetpack-forms' ) } onChange={ newMessage => setAttributes( { customThankyouMessage: newMessage } ) } + __nextHasNoMarginBottom={ true } /> ) } @@ -198,6 +201,7 @@ export const JetpackContactFormEdit = forwardRef( ) ) }
diff --git a/projects/packages/my-jetpack/changelog/update-base-control-bottom-margin-style-deprecation b/projects/packages/my-jetpack/changelog/update-base-control-bottom-margin-style-deprecation new file mode 100644 index 0000000000000..b15eec1ee9e9a --- /dev/null +++ b/projects/packages/my-jetpack/changelog/update-base-control-bottom-margin-style-deprecation @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Components: Add __nextHasNoMarginBottom to BaseControl-based components, preventing deprecation notices. diff --git a/projects/packages/search/changelog/update-base-control-bottom-margin-style-deprecation b/projects/packages/search/changelog/update-base-control-bottom-margin-style-deprecation new file mode 100644 index 0000000000000..b15eec1ee9e9a --- /dev/null +++ b/projects/packages/search/changelog/update-base-control-bottom-margin-style-deprecation @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Components: Add __nextHasNoMarginBottom to BaseControl-based components, preventing deprecation notices. diff --git a/projects/packages/search/src/customberg/components/sidebar/excluded-post-types-control.jsx b/projects/packages/search/src/customberg/components/sidebar/excluded-post-types-control.jsx index 6e52bac68f12d..5d9d660b4ffcb 100644 --- a/projects/packages/search/src/customberg/components/sidebar/excluded-post-types-control.jsx +++ b/projects/packages/search/src/customberg/components/sidebar/excluded-post-types-control.jsx @@ -58,6 +58,7 @@ export default function ExcludedPostTypesControl( { label={ VALID_POST_TYPES[ type ].name } onChange={ changeHandler( type ) } value={ type } + __nextHasNoMarginBottom={ true } /> ) ) }
diff --git a/projects/packages/search/src/customberg/components/sidebar/sidebar-options.jsx b/projects/packages/search/src/customberg/components/sidebar/sidebar-options.jsx index 48c9c819f50f2..22dee07d2f24d 100644 --- a/projects/packages/search/src/customberg/components/sidebar/sidebar-options.jsx +++ b/projects/packages/search/src/customberg/components/sidebar/sidebar-options.jsx @@ -104,6 +104,7 @@ export default function SidebarOptions() { value={ sort } options={ sortOptions } onChange={ setSort } + __nextHasNoMarginBottom={ true } /> { 'expanded' === resultFormat && ( ) } { ! isFreePlan && ( @@ -171,6 +177,7 @@ export default function SidebarOptions() { disabled={ isDisabled } label={ __( 'Show "Powered by Jetpack"', 'jetpack-search-pkg' ) } onChange={ setShowLogo } + __nextHasNoMarginBottom={ true } /> ) }
diff --git a/projects/packages/videopress/changelog/update-base-control-bottom-margin-style-deprecation b/projects/packages/videopress/changelog/update-base-control-bottom-margin-style-deprecation new file mode 100644 index 0000000000000..b15eec1ee9e9a --- /dev/null +++ b/projects/packages/videopress/changelog/update-base-control-bottom-margin-style-deprecation @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Components: Add __nextHasNoMarginBottom to BaseControl-based components, preventing deprecation notices. diff --git a/projects/packages/videopress/src/client/admin/components/edit-video-details/index.tsx b/projects/packages/videopress/src/client/admin/components/edit-video-details/index.tsx index 61982c0e5930a..a0208bdd8241d 100644 --- a/projects/packages/videopress/src/client/admin/components/edit-video-details/index.tsx +++ b/projects/packages/videopress/src/client/admin/components/edit-video-details/index.tsx @@ -371,6 +371,7 @@ const EditVideoDetails = () => { value: VIDEO_PRIVACY_LEVEL_PRIVATE, }, ] } + __nextHasNoMarginBottom={ true } /> ) } { isFetchingData ? ( @@ -388,6 +389,7 @@ const EditVideoDetails = () => { 'jetpack-videopress-pkg' ) } onChange={ value => setDisplayEmbed( value ? 1 : 0 ) } + __nextHasNoMarginBottom={ true } /> ) } @@ -406,6 +408,7 @@ const EditVideoDetails = () => { 'jetpack-videopress-pkg' ) } onChange={ value => setAllowDownload( value ? 1 : 0 ) } + __nextHasNoMarginBottom={ true } /> ) } diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/components/color-panel/index.native.js b/projects/packages/videopress/src/client/block-editor/blocks/video/components/color-panel/index.native.js index 1942e26468ef4..34d26df869c70 100644 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/components/color-panel/index.native.js +++ b/projects/packages/videopress/src/client/block-editor/blocks/video/components/color-panel/index.native.js @@ -86,6 +86,7 @@ export default function ColorPanel( { attributes, setAttributes } ) { setAttributes( { useAverageColor: newUseAverageColor } ) } checked={ useAverageColor } + __nextHasNoMarginBottom={ true } /> diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/components/color-panel/index.tsx b/projects/packages/videopress/src/client/block-editor/blocks/video/components/color-panel/index.tsx index 3f7e6474e8fb7..42cb91a4c3753 100644 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/components/color-panel/index.tsx +++ b/projects/packages/videopress/src/client/block-editor/blocks/video/components/color-panel/index.tsx @@ -90,6 +90,7 @@ export default function ColorPanel( { clientId, attributes, setAttributes }: Vid } onChange={ newUseAverageColor => setAttributes( { useAverageColor: newUseAverageColor } ) } checked={ useAverageColor } + __nextHasNoMarginBottom={ true } /> { ! useAverageColor && ( diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/components/details-panel/index.native.js b/projects/packages/videopress/src/client/block-editor/blocks/video/components/details-panel/index.native.js index 2d68c58936ee0..98344c3ed3191 100644 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/components/details-panel/index.native.js +++ b/projects/packages/videopress/src/client/block-editor/blocks/video/components/details-panel/index.native.js @@ -34,6 +34,7 @@ export default function DetailsPanel( { attributes, setAttributes, videoBelongTo placeholder={ titlePlaceholder } label={ __( 'Title', 'jetpack-videopress-pkg' ) } disabled={ ! videoBelongToSite } + __nextHasNoMarginBottom={ true } /> setAttributes( { title: value } ) } disabled={ isRequestingVideoData || !! updateError || ! videoBelongToSite } + __nextHasNoMarginBottom={ true } /> { ! hasUploadedChapters && hasIncompleteChapters && ( diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/components/playback-panel/index.native.js b/projects/packages/videopress/src/client/block-editor/blocks/video/components/playback-panel/index.native.js index bd8f632905fda..7d6d3fdcdc817 100644 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/components/playback-panel/index.native.js +++ b/projects/packages/videopress/src/client/block-editor/blocks/video/components/playback-panel/index.native.js @@ -107,6 +107,7 @@ export default function PlaybackPanel( { attributes, setAttributes } ) { checked={ autoplay && ! isPreviewOnHoverEnabled } disabled={ isPreviewOnHoverEnabled } help={ } + __nextHasNoMarginBottom={ true } /> diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/components/playback-panel/index.tsx b/projects/packages/videopress/src/client/block-editor/blocks/video/components/playback-panel/index.tsx index b2636a4fd1f1c..5718058ae43c7 100644 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/components/playback-panel/index.tsx +++ b/projects/packages/videopress/src/client/block-editor/blocks/video/components/playback-panel/index.tsx @@ -77,6 +77,7 @@ export default function PlaybackPanel( { attributes, setAttributes }: VideoContr checked={ autoplay && ! isPreviewOnHoverEnabled } disabled={ isPreviewOnHoverEnabled } help={ } + __nextHasNoMarginBottom={ true } /> { createInterpolateElement( diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/components/poster-panel/index.tsx b/projects/packages/videopress/src/client/block-editor/blocks/video/components/poster-panel/index.tsx index 48a9537c51d09..0b1b003a49f34 100644 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/components/poster-panel/index.tsx +++ b/projects/packages/videopress/src/client/block-editor/blocks/video/components/poster-panel/index.tsx @@ -395,6 +395,7 @@ export function VideoHoverPreviewControl( { checked={ previewOnHover } onChange={ onPreviewOnHoverChange } disabled={ ! previewOnHover && disabled } + __nextHasNoMarginBottom={ true } /> { previewOnHover && ( @@ -551,6 +552,7 @@ export default function PosterPanel( { checked={ pickPosterFromFrame && videoBelongToSite } onChange={ switchPosterSource } disabled={ ! videoBelongToSite } + __nextHasNoMarginBottom={ true } />
{ ! videoBelongToSite && } diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/components/privacy-and-rating-panel/privacy-and-rating-settings.tsx b/projects/packages/videopress/src/client/block-editor/blocks/video/components/privacy-and-rating-panel/privacy-and-rating-settings.tsx index 1bff6857e5e32..4660e0c69c38c 100644 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/components/privacy-and-rating-panel/privacy-and-rating-settings.tsx +++ b/projects/packages/videopress/src/client/block-editor/blocks/video/components/privacy-and-rating-panel/privacy-and-rating-settings.tsx @@ -84,6 +84,7 @@ export default function PrivacyAndRatingSettings( { setAttributes( { rating: value } ); } } disabled={ ! videoBelongToSite } + __nextHasNoMarginBottom={ true } /> ); diff --git a/projects/packages/videopress/src/client/block-editor/blocks/video/components/tracks-control/track-form.tsx b/projects/packages/videopress/src/client/block-editor/blocks/video/components/tracks-control/track-form.tsx index 0a5491bc53cc9..fda27567b1879 100644 --- a/projects/packages/videopress/src/client/block-editor/blocks/video/components/tracks-control/track-form.tsx +++ b/projects/packages/videopress/src/client/block-editor/blocks/video/components/tracks-control/track-form.tsx @@ -172,6 +172,7 @@ export default function TrackForm( { value={ track.label } help={ __( 'Title of track', 'jetpack-videopress-pkg' ) } disabled={ isSavingTrack } + __nextHasNoMarginBottom={ true } />
updateTrack( 'kind', newKind ) } disabled={ isSavingTrack } + __nextHasNoMarginBottom={ true } /> { error && ( @@ -210,6 +213,7 @@ export default function TrackForm( { label={ __( 'Track exists. Replace?', 'jetpack-videopress-pkg' ) } checked={ replaceTrack } onChange={ setReplaceTrack } + __nextHasNoMarginBottom={ true } /> ) }