diff --git a/docma-config.json b/docma-config.json
index 848891deb7..cbaeebb585 100644
--- a/docma-config.json
+++ b/docma-config.json
@@ -127,7 +127,8 @@
"web/client/epics/search.js",
"web/client/utils/index.jsdoc",
- "web/client/utils/PluginsUtils.js"
+ "web/client/utils/PluginsUtils.js",
+ "web/client/utils/ShareUtils.js"
],
"jsapi": "web/client/jsapi/MapStore2.js",
"plugins": [
diff --git a/web/client/actions/fullscreen.js b/web/client/actions/fullscreen.js
index 535fe706ab..b0a2ed03dd 100644
--- a/web/client/actions/fullscreen.js
+++ b/web/client/actions/fullscreen.js
@@ -10,6 +10,7 @@
const TOGGLE_FULLSCREEN = "TOGGLE_FULLSCREEN";
/**
* when fullscreen have to be toggled
+ * @memberof actions.fullscreen
* @param {boolean} enable true for enable, false for disable
* @param {string} elementSelector querySelector string to use to get the element to fullscreen.
* @return {action} the action of type `TOGGLE_FULLSCREEN` with enable flag and element selector.
@@ -21,7 +22,10 @@ function toggleFullscreen(enable, elementSelector) {
elementSelector
};
}
-
+/**
+ * Actions for FullScreen Plugin.
+ * @name actions.fullscreen
+ */
module.exports = {
toggleFullscreen,
TOGGLE_FULLSCREEN
diff --git a/web/client/components/share/ShareEmbed.jsx b/web/client/components/share/ShareEmbed.jsx
index 5ee585fb4a..8ea237836b 100644
--- a/web/client/components/share/ShareEmbed.jsx
+++ b/web/client/components/share/ShareEmbed.jsx
@@ -24,10 +24,23 @@ require('./share.css');
const ShareEmbed = React.createClass({
propTypes: {
- shareUrl: React.PropTypes.string
+ shareUrl: React.PropTypes.string,
+ showTOCToggle: React.PropTypes.bool
},
- getInitialState() {
- return {copied: false, forceDrawer: false};
+ getInitialState() {
+ return {copied: false, forceDrawer: false};
+ },
+ getDefaultProps() {
+ return {
+ showTOCToggle: true
+ };
+ },
+ renderTools() {
+ if (this.props.showTOCToggle) {
+ return ( this.setState({forceDrawer: !this.state.forceDrawer})}>
+
+ );
+ }
},
render() {
@@ -44,16 +57,12 @@ const ShareEmbed = React.createClass({
);
return (
-
-
- this.setState({forceDrawer: !this.state.forceDrawer})}>
-
-
+ {this.renderTools()}
diff --git a/web/client/components/share/SharePanel.jsx b/web/client/components/share/SharePanel.jsx
index 764ebd46d0..689d4b7b9b 100644
--- a/web/client/components/share/SharePanel.jsx
+++ b/web/client/components/share/SharePanel.jsx
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright 2016, GeoSolutions Sas.
* All rights reserved.
*
@@ -6,13 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
- /** DESCRIPTION
- * SharePanel allow to share the current map in some different ways.
- * You can share it on socials networks(facebook,twitter,google+,linkedin)
- * copying the direct link
- * copying the embedded code
- * using the QR code with mobile apps
- */
const React = require('react');
const Dialog = require('../misc/Dialog');
@@ -24,15 +17,36 @@ const ShareQRCode = require('./ShareQRCode');
const {Glyphicon, Tabs, Tab} = require('react-bootstrap');
const Message = require('../../components/I18N/Message');
+ /**
+ * SharePanel allow to share the current map in some different ways.
+ * You can share it on socials networks(facebook,twitter,google+,linkedin)
+ * copying the direct link
+ * copying the embedded code
+ * using the QR code with mobile apps
+ * @class
+ * @memberof components.share
+ * @prop {boolean} [isVisible] display or hide
+ * @prop {node} [title] the title of the page
+ * @prop {string} [shareUrl] the url to use for share. by default location.href
+ * @prop {string} [shareUrlRegex] reqular expression to parse the shareUrl to generate the final url, using shareUrlReplaceString
+ * @prop {string} [shareUrlReplaceString] expression to be replaced by groups of the shareUrlRegex to get the final shareUrl to use for the iframe
+ * @prop {string} [shareApiUrl] url for share API part
+ * @prop {string} [shareConfigUrl] the url of the config to use for shareAPI
+ * @prop {function} [onClose] function to call on close window event.
+ * @prop {getCount} [getCount] function used to get the count for social links.
+ */
let SharePanel = React.createClass({
propTypes: {
isVisible: React.PropTypes.bool,
title: React.PropTypes.node,
shareUrl: React.PropTypes.string,
- shareEmbeddedUrl: React.PropTypes.string,
+ shareUrlRegex: React.PropTypes.string,
+ shareUrlReplaceString: React.PropTypes.string,
shareApiUrl: React.PropTypes.string,
shareConfigUrl: React.PropTypes.string,
+ embedOptions: React.PropTypes.object,
+ showAPI: React.PropTypes.bool,
onClose: React.PropTypes.func,
getCount: React.PropTypes.func,
closeGlyph: React.PropTypes.string
@@ -41,19 +55,33 @@ let SharePanel = React.createClass({
return {
title: ,
onClose: () => {},
+ shareUrlRegex: "(h[^#]*)#\\/viewer\\/([^\\/]*)\\/([A-Za-z0-9]*)",
+ shareUrlReplaceString: "$1embedded.html#/$3",
+ embedOptions: {},
+ showAPI: true,
closeGlyph: "1-close"
};
},
+ generateUrl(orig = location.href, pattern, replaceString) {
+ let regexp = new RegExp(pattern);
+ if (orig.match(regexp)) {
+ return orig.replace(regexp, replaceString);
+ }
+ return orig;
+ },
render() {
// ************************ CHANGE URL PARAMATER FOR EMBED CODE ****************************
/* if the property shareUrl is not defined it takes the url from location.href */
const shareUrl = this.props.shareUrl || location.href;
- const shareEmbeddedUrl = this.props.shareEmbeddedUrl || this.props.shareUrl || location.href;
+ let shareEmbeddedUrl = this.props.shareUrl || location.href;
+ if (this.props.shareUrlRegex && this.props.shareUrlReplaceString) {
+ shareEmbeddedUrl = this.generateUrl(shareEmbeddedUrl, this.props.shareUrlRegex, this.props.shareUrlReplaceString);
+ }
const shareApiUrl = this.props.shareApiUrl || this.props.shareUrl || location.href;
const social = ;
const direct = (
);
- const code = (
-
);
+ const code = (
+ {this.props.showAPI ? : null}
);
const tabs = (
}>{direct}
diff --git a/web/client/components/share/__tests__/ShareEmbed-test.jsx b/web/client/components/share/__tests__/ShareEmbed-test.jsx
index 5286dcdc2b..32bf76c690 100644
--- a/web/client/components/share/__tests__/ShareEmbed-test.jsx
+++ b/web/client/components/share/__tests__/ShareEmbed-test.jsx
@@ -10,6 +10,7 @@ const expect = require('expect');
const React = require('react');
const ReactDOM = require('react-dom');
const ShareEmbed = require('../ShareEmbed');
+const {head} = require('lodash');
const ReactTestUtils = require('react-addons-test-utils');
describe("The ShareEmbed component", () => {
@@ -41,5 +42,27 @@ describe("The ShareEmbed component", () => {
expect(textareaEmbed.value).toEqual(iFrameStr);
});
-
+ it('test forceDrawer', () => {
+ const host = "http://localhost:8081/";
+ const hashPart = "#/abc/def/1";
+ let expectedParam = "?forceDrawer=true";
+ const iFrameStr = "";
+ const cmpSharePanel = ReactDOM.render(, document.getElementById("container"));
+ const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag(cmpSharePanel, "input");
+ let checkbox = head(inputs.filter(i => i.type === "checkbox"));
+ expect(checkbox.checked).toBe(false);
+ ReactTestUtils.Simulate.change(checkbox);
+ const textareaEmbed = ReactDOM.findDOMNode(ReactTestUtils.scryRenderedDOMComponentsWithTag(cmpSharePanel, "textarea")[0]);
+ expect(checkbox.checked).toBe(true);
+ expect(textareaEmbed).toExist();
+ expect(textareaEmbed.value).toEqual(iFrameStr);
+ });
+ it('test showTOCToggle prop', () => {
+ const host = "http://localhost:8081/";
+ const hashPart = "#/abc/def/1";
+ const cmpSharePanel = ReactDOM.render(, document.getElementById("container"));
+ const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag(cmpSharePanel, "input");
+ let checkboxes = inputs.filter(i => i.type === "checkbox");
+ expect(checkboxes.length).toBe(0);
+ });
});
diff --git a/web/client/components/share/__tests__/SharePanel-test.jsx b/web/client/components/share/__tests__/SharePanel-test.jsx
index 2169aec5d1..4c752648b3 100644
--- a/web/client/components/share/__tests__/SharePanel-test.jsx
+++ b/web/client/components/share/__tests__/SharePanel-test.jsx
@@ -10,7 +10,7 @@ const expect = require('expect');
const React = require('react');
const ReactDOM = require('react-dom');
const SharePanel = require('../SharePanel');
-
+const ReactTestUtils = require('react-addons-test-utils');
describe("The SharePanel component", () => {
beforeEach((done) => {
@@ -44,7 +44,22 @@ describe("The SharePanel component", () => {
expect(cmpSharePanel).toExist();
const cmpSharePanelDom = ReactDOM.findDOMNode(cmpSharePanel);
expect(cmpSharePanelDom).toBeFalsy();
-
});
+ it('test regex parsing for shareEmbeddedUrl generation', () => {
+ const cmpSharePanel = ReactDOM.render(0} shareUrlRegex=".*" shareUrlReplaceString="ABC" shareUrl="www.geo-solutions.it" isVisible={false} />, document.getElementById("container"));
+ expect(cmpSharePanel).toExist();
+ const parsed = cmpSharePanel.generateUrl("TEST", "(TE)ST", "$1");
+ expect(parsed).toBe("TE");
+ });
+ it('test showAPI flag', () => {
+ let cmpSharePanel = ReactDOM.render(0} shareUrl="www.geo-solutions.it" isVisible={true} />, document.getElementById("container"));
+ expect(cmpSharePanel).toExist();
+ let textareaEmbed = ReactTestUtils.scryRenderedDOMComponentsWithTag(cmpSharePanel, "textarea");
+ expect(textareaEmbed.length).toBe(1);
+ cmpSharePanel = ReactDOM.render(0} shareUrl="www.geo-solutions.it" isVisible={true} />, document.getElementById("container"));
+ textareaEmbed = ReactTestUtils.scryRenderedDOMComponentsWithTag(cmpSharePanel, "textarea");
+ expect(textareaEmbed.length).toBe(2);
+ });
+
});
diff --git a/web/client/plugins/Share.jsx b/web/client/plugins/Share.jsx
index 01cee3229c..a67e24dfe0 100644
--- a/web/client/plugins/Share.jsx
+++ b/web/client/plugins/Share.jsx
@@ -6,13 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
- /************** DESCRIPTION OF COMPONENT **************
- The share plugin should provide functionalities to:
- 1. Share the map on social networks: Facebook, Twitter (linkedin and Google+ is a plus)
- 2. Copy the unique link to the map.
- 3. Copy a code to embed the map in your site (using an iframe).
- 4. Using QR-Code for mobile devices.
-*/
const React = require('react');
@@ -22,35 +15,30 @@ const {Glyphicon} = require('react-bootstrap');
const Message = require('../components/I18N/Message');
const {toggleControl} = require('../actions/controls');
const ConfigUtils = require('../utils/ConfigUtils');
+const ShareUtils = require('../utils/ShareUtils');
-const Url = require('url');
-
-
-const getEmbeddedUrl = (url) => {
- let urlParsedObj = Url.parse(url, true);
-
- return urlParsedObj.protocol + '//' + urlParsedObj.host + urlParsedObj.path + "embedded.html#/" +
- urlParsedObj.hash.substring(urlParsedObj.hash.lastIndexOf('/') + 1, urlParsedObj.hash.lastIndexOf('?'));
-};
-
-const getApiUrl = (url) => {
- let urlParsedObj = Url.parse(url, true);
-
- return urlParsedObj.protocol + '//' + urlParsedObj.host + urlParsedObj.path;
-};
-
-const getConfigUrl = (url) => {
- let urlParsedObj = Url.parse(url, true);
-
- return urlParsedObj.protocol + '//' + (urlParsedObj.host + urlParsedObj.path + ConfigUtils.getConfigProp('geoStoreUrl') + 'data/' + urlParsedObj.hash.substring(urlParsedObj.hash.lastIndexOf('/') + 1, urlParsedObj.hash.lastIndexOf('?'))).replace('//', '/');
-};
-
+/**
+ * Share Plugin allows to share the current URL (location.href) in some different ways.
+ * You can share it on socials networks(facebook,twitter,google+,linkedin)
+ * copying the direct link
+ * copying the embedded code
+ * using the QR code with mobile apps
+ * @class
+ * @memberof plugins
+ * @prop {node} [title] the title of the page
+ * @prop {string} [shareUrlRegex] reqular expression to parse the shareUrl to generate the final url, using shareUrlReplaceString
+ * @prop {string} [shareUrlReplaceString] expression to be replaced by groups of the shareUrlRegex to get the final shareUrl to use for the iframe
+ * @prop {object} [embedOptions] options for the iframe version of embedded share options
+ * @prop {boolean} [embedOptions.showTOCToggle] true by default, set to false to hide the "show TOC" toggle.
+ * @prop {boolean} [showAPI] default true, if false, hides the API entry of embed.
+ * @prop {function} [onClose] function to call on close window event.
+ * @prop {getCount} [getCount] function used to get the count for social links.
+ */
const Share = connect((state) => ({
isVisible: state.controls && state.controls.share && state.controls.share.enabled,
shareUrl: location.href,
- shareEmbeddedUrl: getEmbeddedUrl(location.href),
- shareApiUrl: getApiUrl(location.href),
- shareConfigUrl: getConfigUrl(location.href)
+ shareApiUrl: ShareUtils.getApiUrl(location.href),
+ shareConfigUrl: ShareUtils.getConfigUrl(location.href, ConfigUtils.getConfigProp('geoStoreUrl'))
}), {
onClose: toggleControl.bind(null, 'share', null)
})(require('../components/share/SharePanel'));
diff --git a/web/client/utils/ShareUtils.js b/web/client/utils/ShareUtils.js
new file mode 100644
index 0000000000..3ee231445c
--- /dev/null
+++ b/web/client/utils/ShareUtils.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017, 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 Url = require('url');
+
+/**
+ * Utility functions for Share tools
+ * @memberof utils
+ * @type {Object}
+ */
+var ShareUtils = {
+ /**
+ * get the absolute URL from the local url and the url to convert
+ * @param {string} localUrl the context where to evaluate the URL, typically location.href
+ * @param {string} urlToConvert the url to convert
+ * @return {string} the absolute url of the urlToConvert
+ */
+ getAbsoluteURL: (localUrl, urlToConvert) => {
+ // case absolute URL
+ if (urlToConvert.indexOf("http") === 0 || urlToConvert.indexOf("//") === 0) {
+ return urlToConvert;
+ }
+ return Url.resolve(localUrl, urlToConvert);
+ },
+ /**
+ * get the url for the configuration in GeoStore parsing the hash string (`#/viewer/{maptype}/1`)
+ * @param {string} url the context where to evaluate the URL, typically location.href
+ * @param {string} geoStoreUrl the Base URL of GeoStore
+ * @return {string} the absolute url of the GeoStore Resource
+ */
+ getConfigUrl: (url, geoStoreUrl) => {
+ let urlParsedObj = Url.parse(url, true);
+ if (!urlParsedObj.hash) {
+ return null;
+ }
+ const start = urlParsedObj.hash.lastIndexOf('/') + 1;
+ const end = urlParsedObj.hash.lastIndexOf('?') >= 0 ? urlParsedObj.hash.lastIndexOf('?') : urlParsedObj.hash.length;
+ const mapId = urlParsedObj.hash.substring(start, end);
+ return Url.resolve(ShareUtils.getAbsoluteURL(url, geoStoreUrl), ('data/' + mapId));
+ },
+ /**
+ * Parses the API url to get the proper base path where to retrieve the js for the api.
+ * @param {string} url the current context
+ * @return {string} the base path of mapstore where to retrieve the js api.
+ */
+ getApiUrl: (url) => {
+ let urlParsedObj = Url.parse(url, false);
+ return urlParsedObj.protocol + '//' + urlParsedObj.host + urlParsedObj.pathname;
+ }
+};
+
+module.exports = ShareUtils;
diff --git a/web/client/utils/__tests__/ShareUtils-test.js b/web/client/utils/__tests__/ShareUtils-test.js
new file mode 100644
index 0000000000..44b47d01aa
--- /dev/null
+++ b/web/client/utils/__tests__/ShareUtils-test.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017, 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 expect = require('expect');
+var ShareUtils = require('../ShareUtils');
+
+
+const MAPSTORE_PATH = "/mapstore/";
+const MAP_HASH_PATH = "#/viewer/leaflet/";
+const GEOSTORE_DATA_PATH = "data/";
+const QUERY_STRING = "?config=myCustomConfig";
+const MS_GEOSTORE_EMBEDDED_PATH = "/mapstore/rest/geostore/";
+const STANDALONE_GEOSTORE_PATH = "/geostore/rest/";
+
+const LOCALURL = "http://localhost:8081";
+const SOMEHOST = "http://somehost.it";
+const DEV_URL = "http://dev.mapstore2.geo-solutions.it";
+
+const LOCALURL_PATH = LOCALURL + MAPSTORE_PATH;
+const DEV_URL_PATH = DEV_URL + MAPSTORE_PATH;
+const DEV_URL_MAP_PATH = DEV_URL_PATH + MAP_HASH_PATH;
+const SOMEHOST_PATH = SOMEHOST + MAPSTORE_PATH;
+const SOMEHOST_PATH_QUERY_STRING = SOMEHOST_PATH + QUERY_STRING;
+const EXTERNAL_GEOSTORE = "http://dev.mapstore2.geo-solutions.it/geostore/rest/";
+
+describe('ShareUtils test', () => {
+ it('getAbsoluteURL', () => {
+ expect(ShareUtils.getAbsoluteURL(LOCALURL, MS_GEOSTORE_EMBEDDED_PATH)).toBe( LOCALURL + MS_GEOSTORE_EMBEDDED_PATH );
+ expect(ShareUtils.getAbsoluteURL(DEV_URL, MS_GEOSTORE_EMBEDDED_PATH)).toBe(DEV_URL + MS_GEOSTORE_EMBEDDED_PATH);
+ expect(ShareUtils.getAbsoluteURL(DEV_URL_PATH, MS_GEOSTORE_EMBEDDED_PATH)).toBe(DEV_URL + MS_GEOSTORE_EMBEDDED_PATH);
+ expect(ShareUtils.getAbsoluteURL(SOMEHOST_PATH, STANDALONE_GEOSTORE_PATH)).toBe(SOMEHOST + STANDALONE_GEOSTORE_PATH);
+ expect(ShareUtils.getAbsoluteURL(SOMEHOST_PATH_QUERY_STRING, STANDALONE_GEOSTORE_PATH)).toBe(SOMEHOST + STANDALONE_GEOSTORE_PATH);
+ expect(ShareUtils.getAbsoluteURL(SOMEHOST_PATH_QUERY_STRING, EXTERNAL_GEOSTORE)).toBe(EXTERNAL_GEOSTORE);
+ });
+ it('getConfigUrl', () => {
+ expect(ShareUtils.getConfigUrl(DEV_URL_MAP_PATH + "1", MS_GEOSTORE_EMBEDDED_PATH)).toBe(DEV_URL + MS_GEOSTORE_EMBEDDED_PATH + GEOSTORE_DATA_PATH + "1");
+ expect(ShareUtils.getConfigUrl(DEV_URL_MAP_PATH + "11", MS_GEOSTORE_EMBEDDED_PATH)).toBe(DEV_URL + MS_GEOSTORE_EMBEDDED_PATH + GEOSTORE_DATA_PATH + "11");
+ expect(ShareUtils.getConfigUrl(DEV_URL_MAP_PATH + "111", MS_GEOSTORE_EMBEDDED_PATH)).toBe(DEV_URL + MS_GEOSTORE_EMBEDDED_PATH + GEOSTORE_DATA_PATH + "111");
+ expect(ShareUtils.getConfigUrl(DEV_URL_MAP_PATH + "111?abc=def", MS_GEOSTORE_EMBEDDED_PATH)).toBe(DEV_URL + MS_GEOSTORE_EMBEDDED_PATH + GEOSTORE_DATA_PATH + "111");
+ expect(ShareUtils.getConfigUrl(DEV_URL_MAP_PATH, MS_GEOSTORE_EMBEDDED_PATH)).toBe(DEV_URL + MS_GEOSTORE_EMBEDDED_PATH + GEOSTORE_DATA_PATH);
+ expect(ShareUtils.getConfigUrl(SOMEHOST_PATH, MS_GEOSTORE_EMBEDDED_PATH)).toBe(null);
+ });
+ it('getApiUrl', () => {
+ expect(ShareUtils.getApiUrl(DEV_URL_MAP_PATH)).toBe(DEV_URL_PATH);
+ expect(ShareUtils.getApiUrl(LOCALURL_PATH)).toBe(LOCALURL_PATH);
+ expect(ShareUtils.getApiUrl(LOCALURL_PATH + MAPSTORE_PATH + QUERY_STRING)).toBe(LOCALURL_PATH + MAPSTORE_PATH);
+ });
+});