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",