Skip to content

Commit

Permalink
Fix #1750. Add smarter shareURLs generator and tests (#1751)
Browse files Browse the repository at this point in the history
* Fix #1728. Generalized Share embedded strings. More tests (#1729)

* Fix #1738. Made optional share api and showTOC (#1739)

With this options you can remove via configuration the API share tool and the showTOC checkbox.

* Fix #1750.Add smarter shareURLs generator and tests
  • Loading branch information
offtherailz authored and Gnafu committed Apr 20, 2017
1 parent 9ea9d2f commit 05bfc5e
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 57 deletions.
3 changes: 2 additions & 1 deletion docma-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
6 changes: 5 additions & 1 deletion web/client/actions/fullscreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -21,7 +22,10 @@ function toggleFullscreen(enable, elementSelector) {
elementSelector
};
}

/**
* Actions for FullScreen Plugin.
* @name actions.fullscreen
*/
module.exports = {
toggleFullscreen,
TOGGLE_FULLSCREEN
Expand Down
25 changes: 17 additions & 8 deletions web/client/components/share/ShareEmbed.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<Checkbox checked={this.state.forceDrawer} onChange={() => this.setState({forceDrawer: !this.state.forceDrawer})}>
<Message msgId="share.forceDrawer"/>
</Checkbox>);
}
},
render() {

Expand All @@ -44,16 +57,12 @@ const ShareEmbed = React.createClass({
</OverlayTrigger>);
return (
<div className="input-link">


<Grid className="embed-box" fluid={true}>
<Row key="title">
<h4>
<Message msgId="share.embeddedLinkTitle"/>
</h4>
<Checkbox checked={this.state.forceDrawer} onChange={() => this.setState({forceDrawer: !this.state.forceDrawer})}>
<Message msgId="share.forceDrawer"/>
</Checkbox>
{this.renderTools()}
</Row>
<Row key="data" className="row-button">
<Col key="textarea" xs={10} sm={10} md={10}><textarea name="description" rows="6" value={codeEmbedded} enabled="false" readOnly /></Col>
Expand Down
52 changes: 40 additions & 12 deletions web/client/components/share/SharePanel.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
/**
/*
* 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.
*/

/** 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');
Expand All @@ -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
Expand All @@ -41,19 +55,33 @@ let SharePanel = React.createClass({
return {
title: <Message msgId="share.titlePanel"/>,
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 = <ShareSocials shareUrl={shareUrl} getCount={this.props.getCount}/>;
const direct = (<div><ShareLink shareUrl={shareUrl}/><ShareQRCode shareUrl={shareUrl}/></div>);
const code = (<div><ShareEmbed shareUrl={shareEmbeddedUrl}/>
<ShareApi shareUrl={shareApiUrl} shareConfigUrl={this.props.shareConfigUrl}/></div>);
const code = (<div><ShareEmbed shareUrl={shareEmbeddedUrl} {...this.props.embedOptions} />
{this.props.showAPI ? <ShareApi shareUrl={shareApiUrl} shareConfigUrl={this.props.shareConfigUrl}/> : null}</div>);

const tabs = (<Tabs defaultActiveKey={1} id="sharePanel-tabs">
<Tab eventKey={1} title={<Message msgId="share.direct" />}>{direct}</Tab>
Expand Down
25 changes: 24 additions & 1 deletion web/client/components/share/__tests__/ShareEmbed-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down Expand Up @@ -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 = "<iframe style=\"border: none;\" height=\"400\" width=\"600\" src=\"" + host + expectedParam + hashPart + "\"></iframe>";
const cmpSharePanel = ReactDOM.render(<ShareEmbed shareUrl={ host + hashPart }/>, 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(<ShareEmbed showTOCToggle={false} shareUrl={ host + hashPart }/>, document.getElementById("container"));
const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag(cmpSharePanel, "input");
let checkboxes = inputs.filter(i => i.type === "checkbox");
expect(checkboxes.length).toBe(0);
});
});
19 changes: 17 additions & 2 deletions web/client/components/share/__tests__/SharePanel-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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(<SharePanel getCount={()=>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(<SharePanel showAPI={false} getCount={()=>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(<SharePanel showAPI={true} getCount={()=>0} shareUrl="www.geo-solutions.it" isVisible={true} />, document.getElementById("container"));
textareaEmbed = ReactTestUtils.scryRenderedDOMComponentsWithTag(cmpSharePanel, "textarea");
expect(textareaEmbed.length).toBe(2);
});


});
52 changes: 20 additions & 32 deletions web/client/plugins/Share.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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'));
Expand Down
56 changes: 56 additions & 0 deletions web/client/utils/ShareUtils.js
Original file line number Diff line number Diff line change
@@ -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;
Loading

0 comments on commit 05bfc5e

Please sign in to comment.