diff --git a/.gitignore b/.gitignore index 04a32c2e9f169f..143a33a7712e80 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,8 @@ env-config.sh /public/style*.css.map /public/editor*.css /public/directly*.css +/public/jetpack*.css +/public/jetpack*.css.map /public/images/flags/*.svg /public/images/flags/*.png /server/devdocs/search-index.js diff --git a/assets/stylesheets/_wpadmin-normalize.scss b/assets/stylesheets/_wpadmin-normalize.scss new file mode 100644 index 00000000000000..3a93d498407fe1 --- /dev/null +++ b/assets/stylesheets/_wpadmin-normalize.scss @@ -0,0 +1,6 @@ +.layout__content { + padding: 0; +} +.button { + height: auto; +} diff --git a/assets/stylesheets/jetpack.scss b/assets/stylesheets/jetpack.scss new file mode 100644 index 00000000000000..2a9b117f5e6321 --- /dev/null +++ b/assets/stylesheets/jetpack.scss @@ -0,0 +1,64 @@ +// namespaceing breaks nested & selectors +// For example, in #wpcom wrapper: +// .foldable-card__content { +// display: none; +// .foldable-card.is-expanded & { +// display: block; +// } +// } +// +// produces +// +// #wpcom .foldable-card__content { +// display: none; +// } +// |------this is a problem +// vvvvvv +// .foldable-card.is-expanded #wpcom .foldable-card__content { +// display: block; +// } +// +// #wpcom { + @import 'shared/normalize'; + + // External Dependencies + @import 'vendor'; + + // Shared + @import 'shared/reset'; // css reset before the rest of the styles are defined + @import 'shared/functions'; // functions that we've used from Compass, ported over + @import 'shared/functions/functions'; // sass functions for z-index, etc. + @import 'shared/colors'; // import all of our wpcom colors + @import 'shared/utilities'; // Helper classes + @import 'shared/typography'; // all the typographic rules, variables, etc. + @import 'shared/mixins/mixins'; // sass mixins for gradients, bordius radii, etc. + @import 'shared/extends'; // sass extends for commonly used styles + @import 'shared/animation'; // all UI animation + @import 'shared/forms'; // form styling + @import 'shared/dropdowns'; // dropdown styling + @import 'shared/livechat'; // styles for the popup livechat box + @import 'shared/welcome'; // welcome messages + @import 'shared/infinite-scroll-end'; // Last page marker once infinite scroll has reached end + + // Main + @import 'main'; // global layout and responsive styles + + // Sections + @import 'sections/updated-confirmation'; // confirmation boxes for posts/pages + @import 'sections/notifications'; // notifications styles + @import 'sections/domain-search'; // Domain Search styles + + // Components + @import 'components'; + + // Extensions + @import 'extensions/woocommerce/style'; + @import 'extensions/wp-super-cache/style'; + + // Devdocs + @import 'devdocs/design/style'; + @import 'devdocs/design/syntax'; + + // WP Admin Environment + @import 'wpadmin-normalize'; +// } diff --git a/assets/stylesheets/shared/_normalize.scss b/assets/stylesheets/shared/_normalize.scss new file mode 100644 index 00000000000000..fa4e73dd418b8a --- /dev/null +++ b/assets/stylesheets/shared/_normalize.scss @@ -0,0 +1,447 @@ +/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers (opinionated). + */ + +body { + margin: 0; +} + +/** + * Add the correct display in IE 9-. + */ + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8. + */ + +figure { + margin: 1em 40px; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ +} + +/** + * 1. Remove the bottom border in Chrome 57- and Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. + */ + +mark { + background-color: #ff0; + color: #000; +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} diff --git a/bin/copy-files.sh b/bin/copy-files.sh new file mode 100755 index 00000000000000..83f3aaef68f5c8 --- /dev/null +++ b/bin/copy-files.sh @@ -0,0 +1,10 @@ +JETPACK_DIR="/home/javi/work/jetpackSandbox/wp-content/plugins/jetpack-ui" + +find ./public -iregex ".*manifest.*\.js" ! -iregex ".*\.m\.js" -exec mv {} $JETPACK_DIR/public/manifest.js \; +find ./public -iregex ".*build.*\.js" ! -iregex ".*\.m\.js" -exec mv {} $JETPACK_DIR/public/build.js \; +find ./public -iregex ".*vendor.*\.js" ! -iregex ".*\.m\.js" -exec mv {} $JETPACK_DIR/public/vendor.js \; +cp ./public/jetpack.css $JETPACK_DIR/public/ +cp ./public/jetpack-debug.css $JETPACK_DIR/public/ +cp ./public/jetpack-debug.css.map $JETPACK_DIR/public/ + +# npm run -s build-server && NODE_PATH=$NODE_PATH:server:client:. CALYPSO_ENV=jetpack node server/bundler/bin/bundler.js && ./bin/copy-files.sh diff --git a/client/boot/app.js b/client/boot/app.js index 79351545b37dc3..19c22bd63babc2 100644 --- a/client/boot/app.js +++ b/client/boot/app.js @@ -53,4 +53,8 @@ window.AppBoot = () => { } else { user.once( 'change', () => boot( user ) ); } + // @todo Can we get a wp-defined user...? + if ( 'jetpack' === PROJECT_NAME ) { + boot( user ); + } }; diff --git a/client/boot/project/jetpack.js b/client/boot/project/jetpack.js new file mode 100644 index 00000000000000..9b97ff577365b2 --- /dev/null +++ b/client/boot/project/jetpack.js @@ -0,0 +1,109 @@ +/** + * External dependencies + */ +const React = require( 'react' ), + ReactDom = require( 'react-dom' ), + store = require( 'store' ), + debug = require( 'debug' )( 'calypso' ), + page = require( 'page' ), + includes = require( 'lodash/includes' ); + +/** + * Internal dependencies + */ +const config = require( 'config' ), + normalize = require( 'lib/route/normalize' ), + { isLegacyRoute } = require( 'lib/route/legacy-routes' ); + +import { getSectionName } from 'state/ui/selectors'; + +function renderLayout( reduxStore ) { + const Layout = require( 'controller' ).ReduxWrappedLayout; + + const layoutElement = React.createElement( Layout, { + store: reduxStore + } ); + + ReactDom.render( + layoutElement, + document.getElementById( 'wpcom' ) + ); + + debug( 'Main layout rendered.' ); +} + +export function utils() { + debug( 'Executing Jetpack utils.' ); +} + +export const configureReduxStore = ( currentUser, reduxStore ) => { + debug( 'Executing Jetpack configure Redux store.' ); +}; + +export function setupMiddlewares( currentUser, reduxStore ) { + debug( 'Executing Jetpack setup middlewares.' ); + + // Render Layout only for non-isomorphic sections. + // Isomorphic sections will take care of rendering their Layout last themselves. + if ( ! document.getElementById( 'primary' ) ) { + renderLayout( reduxStore ); + } + + page( '*', function( context, next ) { + // Don't normalize legacy routes - let them fall through and be unhandled + // so that page redirects away from Calypso + if ( isLegacyRoute( context.pathname ) ) { + return next(); + } + + return normalize( context, next ); + } ); + + page( '*', function( context, next ) { + const path = context.pathname; + + // Bypass this global handler for legacy routes + // to avoid bumping stats and changing focus to the content + if ( isLegacyRoute( path ) ) { + return next(); + } + + next(); + } ); + + require( 'my-sites' )(); + + /* + * Layouts with differing React mount-points will not reconcile correctly, + * so remove an existing single-tree layout by re-rendering if necessary. + * + * TODO (@seear): Converting all of Calypso to single-tree layout will + * make this unnecessary. + */ + page( '*', function( context, next ) { + const previousLayoutIsSingleTree = !! ( + document.getElementsByClassName( 'wp-singletree-layout' ).length + ); + + const singleTreeSections = [ 'account-recovery', 'login', 'posts-custom', 'theme', 'themes', 'preview' ]; + const sectionName = getSectionName( context.store.getState() ); + const isMultiTreeLayout = ! includes( singleTreeSections, sectionName ); + + if ( isMultiTreeLayout && previousLayoutIsSingleTree ) { + debug( 'Re-rendering multi-tree layout' ); + ReactDom.unmountComponentAtNode( document.getElementById( 'wpcom' ) ); + renderLayout( context.store ); + } else if ( ! isMultiTreeLayout && ! previousLayoutIsSingleTree ) { + debug( 'Unmounting multi-tree layout' ); + ReactDom.unmountComponentAtNode( document.getElementById( 'primary' ) ); + ReactDom.unmountComponentAtNode( document.getElementById( 'secondary' ) ); + } + next(); + } ); + + page.start( { + hashbang: true, + decodeURLComponents: false, + } ); + page.base( window.pageBase ); +} diff --git a/client/components/data/query-jetpack-connection/index.jsx b/client/components/data/query-jetpack-connection/index.jsx index f1c0a18b94d1f2..4b1d76032dd1b5 100644 --- a/client/components/data/query-jetpack-connection/index.jsx +++ b/client/components/data/query-jetpack-connection/index.jsx @@ -12,13 +12,15 @@ import { requestJetpackConnectionStatus } from 'state/jetpack/connection/actions class QueryJetpackConnection extends Component { static propTypes = { - siteId: PropTypes.number.isRequired, + siteId: PropTypes.number, requestingJetpackConnectionStatus: PropTypes.bool, requestJetpackConnectionStatus: PropTypes.func }; componentWillMount() { - this.request( this.props ); + if ( this.props.siteId !== null ) { + this.request( this.props ); + } } componentWillReceiveProps( nextProps ) { diff --git a/client/components/data/query-jetpack-modules/index.jsx b/client/components/data/query-jetpack-modules/index.jsx index 49f607c4d0956b..4e24ae40931660 100644 --- a/client/components/data/query-jetpack-modules/index.jsx +++ b/client/components/data/query-jetpack-modules/index.jsx @@ -12,13 +12,15 @@ import { fetchModuleList } from 'state/jetpack/modules/actions'; class QueryJetpackModules extends Component { static propTypes = { - siteId: PropTypes.number.isRequired, + siteId: PropTypes.number, requestingModules: PropTypes.bool, fetchModuleList: PropTypes.func }; componentWillMount() { - this.request( this.props ); + if ( this.props.siteId !== null ) { + this.request( this.props ); + } } componentWillReceiveProps( nextProps ) { diff --git a/client/components/data/query-jetpack-settings/index.jsx b/client/components/data/query-jetpack-settings/index.jsx index 3d4f3439184967..c51b7ae4391d9f 100644 --- a/client/components/data/query-jetpack-settings/index.jsx +++ b/client/components/data/query-jetpack-settings/index.jsx @@ -12,13 +12,15 @@ import { fetchSettings } from 'state/jetpack/settings/actions'; class QueryJetpackSettings extends Component { static propTypes = { - siteId: PropTypes.number.isRequired, + siteId: PropTypes.number, requestingSettings: PropTypes.bool, fetchSettings: PropTypes.func }; componentWillMount() { - this.request( this.props ); + if ( this.props.siteId !== null ) { + this.request( this.props ); + } } componentWillReceiveProps( nextProps ) { diff --git a/client/extensions/test/controller.js b/client/extensions/test/controller.js new file mode 100644 index 00000000000000..d64d70bc11d230 --- /dev/null +++ b/client/extensions/test/controller.js @@ -0,0 +1,31 @@ +/** + * External dependencies + */ +import React from 'react'; +import page from 'page'; + +/** + * Internal dependencies + */ +import WritingSettings from './writing'; +import DiscussionSettings from './discussion'; +import { renderPage } from 'lib/react-helpers'; + +const loadPage = ( path ) => { + return () => page( path ); +}; + +const jetpackUi = { + + // Writing Settings + writingSettings( context ) { + renderPage( context, React.createElement( WritingSettings, { loadPage } ) ); + }, + + // Discussion Settings + discussionSettings( context ) { + renderPage( context, React.createElement( DiscussionSettings, { loadPage } ) ); + }, +}; + +module.exports = jetpackUi; diff --git a/client/extensions/test/discussion/index.jsx b/client/extensions/test/discussion/index.jsx new file mode 100644 index 00000000000000..5b9439e66705f1 --- /dev/null +++ b/client/extensions/test/discussion/index.jsx @@ -0,0 +1,25 @@ +/** + * External dependencies + */ +import React, { PureComponent } from 'react'; + +/** + * Internal dependencies + */ +import Main from 'components/main'; +import Button from 'components/button'; +import Card from 'components/card'; + +export default class JetpackDiscussion extends PureComponent { + render() { + return ( +
+ +

Discussion

+

Settings, or something, would go here.

+ +
+
+ ); + } +} diff --git a/client/extensions/test/index.js b/client/extensions/test/index.js new file mode 100644 index 00000000000000..f8a526802386a7 --- /dev/null +++ b/client/extensions/test/index.js @@ -0,0 +1,15 @@ +/** + * External dependencies + */ +import page from 'page'; + +/** + * Internal dependencies + */ +import controller from './controller'; + +export default function() { + page( '/test/writing', controller.writingSettings ); + page( '/test/discussion', controller.discussionSettings ); + page( '/test/', controller.writingSettings ); +} diff --git a/client/extensions/test/package.json b/client/extensions/test/package.json new file mode 100644 index 00000000000000..cfe6fdace9248a --- /dev/null +++ b/client/extensions/test/package.json @@ -0,0 +1,15 @@ +{ + "name": "test", + "author": "Automattic", + "description": "Test", + "version": "0.0.1", + "env_id": [ "development", "wpcalypso", "jetpack" ], + "section": { + "name": "test", + "paths": [ "/test" ], + "module": "test", + "group": "jetpack", + "enableLoggedOut": true, + "secondary": false + } +} diff --git a/client/extensions/test/writing/index.jsx b/client/extensions/test/writing/index.jsx new file mode 100644 index 00000000000000..a04c20c2398267 --- /dev/null +++ b/client/extensions/test/writing/index.jsx @@ -0,0 +1,43 @@ +/** + * External dependencies + */ +import React, { PureComponent } from 'react'; + +/** + * Internal dependencies + */ +import Main from 'components/main'; +import Button from 'components/button'; +import Card from 'components/card'; +import Banner from 'components/banner'; +import FoldableCard from 'components/foldable-card'; +import MediaSettings from 'my-sites/site-settings/media-settings'; +import QueryJetpackModules from 'components/data/query-jetpack-modules'; +import wrapSettingsForms from 'my-sites/site-settings/wrap-settings-form'; + +class JetpackWriting extends PureComponent { + render() { + const { fields, handleAutosavingToggle, isRequestingSettings, isSavingSettings, onChangeField, } = this.props; + return ( +
+ +

Writing

+ + + +
+ + WOW! +
+ ); + } +} + +export default wrapSettingsForms( () => ( {} ) )( JetpackWriting ); \ No newline at end of file diff --git a/client/jetpack.js b/client/jetpack.js new file mode 100644 index 00000000000000..905909f974dcf1 --- /dev/null +++ b/client/jetpack.js @@ -0,0 +1,12 @@ +const sections = [ + { + name: 'test', + paths: [ 'test' ], + module: 'extensions/test', + group: 'jetpack', + enableLoggedOut: true, + secondary: false + }, +]; + +module.exports = sections; diff --git a/client/layout/logged-out.jsx b/client/layout/logged-out.jsx index 36fc431d5d3290..5887c89c471943 100644 --- a/client/layout/logged-out.jsx +++ b/client/layout/logged-out.jsx @@ -4,6 +4,7 @@ import React from 'react'; import classNames from 'classnames'; import { connect } from 'react-redux'; +import config from 'config'; /** * Internal dependencies @@ -27,7 +28,10 @@ const LayoutLoggedOut = ( { return (
- + { ( 'jetpack' === config( 'project' ) ) + ? null + : + }
{ primary } diff --git a/client/lib/jetpack-rest-api-client/index.js b/client/lib/jetpack-rest-api-client/index.js new file mode 100644 index 00000000000000..21c670962f01d9 --- /dev/null +++ b/client/lib/jetpack-rest-api-client/index.js @@ -0,0 +1,256 @@ +/** + * External dependencies + */ +import { assign } from 'lodash'; + +/** + * External dependencies + */ + +function JetpackRestApiClient( root, nonce ) { + let apiRoot = root, + headers = { + 'X-WP-Nonce': nonce + }, + getParams = { + credentials: 'same-origin', + headers: headers + }, + postParams = { + method: 'post', + credentials: 'same-origin', + headers: assign( {}, headers, { + 'Content-type': 'application/json' + } ) + }; + + const methods = { + setApiRoot( newRoot ) { + apiRoot = newRoot; + }, + setApiNonce( newNonce ) { + headers = { + 'X-WP-Nonce': newNonce + }; + getParams = { + credentials: 'same-origin', + headers: headers + }; + postParams = { + method: 'post', + credentials: 'same-origin', + headers: assign( {}, headers, { + 'Content-type': 'application/json' + } ) + }; + }, + + fetchSiteConnectionStatus: () => getRequest( `${ apiRoot }jetpack/v4/connection`, getParams ) + .then( response => response.json() ), + + fetchUserConnectionData: () => getRequest( `${ apiRoot }jetpack/v4/connection/data`, getParams ) + .then( response => response.json() ), + + disconnectSite: () => postRequest( `${ apiRoot }jetpack/v4/connection`, postParams, { + body: JSON.stringify( { isActive: false } ) + } ) + .then( checkStatus ) + .then( response => response.json() ), + + fetchConnectUrl: () => getRequest( `${ apiRoot }jetpack/v4/connection/url`, getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + unlinkUser: () => postRequest( `${ apiRoot }jetpack/v4/connection/user`, postParams, { + body: JSON.stringify( { linked: false } ) + } ) + .then( checkStatus ) + .then( response => response.json() ), + + jumpStart: ( action ) => { + let active; + if ( action === 'activate' ) { + active = true; + } + if ( action === 'deactivate' ) { + active = false; + } + return postRequest( `${ apiRoot }jetpack/v4/jumpstart`, postParams, { + body: JSON.stringify( { active } ) + } ) + .then( checkStatus ) + .then( response => response.json() ); + }, + + fetchModules: () => getRequest( `${ apiRoot }jetpack/v4/module/all`, getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + fetchModule: ( slug ) => getRequest( `${ apiRoot }jetpack/v4/module/${ slug }`, getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + activateModule: ( slug ) => postRequest( + `${ apiRoot }jetpack/v4/module/${ slug }/active`, + postParams, + { + body: JSON.stringify( { active: true } ) + } + ) + .then( checkStatus ) + .then( response => response.json() ), + + deactivateModule: ( slug ) => postRequest( + `${ apiRoot }jetpack/v4/module/${ slug }/active`, + postParams, + { + body: JSON.stringify( { active: false } ) + } + ), + + updateModuleOptions: ( slug, newOptionValues ) => postRequest( + `${ apiRoot }jetpack/v4/module/${ slug }`, + postParams, + { + body: JSON.stringify( newOptionValues ) + } + ) + .then( checkStatus ) + .then( response => response.json() ), + + updateSettings: ( newOptionValues ) => postRequest( + `${ apiRoot }jetpack/v4/settings`, + postParams, + { + body: JSON.stringify( newOptionValues ) + } + ) + .then( checkStatus ) + .then( response => response.json() ), + + getProtectCount: () => getRequest( `${ apiRoot }jetpack/v4/module/protect/data`, getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + resetOptions: ( options ) => postRequest( + `${ apiRoot }jetpack/v4/options/${ options }`, + postParams, + { + body: JSON.stringify( { reset: true } ) + } + ) + .then( checkStatus ) + .then( response => response.json() ), + + getVaultPressData: () => getRequest( `${ apiRoot }jetpack/v4/module/vaultpress/data`, getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + getAkismetData: () => getRequest( `${ apiRoot }jetpack/v4/module/akismet/data`, getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + checkAkismetKey: () => getRequest( `${ apiRoot }jetpack/v4/module/akismet/key/check`, getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + checkAkismetKeyTyped: apiKey => postRequest( + `${ apiRoot }jetpack/v4/module/akismet/key/check`, + postParams, + { + body: JSON.stringify( { api_key: apiKey } ) + } + ) + .then( checkStatus ) + .then( response => response.json() ), + + fetchStatsData: ( range ) => getRequest( statsDataUrl( range ), getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + getPluginUpdates: () => getRequest( `${ apiRoot }jetpack/v4/updates/plugins`, getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + fetchSettings: () => getRequest( `${ apiRoot }jetpack/v4/settings`, getParams ) + .then( checkStatus ) + .then( response => response.json() ), + + updateSetting: ( updatedSetting ) => postRequest( `${ apiRoot }jetpack/v4/settings`, postParams, { + body: JSON.stringify( updatedSetting ) + } ) + .then( checkStatus ) + .then( response => response.json() ), + + fetchSiteData: () => getRequest( `${ apiRoot }jetpack/v4/site`, getParams ) + .then( checkStatus ) + .then( response => response.json() ) + .then( body => JSON.parse( body.data ) ), + + dismissJetpackNotice: ( notice ) => postRequest( + `${ apiRoot }jetpack/v4/notice/${ notice }`, + postParams, + { + body: JSON.stringify( { dismissed: true } ) + } + ) + .then( checkStatus ) + .then( response => response.json() ), + + fetchPluginsData: () => getRequest( `${ apiRoot }jetpack/v4/plugins`, getParams ) + .then( checkStatus ) + .then( response => response.json() ) + }; + + function addCacheBuster( route ) { + const parts = route.split( '?' ), + query = parts.length > 1 + ? parts[ 1 ] + : '', + args = query.length + ? query.split( '&' ) + : []; + + args.push( '_cacheBuster=' + new Date().getTime() ); + + return parts[ 0 ] + '?' + args.join( '&' ); + } + + function getRequest( route, params ) { + return fetch( addCacheBuster( route ), params ); + } + + function postRequest( route, params, body ) { + return fetch( route, assign( {}, params, body ) ); + } + + function statsDataUrl( range ) { + let url = `${ apiRoot }jetpack/v4/module/stats/data`; + if ( url.indexOf( '?' ) !== -1 ) { + url = url + `&range=${ encodeURIComponent( range ) }`; + } else { + url = url + `?range=${ encodeURIComponent( range ) }`; + } + return url; + } + + assign( this, methods ); +} + +const restApi = new JetpackRestApiClient(); + +restApi.setApiRoot( window.WP_API_root ); +restApi.setApiNonce( window.WP_API_nonce ); + +export default restApi; + +function checkStatus( response ) { + if ( response.status >= 200 && response.status < 300 ) { + return response; + } + return response.json().then( json => { + const error = new Error( json.message ); + error.response = json; + throw error; + } ); +} \ No newline at end of file diff --git a/client/state/jetpack/modules/actions.js b/client/state/jetpack/modules/actions.js index 2cf9cf25e1d0dd..bb7c0892c7178e 100644 --- a/client/state/jetpack/modules/actions.js +++ b/client/state/jetpack/modules/actions.js @@ -19,6 +19,10 @@ import { JETPACK_MODULES_REQUEST_SUCCESS } from 'state/action-types'; import wp from 'lib/wp'; +import config from 'config'; +import restApiClient from 'lib/jetpack-rest-api-client'; + +const isJetpackAdminPage = config( 'env_id' ) === 'jetpack'; export const activateModule = ( siteId, moduleSlug, silent = false ) => { return ( dispatch ) => { @@ -100,11 +104,11 @@ export const fetchModuleList = ( siteId ) => { type: JETPACK_MODULES_REQUEST, siteId } ); - - return wp.undocumented().getJetpackModules( siteId ) - .then( ( { data } ) => { + const method = isJetpackAdminPage ? restApiClient.fetchModules : wp.undocumented().getJetpackModules + return method( siteId ) + .then( ( data ) => { const modules = mapValues( - data, + isJetpackAdminPage ? data : data.data, ( module ) => ( { active: module.activated, ...omit( module, 'activated' ) diff --git a/client/state/ui/reducer.js b/client/state/ui/reducer.js index d3493e406c2ea5..21652d1687c88f 100644 --- a/client/state/ui/reducer.js +++ b/client/state/ui/reducer.js @@ -23,7 +23,9 @@ import happychat from './happychat/reducer'; import mediaModal from './media-modal/reducer'; import themeSetup from './theme-setup/reducers'; import npsSurveyNotice from './nps-survey-notice/reducer'; +import config from 'config'; +const isJetpackAdminPage = config( 'env_id' ) === 'jetpack'; /** * Tracks the currently selected site ID. * @@ -31,7 +33,7 @@ import npsSurveyNotice from './nps-survey-notice/reducer'; * @param {Object} action Action payload * @return {Object} Updated state */ -export function selectedSiteId( state = null, action ) { +export function selectedSiteId( state = isJetpackAdminPage? 0 : null, action ) { switch ( action.type ) { case SELECTED_SITE_SET: return action.siteId || null; diff --git a/config/jetpack.json b/config/jetpack.json new file mode 100644 index 00000000000000..10b653a69fae0a --- /dev/null +++ b/config/jetpack.json @@ -0,0 +1,103 @@ +{ + "env": "jetpack", + "env_id": "jetpack", + "client_slug": "browser", + "hostname": "calypso.localhost", + "port": 3000, + "project": "jetpack", + "i18n_default_locale_slug": "en", + "wpcom_user_bootstrap": false, + "features": { + "ad-tracking": false, + "apple-pay": false, + "boosted-post-survey": false, + "catch-js-errors": false, + "code-splitting": false, + "community-translator": false, + "desktop-promo": false, + "happychat": false, + "help": false, + "help/courses": false, + "jetpack/connect": false, + "jetpack/google-analytics": false, + "jetpack/personalPlan": false, + "jetpack/api-cache": false, + "manage/custom-post-types": false, + "manage/customize": false, + "manage/edit-user": false, + "manage/export": false, + "manage/jetpack": false, + "manage/menus": false, + "manage/menus-jetpack": false, + "manage/option_sync_non_public_post_stati": false, + "manage/pages": false, + "manage/pages/set-homepage": false, + "manage/payment-methods": false, + "manage/people": false, + "manage/people/readers": false, + "manage/plans": false, + "manage/plan-features": false, + "manage/plugins": false, + "manage/plugins/browser": false, + "manage/plugins/cache": false, + "manage/plugins/setup": false, + "manage/posts": false, + "manage/security": false, + "manage/seo": false, + "manage/sharing": false, + "manage/site-settings/analytics": false, + "manage/site-settings/categories": false, + "manage/site-settings/delete-site": false, + "manage/site-settings/site-icon": false, + "manage/stats": false, + "manage/themes": false, + "manage/themes-jetpack": false, + "manage/themes/details": false, + "manage/themes/logged-out": false, + "me/account": false, + "me/find-friends": false, + "me/my-profile": false, + "me/next-steps": false, + "me/notifications": false, + "me/trophies": false, + "olark": false, + "perfmon": false, + "persist-redux": true, + "plans/personal-plan": false, + "post-editor/author-selector": false, + "post-editor/insert-menu": false, + "press-this": false, + "preview-layout": false, + "push-notifications": false, + "reader": false, + "reader/full-errors": false, + "reader/tags-with-elasticsearch": false, + "reader/related-posts": false, + "reader/search": false, + "resume-editing": false, + "republicize": false, + "rubberband-scroll-disable": false, + "server-side-rendering": false, + "settings/security/monitor": false, + "signup/domain-first-flow": false, + "support-user": false, + "sync-handler": false, + "ui/first-view": false, + "upgrades/checkout": false, + "upgrades/credit-cards": false, + "upgrades/domain-search": false, + "upgrades/in-app-purchase": false, + "upgrades/paypal": false, + "upgrades/premium-themes": false, + "upgrades/removal-survey": false, + "upgrades/precancellation-chat": false, + "upgrades/presale-chat": false, + "use-page-hashbang": true + }, + "rtl": false, + "jetpack_min_version": "4.5", + "mc_analytics_enabled": false, + "boom_analytics_enabled": false, + "google_analytics_enabled": false, + "google_adwords_conversion_id": 0 +} diff --git a/package.json b/package.json index aefbfc2c78d711..e28170f4299de8 100644 --- a/package.json +++ b/package.json @@ -162,6 +162,9 @@ "build-css:directly": "node-sass --include-path client assets/stylesheets/directly.scss public/directly.css && npm run -s autoprefixer -- public/directly.css", "build-css:editor": "node-sass --include-path client assets/stylesheets/editor.scss public/editor.css && npm run -s autoprefixer -- public/editor.css", "build-css:style": "node-sass --include-path client assets/stylesheets/style.scss public/style.css && npm run -s autoprefixer -- public/style.css && rtlcss public/style.css public/style-rtl.css", + "build-jetpack-css": "run-p -s build-jetpack-css:*", + "build-jetpack-css:debug": "node-sass --include-path client --source-map \"public/jetpack-debug.css.map\" assets/stylesheets/jetpack.scss public/jetpack-debug.css && npm run -s autoprefixer -- public/jetpack-debug.css", + "build-jetpack-css:production": "node-sass --include-path client assets/stylesheets/jetpack.scss public/jetpack.css && npm run -s autoprefixer -- public/jetpack.css && rtlcss public/jetpack.css public/jetpack-rtl.css", "prebuild-desktop": "npm run -s install-if-needed", "build-desktop": "run-p -s build-server build-css", "postbuild-desktop": "npm run -s bundle", @@ -175,6 +178,9 @@ "build-docker": "docker build -t wp-calypso .", "prebuild-server": "mkdirp build", "build-server": "npm run -s env -- webpack --display-error-details --config webpack.config.node.js", + "prebuild-jetpack": "npm run -s install-if-needed", + "build-jetpack": "cross-env CALYPSO_ENV=jetpack run-p -s build-server build-jetpack-css", + "postbuild-jetpack": "cross-env NODE_PATH=$NODE_PATH:server:client:. CALYPSO_ENV=jetpack node server/bundler/bin/bundler.js", "bundle": "npm run -s env -- node server/bundler/bin/bundler.js", "clean": "npm run -s clean:build && npm run -s clean:devdocs && npm run -s clean:public", "clean:build": "npm run -s rm -- build && npm run -s rm -- server/bundler .json && npm run -s rm -- .babel-cache", diff --git a/server/bundler/assets-jetpack.json b/server/bundler/assets-jetpack.json new file mode 100644 index 00000000000000..511eb56d502382 --- /dev/null +++ b/server/bundler/assets-jetpack.json @@ -0,0 +1,16 @@ +[ + { + "name": "manifest", + "hash": "b38e179bfb394033303c", + "file": "manifest.bc343a13dec3096d77d4.js", + "url": "/calypso/manifest.bc343a13dec3096d77d4.js", + "size": 0 + }, + { + "name": "build-jetpack", + "hash": "fc313da169940c801834", + "file": "build-jetpack.fc313da169940c801834.js", + "url": "/calypso/build-jetpack.fc313da169940c801834.js", + "size": 3327346 + } +] \ No newline at end of file