diff --git a/web/client/actions/__tests__/plugins-test.js b/web/client/actions/__tests__/plugins-test.js
new file mode 100644
index 0000000000..7154a6b4f9
--- /dev/null
+++ b/web/client/actions/__tests__/plugins-test.js
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2016, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const expect = require('expect');
+const {loadPlugins, LOAD_PLUGINS} = require('../plugins');
+
+describe('Test plugins related actions', () => {
+ it('test load plugins action', () => {
+ const action = loadPlugins([{}]);
+
+ expect(action).toExist();
+ expect(action.type).toBe(LOAD_PLUGINS);
+ expect(action.plugins).toExist();
+ expect(action.plugins.length).toBe(1);
+ });
+});
diff --git a/web/client/actions/plugins.js b/web/client/actions/plugins.js
new file mode 100644
index 0000000000..43f065dea7
--- /dev/null
+++ b/web/client/actions/plugins.js
@@ -0,0 +1,16 @@
+/**
+ * Copyright 2016, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+const LOAD_PLUGINS = 'LOAD_PLUGINS';
+
+function loadPlugins(plugins) {
+ return {
+ type: LOAD_PLUGINS,
+ plugins
+ };
+}
+module.exports = {LOAD_PLUGINS, loadPlugins};
diff --git a/web/client/components/app/StandardApp.jsx b/web/client/components/app/StandardApp.jsx
index 55458c874a..7bc1cfadaf 100644
--- a/web/client/components/app/StandardApp.jsx
+++ b/web/client/components/app/StandardApp.jsx
@@ -36,7 +36,7 @@ const StandardApp = React.createClass({
pluginsDef: {plugins: {}, requires: {}},
initialActions: [],
printingEnabled: false,
- appStore: () => ({dispatch: () => {}}),
+ appStore: () => ({dispatch: () => {}, subscribe: () => {}}),
appComponent: () =>
};
},
@@ -57,6 +57,16 @@ const StandardApp = React.createClass({
onPersist: onInit
});
this.store = this.props.appStore(this.props.pluginsDef.plugins, opts);
+ let newPlugins;
+ this.store.subscribe(() => {
+ const state = this.store.getState();
+ if (state.plugins && Object.keys(state.plugins).length > 0) {
+ if (state.plugins !== newPlugins) {
+ newPlugins = state.plugins;
+ this.store.replaceReducer(this.props.appStore(assign({}, this.props.pluginsDef.plugins, newPlugins), {updateReducers: true}));
+ }
+ }
+ });
if (!opts.persist) {
onInit();
}
diff --git a/web/client/components/app/StandardRouter.jsx b/web/client/components/app/StandardRouter.jsx
index 99159daeb0..f9f672e5f4 100644
--- a/web/client/components/app/StandardRouter.jsx
+++ b/web/client/components/app/StandardRouter.jsx
@@ -13,28 +13,43 @@ const Debug = require('../development/Debug');
const {Router, Route, hashHistory} = require('react-router');
const Localized = require('../I18N/Localized');
+const assign = require('object-assign');
+const PluginsUtils = require('../../utils/PluginsUtils');
const StandardRouter = React.createClass({
propTypes: {
plugins: React.PropTypes.object,
locale: React.PropTypes.object,
- pages: React.PropTypes.array
+ pages: React.PropTypes.array,
+ loadPlugins: React.PropTypes.func
},
getDefaultProps() {
return {
plugins: {},
locale: {messages: {}, current: ''},
- pages: []
+ pages: [],
+ loadPlugins: () => {}
};
},
+ getInitialState() {
+ return {plugins: {}};
+ },
renderPages() {
return this.props.pages.map((page) => {
const pageConfig = page.pageConfig || {};
const Component = connect(() => ({
- plugins: this.props.plugins,
+ plugins: assign({}, this.props.plugins, this.state.plugins),
...pageConfig
}))(page.component);
- return ();
+ return ( {
+ page.plugins((newPlugins) => {
+ this.setState({
+ plugins: assign({}, this.state.plugins, PluginsUtils.getPlugins(newPlugins))
+ });
+ this.props.loadPlugins(newPlugins);
+ callback();
+ });
+ } : null}/>);
});
},
render() {
diff --git a/web/client/components/app/__tests__/StandardApp-test.jsx b/web/client/components/app/__tests__/StandardApp-test.jsx
index 35b9f21291..ea8dec6a47 100644
--- a/web/client/components/app/__tests__/StandardApp-test.jsx
+++ b/web/client/components/app/__tests__/StandardApp-test.jsx
@@ -56,7 +56,8 @@ describe('StandardApp', () => {
const store = () => ({
dispatch() {
dispatched++;
- }
+ },
+ subscribe() {}
});
@@ -75,7 +76,8 @@ describe('StandardApp', () => {
if (value === 10) {
done();
}
- }
+ },
+ subscribe() {}
});
diff --git a/web/client/components/app/__tests__/StandardRouter-test.jsx b/web/client/components/app/__tests__/StandardRouter-test.jsx
index cafa1893d1..448c715c9d 100644
--- a/web/client/components/app/__tests__/StandardRouter-test.jsx
+++ b/web/client/components/app/__tests__/StandardRouter-test.jsx
@@ -33,7 +33,7 @@ const mycomponent = React.createClass({
}
});
-describe('StandardApp', () => {
+describe('StandardRouter', () => {
beforeEach((done) => {
document.body.innerHTML = '';
ConfigUtils.setLocalConfigurationFile('base/web/client/test-resources/localConfig.json');
@@ -93,4 +93,34 @@ describe('StandardApp', () => {
expect(dom.getElementsByClassName('MyPlugin').length).toBe(1);
});
+
+ it('creates a default router app with pages and on page plugins', () => {
+ const handlers = {
+ loadPlugins: () => {}
+ };
+ const plugins = {
+ MyPlugin: {}
+ };
+ const spy = expect.spyOn(handlers, "loadPlugins");
+ const pluginsFunc = (callback) => {
+ callback(plugins);
+ };
+
+ const store = {
+ dispatch: () => {},
+ subscribe: () => {},
+ getState: () => ({})
+ };
+ const pages = [{
+ name: 'mypage',
+ path: '/',
+ component: mycomponent,
+ plugins: pluginsFunc
+ }];
+ const app = ReactDOM.render(, document.getElementById("container"));
+ expect(app).toExist();
+
+ expect(spy.calls.length).toBe(1);
+ expect(spy.calls[0].arguments[0]).toBe(plugins);
+ });
});
diff --git a/web/client/components/map/leaflet/Layer.jsx b/web/client/components/map/leaflet/Layer.jsx
index 878406d19c..cadd5d1232 100644
--- a/web/client/components/map/leaflet/Layer.jsx
+++ b/web/client/components/map/leaflet/Layer.jsx
@@ -104,6 +104,7 @@ const LeafletLayer = React.createClass({
if (this.layer) {
this.layer.layerName = options.name;
this.layer.layerId = options.id;
+ this.setState({});
}
}
},
diff --git a/web/client/components/map/openlayers/Layer.jsx b/web/client/components/map/openlayers/Layer.jsx
index 63a1d55ed5..09d5c21ebc 100644
--- a/web/client/components/map/openlayers/Layer.jsx
+++ b/web/client/components/map/openlayers/Layer.jsx
@@ -101,8 +101,11 @@ const OpenlayersLayer = React.createClass({
if (type) {
const layerOptions = this.generateOpts(options, position, CoordinatesUtils.normalizeSRS(this.props.srs));
this.layer = Layers.createLayer(type, layerOptions, this.props.map, this.props.mapId);
- if (this.layer && !this.layer.detached) {
- this.addLayer(options);
+ if (this.layer) {
+ this.setState({});
+ if (!this.layer.detached) {
+ this.addLayer(options);
+ }
}
}
},
diff --git a/web/client/containers/MapViewer.jsx b/web/client/containers/MapViewer.jsx
index 583d1a55e3..6790ba54ad 100644
--- a/web/client/containers/MapViewer.jsx
+++ b/web/client/containers/MapViewer.jsx
@@ -15,7 +15,7 @@ const urlQuery = url.parse(window.location.href, true).query;
const ConfigUtils = require('../utils/ConfigUtils');
const PluginsContainer = connect((state) => ({
- pluginsConfig: state.plugins || ConfigUtils.getConfigProp('plugins') || null,
+ pluginsConfig: ConfigUtils.getConfigProp('plugins') || null,
mode: (urlQuery.mode || (state.browser && state.browser.mobile ? 'mobile' : 'desktop')),
pluginsState: state && state.controls || {}
}))(require('../components/plugins/PluginsContainer'));
diff --git a/web/client/examples/rasterstyler/pages/MapViewer.jsx b/web/client/examples/rasterstyler/pages/MapViewer.jsx
index 552fd93ed1..589df3a952 100644
--- a/web/client/examples/rasterstyler/pages/MapViewer.jsx
+++ b/web/client/examples/rasterstyler/pages/MapViewer.jsx
@@ -22,7 +22,7 @@ const {resetControls} = require('../../../actions/controls');
const urlQuery = url.parse(window.location.href, true).query;
const PluginsContainer = connect((state) => ({
- pluginsConfig: state.plugins || ConfigUtils.getConfigProp('plugins') || null,
+ pluginsConfig: ConfigUtils.getConfigProp('plugins') || null,
mode: (urlQuery.mobile || (state.browser && state.browser.touch)) ? 'mobile' : 'desktop'
}))(require('../../../components/plugins/PluginsContainer'));
diff --git a/web/client/examples/styler/pages/MapViewer.jsx b/web/client/examples/styler/pages/MapViewer.jsx
index 50cc307961..eed5c968a4 100644
--- a/web/client/examples/styler/pages/MapViewer.jsx
+++ b/web/client/examples/styler/pages/MapViewer.jsx
@@ -22,7 +22,7 @@ const {resetControls} = require('../../../actions/controls');
const urlQuery = url.parse(window.location.href, true).query;
const PluginsContainer = connect((state) => ({
- pluginsConfig: state.plugins || ConfigUtils.getConfigProp('plugins') || null,
+ pluginsConfig: ConfigUtils.getConfigProp('plugins') || null,
mode: (urlQuery.mobile || (state.browser && state.browser.touch)) ? 'mobile' : 'desktop'
}))(require('../../../components/plugins/PluginsContainer'));
@@ -65,4 +65,3 @@ module.exports = connect((state) => ({
loadMapConfig,
reset: resetControls
})(MapViewer);
-
diff --git a/web/client/plugins/Save.jsx b/web/client/plugins/Save.jsx
index 26cd5b3145..7d6daf00c9 100644
--- a/web/client/plugins/Save.jsx
+++ b/web/client/plugins/Save.jsx
@@ -145,5 +145,8 @@ module.exports = {
return { style: {display: "none"} };
}
}
- }))
+ })),
+ reducers: {
+ currentMap: require('../reducers/currentMap')
+ }
};
diff --git a/web/client/plugins/SaveAs.jsx b/web/client/plugins/SaveAs.jsx
index 56873414ee..118d7f30da 100644
--- a/web/client/plugins/SaveAs.jsx
+++ b/web/client/plugins/SaveAs.jsx
@@ -186,5 +186,8 @@ module.exports = {
return state && state.security && state.security.user ? {} : { style: {display: "none"} };
}
}
- }))
+ })),
+ reducers: {
+ currentMap: require('../reducers/currentMap')
+ }
};
diff --git a/web/client/product/app.jsx b/web/client/product/app.jsx
index 7349f7592a..d3761efc91 100644
--- a/web/client/product/app.jsx
+++ b/web/client/product/app.jsx
@@ -9,6 +9,7 @@ const React = require('react');
const ReactDOM = require('react-dom');
const {connect} = require('react-redux');
const LocaleUtils = require('../utils/LocaleUtils');
+const {loadPlugins} = require('../actions/plugins');
const startApp = () => {
const ConfigUtils = require('../utils/ConfigUtils');
@@ -22,7 +23,9 @@ const startApp = () => {
const StandardRouter = connect((state) => ({
locale: state.locale || {},
pages
- }))(require('../components/app/StandardRouter'));
+ }), {
+ loadPlugins
+ })(require('../components/app/StandardRouter'));
const appStore = require('../stores/StandardStore').bind(null, initialState, {
home: require('./reducers/home'),
diff --git a/web/client/product/appConfig.js b/web/client/product/appConfig.js
index 9bcc57547f..be6112bea7 100644
--- a/web/client/product/appConfig.js
+++ b/web/client/product/appConfig.js
@@ -10,23 +10,75 @@ module.exports = {
pages: [{
name: "home",
path: "/",
- component: require('./pages/Maps')
- }, {
- name: "maps",
- path: "/maps",
- component: require('./pages/Maps')
+ component: require('./pages/Maps'),
+ plugins: (resolve) => {
+ return require.ensure([], () => {
+ resolve({
+ MapsPlugin: require('../plugins/Maps'),
+ MapSearchPlugin: require('../plugins/MapSearch'),
+ HomeDescriptionPlugin: require('./plugins/HomeDescription'),
+ ExamplesPlugin: require('./plugins/Examples'),
+ MapTypePlugin: require('./plugins/MapType'),
+ ForkPlugin: require('./plugins/Fork'),
+ ManagerPlugin: require('../plugins/manager/Manager'),
+ CreateNewMapPlugin: require('../plugins/CreateNewMap')
+ });
+ });
+ }
}, {
name: "mapviewer",
path: "/viewer/:mapType/:mapId",
- component: require('./pages/MapViewer')
- }, {
- name: "manager",
- path: "/manager",
- component: require('./pages/Manager')
+ component: require('./pages/MapViewer'),
+ plugins: (resolve) => {
+ return require.ensure([], () => {
+ resolve({
+ MousePositionPlugin: require('../plugins/MousePosition'),
+ PrintPlugin: require('../plugins/Print'),
+ IdentifyPlugin: require('../plugins/Identify'),
+ TOCPlugin: require('../plugins/TOC'),
+ BackgroundSwitcherPlugin: require('../plugins/BackgroundSwitcher'),
+ MeasurePlugin: require('../plugins/Measure'),
+ MeasureResultsPlugin: require('../plugins/MeasureResults'),
+ MapPlugin: require('../plugins/Map'),
+ ToolbarPlugin: require('../plugins/Toolbar'),
+ DrawerMenuPlugin: require('../plugins/DrawerMenu'),
+ ShapeFilePlugin: require('../plugins/ShapeFile'),
+ SnapshotPlugin: require('../plugins/Snapshot'),
+ SettingsPlugin: require('../plugins/Settings'),
+ ExpanderPlugin: require('../plugins/Expander'),
+ SearchPlugin: require('../plugins/Search'),
+ ScaleBoxPlugin: require('../plugins/ScaleBox'),
+ LocatePlugin: require('../plugins/Locate'),
+ ZoomInPlugin: require('../plugins/ZoomIn'),
+ ZoomOutPlugin: require('../plugins/ZoomOut'),
+ ZoomAllPlugin: require('../plugins/ZoomAll'),
+ MapLoadingPlugin: require('../plugins/MapLoading'),
+ AboutPlugin: require('./plugins/About'),
+ HelpPlugin: require('../plugins/Help'),
+ MadeWithLovePlugin: require('./plugins/MadeWithLove'),
+ MetadataExplorerPlugin: require('../plugins/MetadataExplorer'),
+ BurgerMenuPlugin: require('../plugins/BurgerMenu'),
+ UndoPlugin: require('../plugins/History'),
+ RedoPlugin: require('../plugins/History'),
+ SavePlugin: require('../plugins/Save'),
+ SaveAsPlugin: require('../plugins/SaveAs'),
+ SharePlugin: require('../plugins/Share')
+ });
+ });
+ }
}, {
name: "manager",
- path: "/manager/:tool",
- component: require('./pages/Manager')
+ path: "/manager(/:tool)",
+ component: require('./pages/Manager'),
+ plugins: (resolve) => {
+ return require.ensure([], () => {
+ resolve({
+ UserManagerPlugin: require('../plugins/manager/UserManager'),
+ RulesManagerPlugin: require('../plugins/manager/RulesManager'),
+ ManagerPlugin: require('../plugins/manager/Manager')
+ });
+ });
+ }
}],
pluginsDef: require('./plugins.js'),
initialState: {
diff --git a/web/client/product/plugins.js b/web/client/product/plugins.js
index ac68ddcd33..883220692f 100644
--- a/web/client/product/plugins.js
+++ b/web/client/product/plugins.js
@@ -8,56 +8,15 @@
module.exports = {
plugins: {
- MousePositionPlugin: require('../plugins/MousePosition'),
- PrintPlugin: require('../plugins/Print'),
- IdentifyPlugin: require('../plugins/Identify'),
- TOCPlugin: require('../plugins/TOC'),
- BackgroundSwitcherPlugin: require('../plugins/BackgroundSwitcher'),
- MeasurePlugin: require('../plugins/Measure'),
- MeasureResultsPlugin: require('../plugins/MeasureResults'),
- MapPlugin: require('../plugins/Map'),
- ToolbarPlugin: require('../plugins/Toolbar'),
- DrawerMenuPlugin: require('../plugins/DrawerMenu'),
- ShapeFilePlugin: require('../plugins/ShapeFile'),
- SnapshotPlugin: require('../plugins/Snapshot'),
- SettingsPlugin: require('../plugins/Settings'),
- ExpanderPlugin: require('../plugins/Expander'),
- SearchPlugin: require('../plugins/Search'),
- ScaleBoxPlugin: require('../plugins/ScaleBox'),
- LocatePlugin: require('../plugins/Locate'),
- ZoomInPlugin: require('../plugins/ZoomIn'),
- ZoomOutPlugin: require('../plugins/ZoomOut'),
- ZoomAllPlugin: require('../plugins/ZoomAll'),
- MapLoadingPlugin: require('../plugins/MapLoading'),
- AboutPlugin: require('./plugins/About'),
- HelpPlugin: require('../plugins/Help'),
+ OmniBarPlugin: require('../plugins/OmniBar'),
HomePlugin: require('../plugins/Home'),
- MadeWithLovePlugin: require('./plugins/MadeWithLove'),
- MetadataExplorerPlugin: require('../plugins/MetadataExplorer'),
LoginPlugin: require('../plugins/Login'),
- OmniBarPlugin: require('../plugins/OmniBar'),
- BurgerMenuPlugin: require('../plugins/BurgerMenu'),
- UndoPlugin: require('../plugins/History'),
- RedoPlugin: require('../plugins/History'),
- MapsPlugin: require('../plugins/Maps'),
- MapSearchPlugin: require('../plugins/MapSearch'),
- HomeDescriptionPlugin: require('./plugins/HomeDescription'),
- ExamplesPlugin: require('./plugins/Examples'),
- MapTypePlugin: require('./plugins/MapType'),
+ ManagerMenuPlugin: require('../plugins/manager/ManagerMenu'),
+ RedirectPlugin: require('../plugins/Redirect'),
LanguagePlugin: require('../plugins/Language'),
AttributionPlugin: require('./plugins/Attribution'),
HeaderPlugin: require('./plugins/Header'),
- ForkPlugin: require('./plugins/Fork'),
- FooterPlugin: require('./plugins/Footer'),
- ManagerPlugin: require('../plugins/manager/Manager'),
- UserManagerPlugin: require('../plugins/manager/UserManager'),
- RulesManagerPlugin: require('../plugins/manager/RulesManager'),
- ManagerMenuPlugin: require('../plugins/manager/ManagerMenu'),
- RedirectPlugin: require('../plugins/Redirect'),
- SharePlugin: require('../plugins/Share'),
- SavePlugin: require('../plugins/Save'),
- SaveAsPlugin: require('../plugins/SaveAs'),
- CreateNewMapPlugin: require('../plugins/CreateNewMap')
+ FooterPlugin: require('./plugins/Footer')
},
requires: {
ReactSwipe: require('react-swipeable-views').default,
diff --git a/web/client/reducers/__tests__/plugins-test.js b/web/client/reducers/__tests__/plugins-test.js
new file mode 100644
index 0000000000..71d536d4cd
--- /dev/null
+++ b/web/client/reducers/__tests__/plugins-test.js
@@ -0,0 +1,17 @@
+/**
+ * Copyright 2015, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+const expect = require('expect');
+
+const plugins = require('../plugins');
+
+describe('Test the plugins reducer', () => {
+ it('load plugins', () => {
+ const state = plugins({}, {type: 'LOAD_PLUGINS', plugins: {MyPlugin: {}}});
+ expect(state.MyPlugin).toExist();
+ });
+});
diff --git a/web/client/reducers/plugins.js b/web/client/reducers/plugins.js
new file mode 100644
index 0000000000..a372dc0bb1
--- /dev/null
+++ b/web/client/reducers/plugins.js
@@ -0,0 +1,24 @@
+/**
+ * Copyright 2016, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+var { LOAD_PLUGINS } = require('../actions/plugins');
+var assign = require('object-assign');
+
+function plugins(state = {}, action) {
+ switch (action.type) {
+ case LOAD_PLUGINS: {
+ return assign({}, state,
+ action.plugins
+ );
+ }
+ default:
+ return state;
+ }
+}
+
+module.exports = plugins;
diff --git a/web/client/stores/StandardStore.js b/web/client/stores/StandardStore.js
index 5ee43d734a..d377d616c7 100644
--- a/web/client/stores/StandardStore.js
+++ b/web/client/stores/StandardStore.js
@@ -31,6 +31,7 @@ module.exports = (initialState = {defaultState: {}, mobile: {}}, appReducers = {
browser: require('../reducers/browser'),
controls: require('../reducers/controls'),
help: require('../reducers/help'),
+ plugins: require('../reducers/plugins'),
map: () => {return null; },
mapInitialConfig: () => {return null; },
layers: () => {return null; }
@@ -51,15 +52,24 @@ module.exports = (initialState = {defaultState: {}, mobile: {}}, appReducers = {
if (action && action.type === CHANGE_BROWSER_PROPERTIES && newState.browser.mobile) {
newState = assign(newState, mobileOverride);
}
-
+ if (action.type === '@@redux/INIT') {
+ Object.keys(newState).forEach((key) => {
+ if (defaultState[key]) {
+ newState[key] = assign(newState[key], defaultState[key]);
+ }
+ });
+ }
return newState;
};
+ if (storeOpts.updateReducers) {
+ return rootReducer;
+ }
let store;
if (storeOpts && storeOpts.persist) {
- store = DebugUtils.createDebugStore(rootReducer, defaultState, [], autoRehydrate());
+ store = DebugUtils.createDebugStore(rootReducer, {}, [], autoRehydrate());
persistStore(store, storeOpts.persist, storeOpts.onPersist);
} else {
- store = DebugUtils.createDebugStore(rootReducer, defaultState);
+ store = DebugUtils.createDebugStore(rootReducer, {});
}
SecurityUtils.setStore(store);
return store;