diff --git a/admin/code/CampaignAdmin.php b/admin/code/CampaignAdmin.php index dae35a644..702e697ae 100644 --- a/admin/code/CampaignAdmin.php +++ b/admin/code/CampaignAdmin.php @@ -35,8 +35,12 @@ class CampaignAdmin extends LeftAndMain implements PermissionProvider { Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/dist/campaign-admin.js'); } - public function getEditForm($id = null, $fields = null) { - return ''; + public function getClientConfig() { + return array_merge(parent::getClientConfig(), [ + 'forms' => [ + 'editForm' => $this->schema->getSchema($this->getEditForm()) + ] + ]); } /** diff --git a/admin/javascript/src/boot/campaign-admin.js b/admin/javascript/src/boot/campaign-admin.js index e336ed65f..a4dd656f5 100644 --- a/admin/javascript/src/boot/campaign-admin.js +++ b/admin/javascript/src/boot/campaign-admin.js @@ -2,6 +2,7 @@ import reducerRegister from 'reducer-register'; import $ from 'jQuery'; import React from 'react'; import ReactDOM from 'react-dom'; +import { Provider } from 'react-redux'; import CampaignAdmin from '../sections/campaign-admin/controller'; import campaignsReducer from '../state/campaigns/reducer'; @@ -12,7 +13,11 @@ $.entwine('ss', function ($) { $('.cms-content.CampaignAdmin').entwine({ onadd: function () { - ReactDOM.render(, this[0]); + ReactDOM.render( + + + + , this[0]); }, onremove: function () { diff --git a/admin/javascript/src/boot/index.js b/admin/javascript/src/boot/index.js index 6abb0ed5b..49afbb79e 100644 --- a/admin/javascript/src/boot/index.js +++ b/admin/javascript/src/boot/index.js @@ -1,15 +1,26 @@ +import $ from 'jQuery'; import { combineReducers, createStore, applyMiddleware } from 'redux'; import thunkMiddleware from 'redux-thunk'; import createLogger from 'redux-logger'; import reducerRegister from 'reducer-register'; +import * as configActions from '../state/config/actions'; +import ConfigReducer from '../state/config/reducer'; + function appBoot() { + reducerRegister.add('config', ConfigReducer); + const initialState = {}; const rootReducer = combineReducers(reducerRegister.getAll()); const createStoreWithMiddleware = applyMiddleware(thunkMiddleware, createLogger())(createStore); - const store = createStoreWithMiddleware(rootReducer, initialState); - console.log(store.getState()); + // TODO: The store needs to be passed into route callbacks on the route context. + window.store = createStoreWithMiddleware(rootReducer, initialState); + + // Set the initial config state. + configActions.setConfig(window.ss.config)(window.store.dispatch); } -window.onload = appBoot; +// TODO: This should be using `window.onload` but isn't because Entwine hooks are being used to set up the . +// `window.onload` happens AFTER these Entwine hooks which means the store is undefined when the is constructed. +$('body').entwine({ onadd: function () { appBoot(); } }); diff --git a/admin/javascript/src/sections/campaign-admin/controller.js b/admin/javascript/src/sections/campaign-admin/controller.js index 3046a2cae..c26042390 100644 --- a/admin/javascript/src/sections/campaign-admin/controller.js +++ b/admin/javascript/src/sections/campaign-admin/controller.js @@ -1,4 +1,5 @@ import React from 'react'; +import { connect } from 'react-redux'; import SilverStripeComponent from 'silverstripe-component'; import NorthHeader from '../../components/north-header'; import GridField from '../grid-field'; @@ -16,4 +17,23 @@ class CampaignAdminContainer extends SilverStripeComponent { } -export default CampaignAdminContainer; +CampaignAdminContainer.propTypes = { + config: React.PropTypes.shape({ + data: React.PropTypes.shape({ + forms: React.PropTypes.shape({ + editForm: React.PropTypes.shape({ + schemaUrl: React.PropTypes.string + }) + }) + }) + }), + sectionConfigKey: React.PropTypes.string.isRequired +}; + +function mapStateToProps(state, ownProps) { + return { + config: state.config.sections[ownProps.sectionConfigKey] + } +} + +export default connect(mapStateToProps)(CampaignAdminContainer); diff --git a/admin/javascript/src/state/config/action-types.js b/admin/javascript/src/state/config/action-types.js new file mode 100644 index 000000000..9494962d0 --- /dev/null +++ b/admin/javascript/src/state/config/action-types.js @@ -0,0 +1,3 @@ +export default { + SET_CONFIG: 'SET_CONFIG' +} diff --git a/admin/javascript/src/state/config/actions.js b/admin/javascript/src/state/config/actions.js new file mode 100644 index 000000000..a6f69a974 --- /dev/null +++ b/admin/javascript/src/state/config/actions.js @@ -0,0 +1,15 @@ +import ACTION_TYPES from './action-types'; + +/** + * Sets global config for the application. + * + * @param object config + */ +export function setConfig(config) { + return (dispatch, getState) => { + return dispatch({ + type: ACTION_TYPES.SET_CONFIG, + payload: { config } + }); + } +} diff --git a/admin/javascript/src/state/config/reducer.js b/admin/javascript/src/state/config/reducer.js new file mode 100644 index 000000000..88309c9da --- /dev/null +++ b/admin/javascript/src/state/config/reducer.js @@ -0,0 +1,18 @@ +import deepFreeze from 'deep-freeze'; +import ACTION_TYPES from './action-types'; + +function configReducer(state = {}, action) { + + switch (action.type) { + + case ACTION_TYPES.SET_CONFIG: + return deepFreeze(Object.assign({}, state, action.payload.config)); + + default: + return state; + + } + +} + +export default configReducer; diff --git a/gulpfile.js b/gulpfile.js index 4404d50bf..87c9b8eaa 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -254,6 +254,7 @@ gulp.task('bundle-boot', function bundleBoot() { ignore: /(node_modules)/ })) .external('reducer-register') + .external('jQuery') .bundle() .on('error', notify.onError({ message: bundleFileName + ': <%= error.message %>' })) .pipe(source(bundleFileName)) diff --git a/package.json b/package.json index b17c8c49f..54c912c59 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,25 @@ "url": "https://github.com/silverstripe/silverstripe-framework/issues" }, "homepage": "https://github.com/silverstripe/silverstripe-framework#readme", + "dependencies": { + "blueimp-file-upload": "^6.0.3", + "blueimp-load-image": "^1.1.3", + "blueimp-tmpl": "^1.0.2", + "bootstrap": "^4.0.0-alpha.2", + "deep-freeze": "0.0.1", + "isomorphic-fetch": "^2.2.1", + "jquery-sizes": "^0.33.0", + "npm-shrinkwrap": "^5.4.1", + "page.js": "^4.13.3", + "react": "^0.14.7", + "react-addons-css-transition-group": "^0.14.7", + "react-addons-test-utils": "^0.14.7", + "react-dom": "^0.14.7", + "react-redux": "^4.0.6", + "redux": "^3.3.1", + "redux-thunk": "^1.0.3", + "tinymce": "^4.3.3" + }, "devDependencies": { "autoprefixer": "^6.3.1", "babel-core": "^6.4.0",