Skip to content

Commit

Permalink
[SDESK-5220] Modal to show Highcharts license (superdesk#127)
Browse files Browse the repository at this point in the history
* [SDESK-5220] Modal to show Highcharts license

* Add client_config to server

* Add config instructions to README

* Update eslint package and config

* Fix behave test

* Improve highcharts client config structure
  • Loading branch information
MarkLark86 committed Jun 3, 2020
1 parent ddfb09c commit f2e76fc
Show file tree
Hide file tree
Showing 13 changed files with 309 additions and 15 deletions.
64 changes: 63 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1,63 @@
module.exports = require('superdesk-code-style');
const sharedConfigs = require('superdesk-code-style');

module.exports = Object.assign({}, sharedConfigs, {
rules: Object.assign(sharedConfigs.rules, {
'no-nested-ternary': 0,
'no-unused-vars': 0, // marks typescript interfaces as unused vars
'no-undef': 0, // marks interface properties as usages of undeclared variables

// field names from back-end use camel-case for naming.
// I'm not convinced it's worth using a bracket notation only to satisfy a lint rule
'camelcase': 0,

// doesn't make sense with many properties
// 10 may use shorthand and one may be inline or reference differently named variable
'object-shorthand': 0,

// can make functions harder to read; forces into rewriting the function to insert a debugger
'arrow-body-style': 0,

// leaving up to developers. I prefer to quote external strings like css names,
// but keep internal properties unquoted unless required
'quote-props': 0,

'newline-per-chained-call': ["error", {"ignoreChainWithDepth": 3}],
}),
parser: '@typescript-eslint/parser',
overrides: [
{
files: ['*.ts', '*.tsx'],
rules: {
'react/prop-types': 0, // interfaces are used in TypeScript files
'no-unused-vars': 0,
'no-undef': 0,
'comma-dangle': 0,
'camelcase': 0,
'object-shorthand': 0,
'arrow-body-style': 0,
'newline-per-chained-call': 0,
'quote-props': 0,
'arrow-body-style': 0,
'max-len': 0, // handled by tslint

"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "always-multiline"
}],


// allow calling hasOwnProperty
"no-prototype-builtins": 0,
},
},
{
files: ['*.d.ts'],
rules: {
'spaced-comment': 0,
},
},
],
});
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The Highcharts JS library is used to render the charts. It is available under di
* [Client](#client-enable-the-superdesk-analytics-module)
* [Server](#server-load-the-superdesk-analytics-module)
* [Development](#development-setup)
* [Highcharts License](#highcharts-license)
* [Config Options](#config-options)
* [Highcharts Export Server](#highcharts-export-server)
* [Installing the Service](#installing-the-service)
Expand Down Expand Up @@ -117,6 +118,21 @@ pip install -e /path/to/superdesk-analytics
```


## Highcharts License
You must have a valid license for [Highcharts](https://www.highcharts.com/) JS v6.x to use this plugin.

The Highcharts JS library is used to render the charts. It is available under different licenses depending on whether it is intended for commercial/government use, or for a personal or non-profit project.

To have the license details available to the end user in the Analytics page of Superdesk, provide the following config options in your settings.py:

* HIGHCHARTS_LICENSE_TYPE (High-Five, Develop or OEM)
* HIGHCHARTS_LICENSEE (Name of the entity that owns the license)
* HIGHCHARTS_LICENSEE_CONTACT (A contact email address for the licensee)
* HIGHCHARTS_LICENSE_ID (The license ID provided by Highsoft)
* HIGHCHARTS_LICENSE_CUSTOMER_ID (A custom license field to use an internal customer number, if required)
* HIGHCHARTS_LICENSE_EXPIRY (the expiry of the license)


## Config Options
* HIGHCHARTS_SERVER_HOST (defaults to 'localhost')
* HIGHCHARTS_SERVER_PORT (defaults to '6060')
Expand Down
126 changes: 126 additions & 0 deletions client/components/HighchartsLicense.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/* eslint-disable react/no-multi-comp */

import React from 'react';

import {appConfig} from 'appConfig';

import hc from 'highcharts';

import {IAnalyticsConfig} from '../interfaces';
import {superdeskApi} from '../superdeskApi';

interface IProps {
closeModal(): void
}

class HighchartsLicenseModal extends React.PureComponent<IProps> {
render() {
const {
Modal,
ModalBody,
ModalFooter,
} = superdeskApi.components;
const gettext = superdeskApi.localization.gettext;
const config: IAnalyticsConfig = appConfig as IAnalyticsConfig;
const highchartsVersionLink = `https://www.highcharts.com/blog/changelog/#highcharts-v${hc.version}`;
const license = config.highcharts?.license ?? {};
const licenseType = license.type ?? gettext('OEM');

return (
<Modal>
<div className="modal__header modal__header--about">
<button className="modal__close pull-right" onClick={this.props.closeModal}>
<i className="icon-close-small" />
</button>
<h2 style={{color: 'white'}}>{gettext('Highcharts {{licenseType}} License', {licenseType})}</h2>
</div>
<ModalBody>
<div>
<p>{gettext('The use of Highcharts is provided under a license with the following details:')}</p>
</div>
<div>
<table>
<tbody>
<tr>
<td>{gettext('License Type')}:</td>
<td>{licenseType}</td>
</tr>
{license.licensee && (
<tr>
<td>{gettext('Licensee:')}</td>
<td>{license.licensee}</td>
</tr>
)}
{license.contact && (
<tr>
<td>{gettext('Licensee Contact:')}</td>
<td>
<a href={`mailto:${license.contact}`}
target="_blank"
rel="noopener noreferrer"
>
{license.contact}
</a>
</td>
</tr>
)}
{license.id && (
<tr>
<td>{gettext('License ID:')}</td>
<td>{license.id}</td>
</tr>
)}
{license.customer_id && (
<tr>
<td>{gettext('Customer Installation No.:')}</td>
<td>{license.customer_id}</td>
</tr>
)}
{license.expiry && (
<tr>
<td>{gettext('Expiry Date:')}</td>
<td>{license.expiry}</td>
</tr>
)}
<tr>
<td>{gettext('Installed Version:')}</td>
<td>
<a href={highchartsVersionLink}
target="_blank"
rel="noopener noreferrer"
>
v{hc.version}
</a>
</td>
</tr>
</tbody>
</table>
</div>
</ModalBody>
<ModalFooter>
<button className="btn btn--primary" onClick={this.props.closeModal} >
{gettext('Close')}
</button>
</ModalFooter>
</Modal>
);
}
}

function showHighchartsModal(): void {
superdeskApi.ui.showModal(HighchartsLicenseModal);
}

export class HighchartsLicense extends React.PureComponent {
render() {
return (
<button className="btn btn--success btn--icon-only btn--hollow"
data-sd-tooltip="Highcharts License"
data-flow="left"
onClick={showHighchartsModal}
>
<i className="icon-info-sign" />
</button>
);
}
}
1 change: 1 addition & 0 deletions client/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {HighchartsLicense} from './HighchartsLicense';
53 changes: 52 additions & 1 deletion client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@
* at https://www.sourcefabric.org/superdesk/license
*/

import {reactToAngular1} from 'superdesk-ui-framework';
import {getSuperdeskApiImplementation} from 'superdesk-core/scripts/core/get-superdesk-api-implementation';
import {superdeskApi} from './superdeskApi';
import {appConfig, extensions} from 'appConfig';
import ng from 'superdesk-core/scripts/core/services/ng';

import {gettext} from './utils';

import './styles/analytics.scss';
import * as svc from './services';
import * as directive from './directives';
import {HighchartsLicense} from './components';

// Base services/directives
import './charts';
Expand Down Expand Up @@ -73,6 +80,11 @@ export default angular.module('superdesk.analytics', [
.directive('sdaArchivePreviewProxy', directive.ArchivePreviewProxy)
.directive('sdaRenditionsPreview', directive.RenditionsPreview)

.component(
'sdHighchartsLicense',
reactToAngular1(HighchartsLicense, [])
)

.service('reportConfigs', svc.ReportConfigService)

.run(cacheIncludedTemplates)
Expand All @@ -92,4 +104,43 @@ export default angular.module('superdesk.analytics', [
return reports.length > 0;
}],
});
}]);
}])

.run([
'$injector',
'modal',
'privileges',
'lock',
'session',
'authoringWorkspace',
'metadata',
(
$injector,
modal,
privileges,
lock,
session,
authoringWorkspace,
metadata
) => {
ng.register($injector);

ng.waitForServicesToBeAvailable()
.then(() => {
Object.assign(
superdeskApi,
getSuperdeskApiImplementation(
null,
extensions,
modal,
privileges,
lock,
session,
authoringWorkspace,
metadata,
appConfig
)
);
});
},
]);
14 changes: 14 additions & 0 deletions client/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {ISuperdeskGlobalConfig} from 'superdesk-api';

export interface IAnalyticsConfig extends ISuperdeskGlobalConfig {
highcharts?: {
license?: {
id?: string;
type?: string;
licensee?: string;
contact?: string;
customer_id?: string;
expiry?: string;
};
};
}
9 changes: 9 additions & 0 deletions client/superdeskApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {ISuperdesk} from 'superdesk-api';

// will be set asynchronously on planning module start
// members can't be accessed in root module scope synchronously

// DO NOT USE OUTSIDE .ts OR .tsx FILES
// because it would make it harder to find and update usages when API changes

export const superdeskApi = {} as ISuperdesk;
1 change: 1 addition & 0 deletions client/typings/refs.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference path='../../node_modules/superdesk-core/scripts/core/superdesk-api.d.ts' />
1 change: 1 addition & 0 deletions client/views/analytics.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<i class="icon-plus-sign icon--white"></i>
Add New
</button>
<sd-highcharts-license></sd-highcharts-license>
</div>
</div>

Expand Down
21 changes: 11 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,21 @@
"dependencies": {
"highcharts": "^6.2.0",
"twix": "^1.1.5",
"@types/lodash": "4.14.116",
"@types/lodash": "4.14.117",
"ts-loader": "3.5.0"
},
"devDependencies": {
"eslint": "^4.19.1",
"eslint-plugin-jasmine": "^1.8.1",
"@typescript-eslint/parser": "2.6.1",
"angular-embed": "github:superdesk/angular-embed#d75968e",
"angular-embedly": "github:Urigo/angular-embedly#0.0.8",
"eslint": "6.6.0",
"eslint-plugin-angular": "^1.6.1",
"eslint-plugin-jasmine": "^1.8.1",
"eslint-plugin-react": "7.16.0",
"jasmine-core": "^2.99.1",
"jasmine-reporters": "^2.3.0",
"jquery": "^3.4.1",
"jquery.gridster": "github:dustmoo/gridster.js#c306335397816beceb74e4a176067baef34a0359",
"karma": "^2.0.0",
"karma-chrome-launcher": "^2.2.0",
"karma-jasmine": "^1.1.1",
Expand All @@ -38,13 +44,8 @@
"karma-webpack": "^2.0.13",
"lodash": "^4.17.5",
"moment-timezone": "^0.5.14",
"superdesk-code-style": "^1.2.0",
"superdesk-core": "github:superdesk/superdesk-client-core#release/1.33.1",
"angular-embed": "github:superdesk/angular-embed#d75968e",
"angular-embedly": "github:Urigo/angular-embedly#0.0.8",
"jquery": "^3.4.1",
"jquery.gridster": "github:dustmoo/gridster.js#c306335397816beceb74e4a176067baef34a0359",
"rangy": "1.3.0",
"typescript-eslint-parser": "^18.0.0"
"superdesk-code-style": "^1.2.0",
"superdesk-core": "github:superdesk/superdesk-client-core#develop"
}
}
10 changes: 10 additions & 0 deletions server/analytics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ def init_app(app):
init_featuremedia_updates_report(app)
init_update_time_report(app)

app.client_config.setdefault('highcharts', {})
app.client_config['highcharts']['license'] = {
'id': app.config.get('HIGHCHARTS_LICENSE_ID') or '',
'type': app.config.get('HIGHCHARTS_LICENSE_TYPE') or 'OEM',
'licensee': app.config.get('HIGHCHARTS_LICENSEE') or '',
'contact': app.config.get('HIGHCHARTS_LICENSEE_CONTACT') or '',
'customer_id': app.config.get('HIGHCHARTS_LICENSE_CUSTOMER_ID') or '',
'expiry': app.config.get('HIGHCHARTS_LICENSE_EXPIRY') or ''
}

# If this app is for testing, then create an endpoint for the base reporting service
# so the core searching/aggregation functionality can be tested
if app.testing:
Expand Down
Loading

0 comments on commit f2e76fc

Please sign in to comment.