summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/user.go5
-rw-r--r--config/config.json5
-rw-r--r--i18n/en.json4
-rw-r--r--model/config.go22
-rw-r--r--templates/welcome_body.html11
-rw-r--r--utils/config.go4
-rw-r--r--webapp/components/admin_console/admin_sidebar.jsx10
-rw-r--r--webapp/components/admin_console/native_app_link_settings.jsx104
-rw-r--r--webapp/components/navbar_dropdown.jsx22
-rw-r--r--webapp/components/sidebar_right_menu.jsx20
-rw-r--r--webapp/components/tutorial/tutorial_intro_screens.jsx46
-rw-r--r--webapp/i18n/en.json11
-rw-r--r--webapp/images/appIcons.pngbin0 -> 10107 bytes
-rw-r--r--webapp/routes/route_admin_console.jsx5
-rw-r--r--webapp/sass/components/_tutorial.scss6
-rw-r--r--webapp/webpack.config.js3
16 files changed, 274 insertions, 4 deletions
diff --git a/api/user.go b/api/user.go
index 8cc4ed8dd..fa643007f 100644
--- a/api/user.go
+++ b/api/user.go
@@ -362,6 +362,11 @@ func sendWelcomeEmail(c *Context, userId string, email string, siteURL string, v
bodyPage.Props["Info3"] = c.T("api.templates.welcome_body.info3")
bodyPage.Props["SiteURL"] = siteURL
+ if *utils.Cfg.NativeAppSettings.AppDownloadLink != "" {
+ bodyPage.Props["AppDownloadInfo"] = c.T("api.templates.welcome_body.app_download_info")
+ bodyPage.Props["AppDownloadLink"] = *utils.Cfg.NativeAppSettings.AppDownloadLink
+ }
+
if !verified {
link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&email=%s", siteURL, userId, model.HashPassword(userId), url.QueryEscape(email))
bodyPage.Props["VerifyUrl"] = link
diff --git a/config/config.json b/config/config.json
index 745be73e3..8f1bea3ca 100644
--- a/config/config.json
+++ b/config/config.json
@@ -195,5 +195,10 @@
"NicknameAttribute": "",
"LocaleAttribute": "",
"LoginButtonText": "With SAML"
+ },
+ "NativeAppSettings": {
+ "AppDownloadLink": "https://about.mattermost.com/downloads/",
+ "AndroidAppDownloadLink": "https://about.mattermost.com/mattermost-android-app/",
+ "IosAppDownloadLink": "https://about.mattermost.com/mattermost-ios-app/"
}
} \ No newline at end of file
diff --git a/i18n/en.json b/i18n/en.json
index 426b1ec1b..d91fea61c 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -1572,6 +1572,10 @@
"translation": "[{{ .SiteName }}] Email Verification"
},
{
+ "id": "api.templates.welcome_body.app_download_info",
+ "translation": "For the best experience, download the apps for PC, Mac, iOS and Android from:"
+ },
+ {
"id": "api.templates.welcome_body.button",
"translation": "Verify Email"
},
diff --git a/model/config.go b/model/config.go
index 4767378fb..b38179cf1 100644
--- a/model/config.go
+++ b/model/config.go
@@ -268,6 +268,12 @@ type SamlSettings struct {
LoginButtonText *string
}
+type NativeAppSettings struct {
+ AppDownloadLink *string
+ AndroidAppDownloadLink *string
+ IosAppDownloadLink *string
+}
+
type Config struct {
ServiceSettings ServiceSettings
TeamSettings TeamSettings
@@ -285,6 +291,7 @@ type Config struct {
ComplianceSettings ComplianceSettings
LocalizationSettings LocalizationSettings
SamlSettings SamlSettings
+ NativeAppSettings NativeAppSettings
}
func (o *Config) ToJson() string {
@@ -792,6 +799,21 @@ func (o *Config) SetDefaults() {
o.SamlSettings.LocaleAttribute = new(string)
*o.SamlSettings.LocaleAttribute = ""
}
+
+ if o.NativeAppSettings.AppDownloadLink == nil {
+ o.NativeAppSettings.AppDownloadLink = new(string)
+ *o.NativeAppSettings.AppDownloadLink = "https://about.mattermost.com/downloads/"
+ }
+
+ if o.NativeAppSettings.AndroidAppDownloadLink == nil {
+ o.NativeAppSettings.AndroidAppDownloadLink = new(string)
+ *o.NativeAppSettings.AndroidAppDownloadLink = "https://about.mattermost.com/mattermost-android-app/"
+ }
+
+ if o.NativeAppSettings.IosAppDownloadLink == nil {
+ o.NativeAppSettings.IosAppDownloadLink = new(string)
+ *o.NativeAppSettings.IosAppDownloadLink = "https://about.mattermost.com/mattermost-ios-app/"
+ }
}
func (o *Config) IsValid() *AppError {
diff --git a/templates/welcome_body.html b/templates/welcome_body.html
index 8dee99cf5..22867e64b 100644
--- a/templates/welcome_body.html
+++ b/templates/welcome_body.html
@@ -33,6 +33,17 @@
<p>{{.Props.Info3}}</p>
</td>
</tr>
+ {{if .Props.AppDownloadLink}}
+ <tr>
+ <td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
+ <p>{{.Props.AppDownloadInfo}}</p>
+ <a href="{{.Props.AppDownloadLink}}">
+ <p>{{.Props.AppDownloadLink}}</p>
+ <img src="{{.Props.SiteURL}}/static/images/appIcons.png" />
+ </a>
+ </td>
+ </tr>
+ {{end}}
</table>
</td>
</tr>
diff --git a/utils/config.go b/utils/config.go
index cbae0e2cb..39dbca4d8 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -264,6 +264,10 @@ func getClientConfig(c *model.Config) map[string]string {
props["RestrictCustomEmojiCreation"] = *c.ServiceSettings.RestrictCustomEmojiCreation
props["MaxFileSize"] = strconv.FormatInt(*c.FileSettings.MaxFileSize, 10)
+ props["AppDownloadLink"] = *c.NativeAppSettings.AppDownloadLink
+ props["AndroidAppDownloadLink"] = *c.NativeAppSettings.AndroidAppDownloadLink
+ props["IosAppDownloadLink"] = *c.NativeAppSettings.IosAppDownloadLink
+
if IsLicensed {
if *License.Features.CustomBrand {
props["EnableCustomBrand"] = strconv.FormatBool(*c.TeamSettings.EnableCustomBrand)
diff --git a/webapp/components/admin_console/admin_sidebar.jsx b/webapp/components/admin_console/admin_sidebar.jsx
index 49df8f820..5c02f419e 100644
--- a/webapp/components/admin_console/admin_sidebar.jsx
+++ b/webapp/components/admin_console/admin_sidebar.jsx
@@ -581,6 +581,16 @@ export default class AdminSidebar extends React.Component {
/>
}
/>
+ <AdminSidebarSection
+ name='native_app_links'
+ title={
+ <FormattedMessage
+ id='admin.sidebar.nativeAppLinks'
+ defaultMessage='Native App Links'
+ />
+
+ }
+ />
</AdminSidebarSection>
<AdminSidebarSection
name='advanced'
diff --git a/webapp/components/admin_console/native_app_link_settings.jsx b/webapp/components/admin_console/native_app_link_settings.jsx
new file mode 100644
index 000000000..6c3acb14f
--- /dev/null
+++ b/webapp/components/admin_console/native_app_link_settings.jsx
@@ -0,0 +1,104 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+import AdminSettings from './admin_settings.jsx';
+import {FormattedMessage} from 'react-intl';
+import SettingsGroup from './settings_group.jsx';
+import TextSetting from './text_setting.jsx';
+
+export default class NativeAppLinkSettings extends AdminSettings {
+ constructor(props) {
+ super(props);
+
+ this.getConfigFromState = this.getConfigFromState.bind(this);
+
+ this.renderSettings = this.renderSettings.bind(this);
+ }
+
+ getConfigFromState(config) {
+ config.NativeAppSettings.AppDownloadLink = this.state.appDownloadLink;
+ config.NativeAppSettings.AndroidAppDownloadLink = this.state.androidAppDownloadLink;
+ config.NativeAppSettings.IosAppDownloadLink = this.state.iosAppDownloadLink;
+
+ return config;
+ }
+
+ getStateFromConfig(config) {
+ return {
+ appDownloadLink: config.NativeAppSettings.AppDownloadLink,
+ androidAppDownloadLink: config.NativeAppSettings.AndroidAppDownloadLink,
+ iosAppDownloadLink: config.NativeAppSettings.IosAppDownloadLink
+ };
+ }
+
+ renderTitle() {
+ return (
+ <h3>
+ <FormattedMessage
+ id='admin.customization.nativeAppLinks'
+ defaultMessage='Native App Links'
+ />
+ </h3>
+ );
+ }
+
+ renderSettings() {
+ return (
+ <SettingsGroup>
+ <TextSetting
+ id='appDownloadLink'
+ label={
+ <FormattedMessage
+ id='admin.customization.appDownloadLinkTitle'
+ defaultMessage='Mattermost Apps Download Page Link:'
+ />
+ }
+ helpText={
+ <FormattedMessage
+ id='admin.customization.appDownloadLinkDesc'
+ defaultMessage='Add a link to a download page for the Mattermost apps. When a link is present, an option to "Download Mattermost Apps" will be added in the Main Menu so users can find the download page. Leave this field blank to hide the option from the Main Menu.'
+ />
+ }
+ value={this.state.appDownloadLink}
+ onChange={this.handleChange}
+ />
+ <TextSetting
+ id='androidAppDownloadLink'
+ label={
+ <FormattedMessage
+ id='admin.customization.androidAppDownloadLinkTitle'
+ defaultMessage='Android App Download Link:'
+ />
+ }
+ helpText={
+ <FormattedMessage
+ id='admin.customization.androidAppDownloadLinkDesc'
+ defaultMessage='Add a link to download the Android app. Users who access the site on a mobile web browser will be prompted with a page giving them the option to download the app. Leave this field blank to prevent the page from appearing.'
+ />
+ }
+ value={this.state.androidAppDownloadLink}
+ onChange={this.handleChange}
+ />
+ <TextSetting
+ id='iosAppDownloadLink'
+ label={
+ <FormattedMessage
+ id='admin.customization.iosAppDownloadLinkTitle'
+ defaultMessage='iOS App Download Link:'
+ />
+ }
+ helpText={
+ <FormattedMessage
+ id='admin.customization.iosAppDownloadLinkDesc'
+ defaultMessage='Add a link to download the iOS app. Users who access the site on a mobile web browser will be prompted with a page giving them the option to download the app. Leave this field blank to prevent the page from appearing.'
+ />
+ }
+ value={this.state.iosAppDownloadLink}
+ onChange={this.handleChange}
+ />
+ </SettingsGroup>
+ );
+ }
+}
diff --git a/webapp/components/navbar_dropdown.jsx b/webapp/components/navbar_dropdown.jsx
index 4912b8ebf..81bd31269 100644
--- a/webapp/components/navbar_dropdown.jsx
+++ b/webapp/components/navbar_dropdown.jsx
@@ -332,6 +332,26 @@ export default class NavbarDropdown extends React.Component {
);
}
+ let nativeAppDivider = null;
+ let nativeAppLink = null;
+ if (global.window.mm_config.AppDownloadLink) {
+ nativeAppDivider = <li className='divider'/>;
+ nativeAppLink = (
+ <li>
+ <Link
+ target='_blank'
+ rel='noopener noreferrer'
+ to={global.window.mm_config.AppDownloadLink}
+ >
+ <FormattedMessage
+ id='navbar_dropdown.nativeApps'
+ defaultMessage='Download Native Apps'
+ />
+ </Link>
+ </li>
+ );
+ }
+
return (
<ul className='nav navbar-nav navbar-right'>
<li
@@ -403,6 +423,8 @@ export default class NavbarDropdown extends React.Component {
/>
</a>
</li>
+ {nativeAppDivider}
+ {nativeAppLink}
<UserSettingsModal
show={this.state.showUserSettingsModal}
onModalDismissed={() => this.setState({showUserSettingsModal: false})}
diff --git a/webapp/components/sidebar_right_menu.jsx b/webapp/components/sidebar_right_menu.jsx
index 25136e8bc..27e7c25d4 100644
--- a/webapp/components/sidebar_right_menu.jsx
+++ b/webapp/components/sidebar_right_menu.jsx
@@ -295,6 +295,25 @@ export default class SidebarRightMenu extends React.Component {
this.openRightSidebar();
}
+ let nativeAppLink = null;
+ if (global.window.mm_config.AppDownloadLink && !Utils.isMobileApp()) {
+ nativeAppLink = (
+ <li>
+ <Link
+ target='_blank'
+ rel='noopener noreferrer'
+ to={global.window.mm_config.AppDownloadLink}
+ >
+ <i className='icon fa fa-mobile'></i>
+ <FormattedMessage
+ id='sidebar_right_menu.nativeApps'
+ defaultMessage='Download Native Apps'
+ />
+ </Link>
+ </li>
+ );
+ }
+
return (
<div
className='sidebar--menu'
@@ -378,6 +397,7 @@ export default class SidebarRightMenu extends React.Component {
/>
</a>
</li>
+ {nativeAppLink}
</ul>
</div>
<UserSettingsModal
diff --git a/webapp/components/tutorial/tutorial_intro_screens.jsx b/webapp/components/tutorial/tutorial_intro_screens.jsx
index 639fa07b2..f435ff972 100644
--- a/webapp/components/tutorial/tutorial_intro_screens.jsx
+++ b/webapp/components/tutorial/tutorial_intro_screens.jsx
@@ -7,12 +7,12 @@ import PreferenceStore from 'stores/preference_store.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
-import Constants from 'utils/constants.jsx';
+import {Constants, Preferences} from 'utils/constants.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import {browserHistory} from 'react-router/es6';
-const Preferences = Constants.Preferences;
+import AppIcons from 'images/appIcons.png';
const NUM_SCREENS = 3;
@@ -91,6 +91,46 @@ export default class TutorialIntroScreens extends React.Component {
createScreenTwo() {
const circles = this.createCircles();
+ let appDownloadLink = null;
+ let appDownloadImage = null;
+ if (global.window.mm_config.AppDownloadLink) {
+ // not using a FormattedHTMLMessage here since mm_config.AppDownloadLink is configurable and could be used
+ // to inject HTML if we're not careful
+ appDownloadLink = (
+ <FormattedMessage
+ id='tutorial_intro.mobileApps'
+ defaultMessage='Install the apps for {link} for easy access and notifications on the go.'
+ values={{
+ link: (
+ <a
+ href={global.window.mm_config.AppDownloadLink}
+ target='_blank'
+ rel='noopener noreferrer'
+ >
+ <FormattedMessage
+ id='tutorial_intro.mobileAppsLinkText'
+ defaultMessage='PC, Mac, iOS and Android'
+ />
+ </a>
+ )
+ }}
+ />
+ );
+
+ appDownloadImage = (
+ <a
+ href={global.window.mm_config.AppDownloadLink}
+ target='_blank'
+ rel='noopener noreferrer'
+ >
+ <img
+ className='tutorial__app-icons'
+ src={AppIcons}
+ />
+ </a>
+ );
+ }
+
return (
<div>
<FormattedHTMLMessage
@@ -99,6 +139,8 @@ export default class TutorialIntroScreens extends React.Component {
<p>Communication happens in public discussion channels, private groups and direct messages.</p>
<p>Everything is archived and searchable from any web-enabled desktop, laptop or phone.</p>'
/>
+ {appDownloadLink}
+ {appDownloadImage}
{circles}
</div>
);
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index ab7406d45..ad1c1d363 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -146,10 +146,17 @@
"admin.connectionSecurityTitle": "Connection Security:",
"admin.connectionSecurityTls": "TLS",
"admin.connectionSecurityTlsDescription": "Encrypts the communication between Mattermost and your server.",
+ "admin.customization.appDownloadLinkDesc": "Add a link to a download page for the Mattermost apps. When a link is present, an option to \"Download Mattermost Apps\" will be added in the Main Menu so users can find the download page. Leave this field blank to hide the option from the Main Menu.",
+ "admin.customization.appDownloadLinkTitle": "Mattermost Apps Download Page Link:",
+ "admin.customization.androidAppDownloadLinkDesc": "Add a link to download the Android app. Users who access the site on a mobile web browser will be prompted with a page giving them the option to download the app. Leave this field blank to prevent the page from appearing.",
+ "admin.customization.androidAppDownloadLinkTitle": "Android App Download Link:",
"admin.customization.customBrand": "Custom Branding",
"admin.customization.customEmoji": "Custom Emoji",
"admin.customization.enableCustomEmojiDesc": "Enable users to create custom emoji for use in messages. When enabled, Custom Emoji settings can be accessed by switching to a team and clicking the three dots above the channel sidebar, and selecting \"Custom Emoji\".",
"admin.customization.enableCustomEmojiTitle": "Enable Custom Emoji:",
+ "admin.customization.iosAppDownloadLinkDesc": "Add a link to download the iOS app. Users who access the site on a mobile web browser will be prompted with a page giving them the option to download the app. Leave this field blank to prevent the page from appearing.",
+ "admin.customization.iosAppDownloadLinkTitle": "iOS App Download Link:",
+ "admin.customization.nativeAppLinks": "Native App Links",
"admin.customization.restrictCustomEmojiCreationAdmin": "Allow System and Team Admins to create custom emoji",
"admin.customization.restrictCustomEmojiCreationAll": "Allow everyone to create custom emoji",
"admin.customization.restrictCustomEmojiCreationDesc": "Restrict the creation of custom emoji to certain users.",
@@ -1229,6 +1236,7 @@
"navbar_dropdown.leave": "Leave Team",
"navbar_dropdown.logout": "Logout",
"navbar_dropdown.manageMembers": "Manage Members",
+ "navbar_dropdown.nativeApps": "Download Native Apps",
"navbar_dropdown.viewMembers": "View Members",
"navbar_dropdown.report": "Report a Problem",
"navbar_dropdown.switchTeam": "Switch to {team}",
@@ -1358,6 +1366,7 @@
"sidebar_right_menu.inviteNew": "Invite New Member",
"sidebar_right_menu.logout": "Logout",
"sidebar_right_menu.manageMembers": "Manage Members",
+ "sidebar_right_menu.nativeApps": "Download Native App",
"sidebar_right_menu.recentMentions": "Recent Mentions",
"sidebar_right_menu.report": "Report a Problem",
"sidebar_right_menu.switch_team": "Team Selection",
@@ -1457,6 +1466,8 @@
"tutorial_intro.allSet": "You’re all set",
"tutorial_intro.end": "Click “Next” to enter {channel}. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.",
"tutorial_intro.invite": "Invite teammates",
+ "tutorial_intro.mobileApps": "Install the apps for {link} for easy access and notifications on the go.",
+ "tutorial_intro.mobileAppsLinkText": "PC, Mac, iOS and Android",
"tutorial_intro.next": "Next",
"tutorial_intro.screenOne": "<h3>Welcome to:</h3><h1>Mattermost</h1><p>Your team communication all in one place, instantly searchable and available anywhere</p><p>Keep your team connected to help them achieve what matters most.</p>",
"tutorial_intro.screenTwo": "<h3>How Mattermost works:</h3><p>Communication happens in public discussion channels, private groups and direct messages.</p><p>Everything is archived and searchable from any web-enabled desktop, laptop or phone.</p>",
diff --git a/webapp/images/appIcons.png b/webapp/images/appIcons.png
new file mode 100644
index 000000000..ce38166fb
--- /dev/null
+++ b/webapp/images/appIcons.png
Binary files differ
diff --git a/webapp/routes/route_admin_console.jsx b/webapp/routes/route_admin_console.jsx
index 9fde948c2..1465bfa6b 100644
--- a/webapp/routes/route_admin_console.jsx
+++ b/webapp/routes/route_admin_console.jsx
@@ -31,6 +31,7 @@ import ImageSettings from 'components/admin_console/image_settings.jsx';
import CustomBrandSettings from 'components/admin_console/custom_brand_settings.jsx';
import CustomEmojiSettings from 'components/admin_console/custom_emoji_settings.jsx';
import LegalAndSupportSettings from 'components/admin_console/legal_and_support_settings.jsx';
+import NativeAppLinkSettings from 'components/admin_console/native_app_link_settings.jsx';
import ComplianceSettings from 'components/admin_console/compliance_settings.jsx';
import RateSettings from 'components/admin_console/rate_settings.jsx';
import DeveloperSettings from 'components/admin_console/developer_settings.jsx';
@@ -166,6 +167,10 @@ export default (
path='legal_and_support'
component={LegalAndSupportSettings}
/>
+ <Route
+ path='native_app_links'
+ component={NativeAppLinkSettings}
+ />
</Route>
<Route path='advanced'>
<IndexRedirect to='rate'/>
diff --git a/webapp/sass/components/_tutorial.scss b/webapp/sass/components/_tutorial.scss
index e4aff1785..a8d3ffdf6 100644
--- a/webapp/sass/components/_tutorial.scss
+++ b/webapp/sass/components/_tutorial.scss
@@ -183,7 +183,7 @@
display: inline-block;
margin-bottom: 50px;
max-width: 310px;
- min-height: 370px;
+ min-height: 410px;
position: relative;
text-align: left;
}
@@ -234,3 +234,7 @@
}
}
}
+
+.tutorial__app-icons {
+ margin: 2em 0;
+} \ No newline at end of file
diff --git a/webapp/webpack.config.js b/webapp/webpack.config.js
index 4f33eaf84..8b1f4b21a 100644
--- a/webapp/webpack.config.js
+++ b/webapp/webpack.config.js
@@ -92,7 +92,8 @@ var config = {
{from: 'images/emoji', to: 'emoji'},
{from: 'images/logo-email.png', to: 'images'},
{from: 'images/circles.png', to: 'images'},
- {from: 'images/favicon', to: 'images/favicon'}
+ {from: 'images/favicon', to: 'images/favicon'},
+ {from: 'images/appIcons.png', to: 'images'}
]),
new webpack.LoaderOptionsPlugin({
minimize: !DEV,