Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jetpack Checklist: Implement Jetpack Backups task #32152

Merged
merged 13 commits into from
Apr 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/layout/guided-tours/all-tours.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { ChecklistSiteIconTour } from 'layout/guided-tours/tours/checklist-site-
import { ChecklistSiteTaglineTour } from 'layout/guided-tours/tours/checklist-site-tagline-tour';
import { ChecklistSiteTitleTour } from 'layout/guided-tours/tours/checklist-site-title-tour';
import { ChecklistUserAvatarTour } from 'layout/guided-tours/tours/checklist-user-avatar-tour';
import { JetpackBackupsRewindTour } from 'layout/guided-tours/tours/jetpack-backups-rewind-tour';
import { JetpackBasicTour } from 'layout/guided-tours/tours/jetpack-basic-tour';
import { JetpackMonitoringTour } from 'layout/guided-tours/tours/jetpack-monitoring-tour';
import { JetpackPluginUpdatesTour } from 'layout/guided-tours/tours/jetpack-plugin-updates-tour';
Expand All @@ -40,6 +41,7 @@ export default combineTours( {
checklistSiteTitle: ChecklistSiteTitleTour,
checklistUserAvatar: ChecklistUserAvatarTour,
jetpack: JetpackBasicTour,
jetpackBackupsRewind: JetpackBackupsRewindTour,
jetpackMonitoring: JetpackMonitoringTour,
jetpackPluginUpdates: JetpackPluginUpdatesTour,
jetpackSignIn: JetpackSignInTour,
Expand Down
6 changes: 4 additions & 2 deletions client/layout/guided-tours/config-elements/site-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ class SiteLink extends Component {
static propTypes = {
href: PropTypes.string,
isButton: PropTypes.bool,
isPrimaryButton: PropTypes.bool,
};

static defaultProps = {
isButton: false,
isPrimaryButton: true,
};

static contextTypes = contextTypes;
Expand All @@ -35,12 +37,12 @@ class SiteLink extends Component {
};

render() {
const { children, href, siteSlug, isButton } = this.props;
const { children, href, siteSlug, isButton, isPrimaryButton } = this.props;
const siteHref = href.replace( ':site', siteSlug );

if ( isButton ) {
return (
<Button primary onClick={ this.onClick } href={ siteHref }>
<Button primary={ isPrimaryButton } onClick={ this.onClick } href={ siteHref }>
{ children }
</Button>
);
Expand Down
7 changes: 6 additions & 1 deletion client/layout/guided-tours/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,16 @@
.button {
min-width: 48%;
}
.guided-tours__quit-button {
.guided-tours__quit-button,
a.button {
color: var( --color-neutral-100 );
background: none;
border: 1px rgba( var( --color-white-rgb ), 0.2 ) solid;
}

a.button {
text-decoration: none;
}
}

a.config-elements__text-link,
Expand Down
162 changes: 162 additions & 0 deletions client/layout/guided-tours/tours/jetpack-backups-rewind-tour/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* External dependencies
*/
import React, { Fragment } from 'react';
import Gridicon from 'gridicons';
import { connect } from 'react-redux';

/**
* Internal dependencies
*/
import getJetpackCredentialsUpdateStatus from 'state/selectors/get-jetpack-credentials-update-status';
import getRewindState from 'state/selectors/get-rewind-state';
import meta from './meta';
import {
ButtonRow,
ConditionalBlock,
Continue,
makeTour,
Quit,
SiteLink,
Step,
Tour,
} from 'layout/guided-tours/config-elements';
import { not } from 'layout/guided-tours/utils';
import { getSelectedSiteId } from 'state/ui/selectors';

function whenWeCanAutoconfigure( state ) {
const siteId = getSelectedSiteId( state );
const { canAutoconfigure, credentials = [] } = getRewindState( state, siteId );

return canAutoconfigure || credentials.some( c => c.type === 'auto' );
}

const JetpackBackupsRewindTourButtons = ( { backText, translate } ) => (
<Fragment>
<SiteLink isButton isPrimaryButton={ false } href="/plans/my-plan/:site">
{ backText || translate( 'Return to the checklist' ) }
</SiteLink>
<Quit>{ translate( 'No, thanks.' ) }</Quit>
</Fragment>
);

const ContinueToLastStep = ( { siteHasCredentials } ) => (
<Continue
target=".rewind-credentials-form .is-primary"
step="finish"
when={ () => siteHasCredentials }
click
hidden
/>
);
const ConnectedContinueToLastStep = connect( state => ( {
siteHasCredentials:
getJetpackCredentialsUpdateStatus( state, getSelectedSiteId( state ) ) === 'success',
} ) )( ContinueToLastStep );

/* eslint-disable wpcalypso/jsx-classname-namespace */
export const JetpackBackupsRewindTour = makeTour(
<Tour { ...meta }>
<Step name="init" target=".credentials-setup-flow" placement="beside" arrow="right-top">
{ ( { translate } ) => (
<Fragment>
<p>
{ translate(
"Let's enable Jetpack backups and restores " +
'by adding access credentials for your site.'
) }
</p>
<ButtonRow>
<Continue
target=".credentials-setup-flow__setup-start"
step="autoconfigureOrConfirm"
click
hidden
/>
<JetpackBackupsRewindTourButtons translate={ translate } />
</ButtonRow>
</Fragment>
) }
</Step>

<Step
name="autoconfigureOrConfirm"
target=".credentials-setup-flow__tos .is-primary"
placement="below"
arrow="top-left"
>
{ ( { translate } ) => (
<Fragment>
<ConditionalBlock when={ whenWeCanAutoconfigure }>
<p>
{ translate(
"You can click this button to provide WordPress.com with access to your host's server."
) }
</p>
<ButtonRow>
<Continue
target=".credentials-setup-flow__tos-buttons .is-primary"
step="finish"
click
hidden
/>
<JetpackBackupsRewindTourButtons translate={ translate } />
</ButtonRow>
</ConditionalBlock>
<ConditionalBlock when={ not( whenWeCanAutoconfigure ) }>
<p>
{ translate(
'You can click the button in order to agree and continue to the credentials form.'
) }
</p>
<ButtonRow>
<Continue
target=".credentials-setup-flow__tos-buttons .is-primary"
step="credentials"
click
hidden
/>
<JetpackBackupsRewindTourButtons translate={ translate } />
</ButtonRow>
</ConditionalBlock>
</Fragment>
) }
</Step>

<Step
name="credentials"
target=".rewind-credentials-form"
placement="beside"
arrow="right-top"
style={ {
display: 'none',
} }
>
{ () => <ConnectedContinueToLastStep /> }
</Step>

<Step name="finish" target=".credentials-configured" placement="right">
{ ( { translate } ) => (
<Fragment>
<h1 className="tours__title">
<span className="tours__completed-icon-wrapper">
<Gridicon icon="checkmark" className="tours__completed-icon" />
</span>
{ translate( 'Excellent, you’re done!' ) }
</h1>
<p>
{ translate(
'Jetpack Backups has been enabled. Would you like to continue setting up the security essential features for your site?'
) }
</p>
<ButtonRow>
<JetpackBackupsRewindTourButtons
translate={ translate }
backText={ translate( "Yes, let's do it." ) }
/>
</ButtonRow>
</Fragment>
) }
</Step>
</Tour>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
name: 'jetpackBackupsRewind',
version: '20190409',
};
26 changes: 16 additions & 10 deletions client/my-sites/plans/current-plan/jetpack-checklist/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,6 @@ export function getJetpackChecklistTaskDuration( minutes ) {
}

export const JETPACK_CHECKLIST_TASKS = {
jetpack_backups: {
title: translate( 'Backups & Scanning' ),
description: translate(
"Connect your site's server to Jetpack to perform backups, rewinds, and security scans."
),
completedButtonText: translate( 'Change', { context: 'verb' } ),
completedTitle: translate( 'You turned on backups and scanning.' ),
getUrl: siteSlug => `/activity-log/${ siteSlug }`,
duration: getJetpackChecklistTaskDuration( 2 ),
},
jetpack_monitor: {
title: translate( 'Downtime Monitoring' ),
description: translate(
Expand Down Expand Up @@ -58,3 +48,19 @@ export const JETPACK_CHECKLIST_TASKS = {
tourId: 'jetpackSignIn',
},
};

export const JETPACK_CHECKLIST_TASK_BACKUPS_REWIND = {
title: translate( 'Backup and Scan' ),
description: translate(
"Connect your site's server to Jetpack to perform backups, restores, and security scans."
),
completedButtonText: translate( 'Change', { context: 'verb' } ),
completedTitle: translate( 'You turned on Backup and Scan.' ),
getUrl: siteSlug => `/settings/security/${ siteSlug }`,
duration: getJetpackChecklistTaskDuration( 3 ),
};

export const JETPACK_CHECKLIST_TASK_BACKUPS_VAULTPRESS = {
title: translate( "We're automatically turning on Backup and Scan." ),
completedTitle: translate( "We've automatically turned on Backup and Scan." ),
};
42 changes: 36 additions & 6 deletions client/my-sites/plans/current-plan/jetpack-checklist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ import { localize } from 'i18n-calypso';
import { Checklist, Task } from 'components/checklist';
import getJetpackProductInstallStatus from 'state/selectors/get-jetpack-product-install-status';
import getSiteChecklist from 'state/selectors/get-site-checklist';
import getRewindState from 'state/selectors/get-rewind-state';
import isSiteOnPaidPlan from 'state/selectors/is-site-on-paid-plan';
import JetpackChecklistHeader from './header';
import QueryJetpackProductInstallStatus from 'components/data/query-jetpack-product-install-status';
import QueryRewindState from 'components/data/query-rewind-state';
import QuerySiteChecklist from 'components/data/query-site-checklist';
import { getSelectedSiteId } from 'state/ui/selectors';
import { getSiteSlug } from 'state/sites/selectors';
import { isDesktop } from 'lib/viewport';
import { JETPACK_CHECKLIST_TASKS } from './constants';
import {
JETPACK_CHECKLIST_TASKS,
JETPACK_CHECKLIST_TASK_BACKUPS_REWIND,
JETPACK_CHECKLIST_TASK_BACKUPS_VAULTPRESS,
} from './constants';
import { recordTracksEvent } from 'state/analytics/actions';
import { requestGuidedTour } from 'state/ui/guided-tours/actions';

Expand Down Expand Up @@ -61,16 +67,22 @@ class JetpackChecklist extends PureComponent {
akismetFinished,
isPaidPlan,
productInstallStatus,
rewindState,
siteId,
siteSlug,
taskStatuses,
translate,
vaultpressFinished,
} = this.props;
const isRewindActive = rewindState === 'active';
const isRewindAvailable = rewindState !== 'uninitialized' && rewindState !== 'unavailable';
const isRewindUnAvailable = rewindState === 'unavailable';

return (
<Fragment>
{ siteId && <QuerySiteChecklist siteId={ siteId } /> }
{ isPaidPlan && <QueryJetpackProductInstallStatus siteId={ siteId } /> }
{ isPaidPlan && <QueryRewindState siteId={ siteId } /> }

<JetpackChecklistHeader />

Expand All @@ -84,6 +96,24 @@ class JetpackChecklist extends PureComponent {
"We've automatically protected you from brute force login attacks."
) }
/>
{ isPaidPlan && isRewindAvailable && (
<Task
{ ...JETPACK_CHECKLIST_TASK_BACKUPS_REWIND }
onClick={ this.handleTaskStart( {
taskId: 'jetpack_backups',
url: JETPACK_CHECKLIST_TASK_BACKUPS_REWIND.getUrl( siteSlug ),
tourId: isRewindActive ? undefined : 'jetpackBackupsRewind',
} ) }
completed={ isRewindActive }
/>
) }
{ isPaidPlan && isRewindUnAvailable && productInstallStatus && (
<Task
{ ...JETPACK_CHECKLIST_TASK_BACKUPS_VAULTPRESS }
completed={ vaultpressFinished }
inProgress={ ! vaultpressFinished }
/>
) }
{ isPaidPlan && productInstallStatus && (
<Task
title={ translate( "We're automatically turning on spam filtering." ) }
Expand All @@ -96,11 +126,7 @@ class JetpackChecklist extends PureComponent {
const task = JETPACK_CHECKLIST_TASKS[ taskId ];

if ( ! task ) {
// UI does not support this task yet.
recordTracksEvent( 'calypso_jetpack_checklist_unsupported_task', {
task_id: taskId,
site_id: siteId,
} );
// UI does not support this task.
return;
}

Expand Down Expand Up @@ -131,10 +157,14 @@ export default connect(
state => {
const siteId = getSelectedSiteId( state );
const productInstallStatus = getJetpackProductInstallStatus( state, siteId );
const rewindState = get( getRewindState( state, siteId ), 'state', 'uninitialized' );

return {
akismetFinished: productInstallStatus && productInstallStatus.akismet_status === 'installed',
vaultpressFinished:
productInstallStatus && productInstallStatus.vaultpress_status === 'installed',
isPaidPlan: isSiteOnPaidPlan( state, siteId ),
rewindState,
productInstallStatus,
siteId,
siteSlug: getSiteSlug( state, siteId ),
Expand Down