From 0dfac9875ef6f5f20318a3ef542b11592da8480e Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Mon, 30 May 2016 09:59:53 -0400 Subject: Add license expiry messages (#3153) --- webapp/actions/team_actions.jsx | 2 +- webapp/components/admin_console/banner.jsx | 40 ++++++++++++++ webapp/components/admin_console/ldap_settings.jsx | 37 ++++++------- webapp/components/analytics/system_analytics.jsx | 36 ++++++++++++- webapp/components/error_bar.jsx | 64 +++++++++++++++++++---- webapp/i18n/en.json | 7 ++- webapp/sass/routes/_admin-console.scss | 3 +- webapp/stores/error_store.jsx | 16 +++--- webapp/utils/constants.jsx | 4 +- webapp/utils/license_utils.jsx | 45 ++++++++++++++++ 10 files changed, 209 insertions(+), 45 deletions(-) create mode 100644 webapp/components/admin_console/banner.jsx create mode 100644 webapp/utils/license_utils.jsx diff --git a/webapp/actions/team_actions.jsx b/webapp/actions/team_actions.jsx index 2408e55fd..2cff86b4a 100644 --- a/webapp/actions/team_actions.jsx +++ b/webapp/actions/team_actions.jsx @@ -8,7 +8,7 @@ const ActionTypes = Constants.ActionTypes; import * as AsyncClient from 'utils/async_client.jsx'; import Client from 'utils/web_client.jsx'; -import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; +import AppDispatcher from 'dispatcher/app_dispatcher.jsx'; import {browserHistory} from 'react-router'; diff --git a/webapp/components/admin_console/banner.jsx b/webapp/components/admin_console/banner.jsx new file mode 100644 index 000000000..2071fff93 --- /dev/null +++ b/webapp/components/admin_console/banner.jsx @@ -0,0 +1,40 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +export default class Banner extends React.Component { + render() { + let title = ( + + ); + + if (this.props.title) { + title = this.props.title; + } + + return ( +
+
+

+ {title} +

+

+ {this.props.description} +

+
+
+ ); + } +} + +Banner.defaultProps = { +}; +Banner.propTypes = { + title: React.PropTypes.node, + description: React.PropTypes.node.isRequired +}; diff --git a/webapp/components/admin_console/ldap_settings.jsx b/webapp/components/admin_console/ldap_settings.jsx index d47a1f8c2..80c1a7867 100644 --- a/webapp/components/admin_console/ldap_settings.jsx +++ b/webapp/components/admin_console/ldap_settings.jsx @@ -1,17 +1,18 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import React from 'react'; - -import * as Utils from 'utils/utils.jsx'; - import AdminSettings from './admin_settings.jsx'; +import Banner from './banner.jsx'; import BooleanSetting from './boolean_setting.jsx'; import ConnectionSecurityDropdownSetting from './connection_security_dropdown_setting.jsx'; -import {FormattedMessage} from 'react-intl'; import SettingsGroup from './settings_group.jsx'; import TextSetting from './text_setting.jsx'; +import * as Utils from 'utils/utils.jsx'; + +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + export default class LdapSettings extends AdminSettings { constructor(props) { super(props); @@ -90,22 +91,14 @@ export default class LdapSettings extends AdminSettings { } > -
-
-

- -

-

- -

-
-
+ + } + /> ); } -} \ No newline at end of file +} diff --git a/webapp/components/analytics/system_analytics.jsx b/webapp/components/analytics/system_analytics.jsx index 8decf523d..5bd8b1d28 100644 --- a/webapp/components/analytics/system_analytics.jsx +++ b/webapp/components/analytics/system_analytics.jsx @@ -1,6 +1,7 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +import Banner from 'components/admin_console/banner.jsx'; import LineChart from './line_chart.jsx'; import DoughnutChart from './doughnut_chart.jsx'; import StatisticCount from './statistic_count.jsx'; @@ -8,11 +9,12 @@ import StatisticCount from './statistic_count.jsx'; import AnalyticsStore from 'stores/analytics_store.jsx'; import * as Utils from 'utils/utils.jsx'; +import {isLicenseExpired, isLicenseExpiring, displayExpiryDate} from 'utils/license_utils.jsx'; import * as AsyncClient from 'utils/async_client.jsx'; import Constants from 'utils/constants.jsx'; const StatTypes = Constants.StatTypes; -import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl'; +import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl'; const holders = defineMessages({ analyticsPublicChannels: { @@ -81,6 +83,7 @@ class SystemAnalytics extends React.Component { let advancedCounts; let advancedGraphs; + let banner; if (global.window.mm_license.IsLicensed === 'true') { advancedCounts = (
@@ -156,6 +159,36 @@ class SystemAnalytics extends React.Component { />
); + + if (isLicenseExpired()) { + banner = ( + + } + /> + ); + } else if (isLicenseExpiring()) { + banner = ( + + } + /> + ); + } } const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]); @@ -169,6 +202,7 @@ class SystemAnalytics extends React.Component { defaultMessage='System Statistics' /> + {banner}
+ ); + } else if (message === EXPIRED_ERROR) { + message = ( + + ); + } else if (message === PAST_GRACE_ERROR) { + message = ( + + ); + } + return (
- {this.state.message} + {message} commercial@mattermost.com.", + "analytics.system.expiringBanner": "The Enterprise license is expiring on {date}. To renew your license, please contact commercial@mattermost.com.", "analytics.system.activeUsers": "Active Users With Posts", "analytics.system.channelTypes": "Channel Types", "analytics.system.postTypes": "Posts, Files and Hashtags", @@ -828,6 +830,9 @@ "email_verify.verified": "{siteName} Email Verified", "email_verify.verifiedBody": "

Your email has been verified! Click here to log in.

", "email_verify.verifyFailed": "Failed to verify your email.", + "error_bar.expiring": "The Enterprise license is expiring on {date}. To renew your license, please contact commercial@mattermost.com", + "error_bar.expired": "Enterprise license has expired; you have 15 days from expiry to renew the license, please contact commercial@mattermost.com for details", + "error_bar.past_grace": "Enterprise license has expired, please contact your System Administrator for details", "error_bar.preview_mode": "Preview Mode: Email notifications have not been configured", "file_attachment.download": "Download", "file_info_preview.size": "Size ", diff --git a/webapp/sass/routes/_admin-console.scss b/webapp/sass/routes/_admin-console.scss index c8af72472..06db93ec5 100644 --- a/webapp/sass/routes/_admin-console.scss +++ b/webapp/sass/routes/_admin-console.scss @@ -145,6 +145,7 @@ .banner__heading { font-size: 1.5em; + margin-bottom: 0.5em; } .banner__content { @@ -374,4 +375,4 @@ .recycle-db { margin-top: 50px !important; -} \ No newline at end of file +} diff --git a/webapp/stores/error_store.jsx b/webapp/stores/error_store.jsx index 4a357472d..e37de40ac 100644 --- a/webapp/stores/error_store.jsx +++ b/webapp/stores/error_store.jsx @@ -20,12 +20,12 @@ class ErrorStoreClass extends EventEmitter { this.removeChangeListener = this.removeChangeListener.bind(this); this.getLastError = this.getLastError.bind(this); this.storeLastError = this.storeLastError.bind(this); - this.getIgnoreEmailPreview = this.getIgnoreEmailPreview.bind(this); - this.ignore_email_preview = false; + this.getIgnoreNotification = this.getIgnoreNotification.bind(this); + this.ignore_notification = false; } - getIgnoreEmailPreview() { - return this.ignore_email_preview; + getIgnoreNotification() { + return this.ignore_notification; } emitChange() { @@ -65,8 +65,8 @@ class ErrorStoreClass extends EventEmitter { clearLastError() { var lastError = this.getLastError(); - // preview message can only be cleared by clearPreviewError - if (lastError && lastError.email_preview) { + // preview message can only be cleared by clearNotificationError + if (lastError && lastError.notification) { return; } @@ -77,8 +77,8 @@ class ErrorStoreClass extends EventEmitter { } } - clearPreviewError() { - this.ignore_email_preview = true; + clearNotificationError() { + this.ignore_notification = true; this.storeLastError(''); this.clearLastError(); } diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index f1af112a9..b7ed97162 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -750,5 +750,7 @@ export default { MHPNS: 'https://push.mattermost.com', MTPNS: 'http://push-test.mattermost.com', BOT_NAME: 'BOT', - POST_COLLAPSE_TIMEOUT: 1000 * 60 * 5 // five minutes + POST_COLLAPSE_TIMEOUT: 1000 * 60 * 5, // five minutes + LICENSE_EXPIRY_NOTIFICATION: 1000 * 60 * 60 * 24 * 15, // 15 days + LICENSE_GRACE_PERIOD: 1000 * 60 * 60 * 24 * 15 // 15 days }; diff --git a/webapp/utils/license_utils.jsx b/webapp/utils/license_utils.jsx new file mode 100644 index 000000000..0ee8b75de --- /dev/null +++ b/webapp/utils/license_utils.jsx @@ -0,0 +1,45 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import Constants from 'utils/constants.jsx'; + +import React from 'react'; +import {FormattedDate} from 'react-intl'; + +export function isLicenseExpiring() { + if (window.mm_license.IsLicensed !== 'true') { + return false; + } + + const timeDiff = parseInt(global.window.mm_license.ExpiresAt, 10) - Date.now(); + return timeDiff <= Constants.LICENSE_EXPIRY_NOTIFICATION; +} + +export function isLicenseExpired() { + if (window.mm_license.IsLicensed !== 'true') { + return false; + } + + const timeDiff = parseInt(global.window.mm_license.ExpiresAt, 10) - Date.now(); + return timeDiff < 0; +} + +export function isLicensePastGracePeriod() { + if (window.mm_license.IsLicensed !== 'true') { + return false; + } + + const timeDiff = Date.now() - parseInt(global.window.mm_license.ExpiresAt, 10); + return timeDiff > Constants.LICENSE_GRACE_PERIOD; +} + +export function displayExpiryDate() { + return ( + + ); +} -- cgit v1.2.3-1-g7c22